// ***** This file is automatically generated from Quant.java.jpp and QuantBody.java.jpp.

package daikon;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import org.plumelib.util.ArraysPlume;
import org.plumelib.util.Intern;
import org.plumelib.util.FuzzyFloat;

import org.checkerframework.checker.interning.qual.Interned;
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.PolyNull;
import org.checkerframework.checker.signedness.qual.Signed;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;

/**
 * The Quant library provides routines that operate on arrays and collections.
 *
 * <p>These routines are used by the Java family of output formats. This allows invariants to be
 * output as snippets of executable (Java) code. For example, an invariant like
 * <pre>a[] elements &ge; 1</pre>
 * is output (in the Java, JML, and DBC formats) as something like
 *
 * <pre>daikon.Quant.eltsGTE(a, 1)</pre>
 *
 * <h2>Naming</h2>
 *
 * The library methods have names of the following forms, where OP indicates an operation: Equal,
 * NotEqual, GT (greater than), GTE (greater than or equal to), LT, LTE.
 * <ul>
 * <li>pairwiseOP. Apply OP to corresponding elements of two arrays:
 *     a[0] OP b[0], a[1] OP b[1], etc.
 * <li>eltsOP. Apply OP to each element of one array, and a scalar:
 *     a[0] OP x, a[1] OP x, etc.
 * <li>eltwiseOP: Apply OP to adjacent elements of one array:
 *     a[0] OP a[1], a[1] OP a[2], etc.
 * <li>lexOP: Determine lexical ordering:
 *     compare two arrays pairwise, stopping as soon as the result is known.
 * <li>eltsOPindex: Apply op to array elements and their indices:
 *     a[0] OP 0, a[1] OP 1, etc.
 * </ul>
 *
 * <h2>Equality semantics</h2>
 *
 * <p>Whenever a method involves comparing two elements for equality, this is always "==" equality
 * (even for Objects and Strings).
 *
 * <h2>No exceptions thrown</h2>
 *
 * <p>The library strives not to throw exceptions, even if illegal arguments are passed to the
 * routines. This has two consequences.
 *
 * <p>First, each predicate (boolean method) returns false when invoked on an illegal argument such
 * as a null collection (array or Collection).
 *
 * <p>Second, each accessor method returns a default "bad" value if inovked on an illegal argument.
 * For example, the default value for the double type is Double.NaN.
 *
 * <p>The rationale for the decision to never throw exceptions is that we wish to be able to invoke
 * the Quant methods at run time without distrubing execution of a program, and without forcing
 * clients to write a try .. catch block around each invocation.
 *
 * <p>A downside of the decision is that if the default value is returned, it may be impossible for
 * a client to determine whether the method really returned that value, or whether the invocation
 * involved an illegal argument. To avoid this problem, it is generally better to use a Quant
 * library predicate rather than returning a value and then testing it externally.
 */
public final class Quant {
  private Quant() { throw new Error("do not instantiate"); }

  public static FuzzyFloat fuzzy = new FuzzyFloat();

  /**
   * Returns the size of the array or collection. If the argument is null or not an array or
   * collection, returns a default value (Integer.MAX_VALUE). Thus, for an array a, this never
   * throws an exception, though a.length may.
   */
  // Not called from Quant; provided only for external use.
  @Pure
  public static int size(Object o) {
    if (o == null) {
      return Integer.MAX_VALUE; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getLength(o);
    } else if (o instanceof Collection<?>) {
      return ((Collection<?>)o).size();
    } else {
      return Integer.MAX_VALUE; // return default value
    }
  }

  /** Returns the size of the collection.
   * If the argument is null, returns a default value (Integer.MAX_VALUE).
   */
  // Not called from Quant; provided only for external use.
  @Pure
  public static int size(Collection<?> o) {
    if (o == null) {
      return Integer.MAX_VALUE; // return default value
    }
    return o.size();
  }

  /** True iff the sequence contains no null elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNonNull(Object[] seq1) {
    if (seq1 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] == null) {
        return false;
      }
    }
    return true;
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for "boolean" (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (false).
   */

  @EnsuresNonNullIf(result=true, expression="#1")

  @Pure
  public static boolean getElement_boolean(Object o, long i) {
    if (o == null) {
      return false; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getBoolean(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.getBoolean(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return false; // return default value
    }
  }

  @EnsuresNonNullIf(result=true, expression="#1")

  @Pure
  public static boolean getElement_boolean(boolean[] arr, long i) {
    if (arr == null) {
      return false; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(boolean x, boolean y) {
    return x == y;
  }

  private static boolean ne(boolean x, boolean y) {
    return x != y;
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static boolean @PolyNull [] concat(boolean @PolyNull [] seq1, boolean @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static boolean @PolyNull [] union(boolean @PolyNull [] seq1, boolean @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static boolean @PolyNull [] intersection(boolean @PolyNull [] seq1, boolean @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    boolean[] intermediate = new boolean[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static boolean @PolyNull [] setDiff(boolean @PolyNull [] seq1, boolean @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    boolean[] intermediate = new boolean[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(boolean[] seq1, boolean[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(boolean @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(boolean elt, boolean @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static boolean @PolyNull [] slice(boolean @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new boolean[] { };
    }
    if (end > seq.length - 1) {
      return new boolean[] { };
    }
    if (sliceStart > sliceEnd) {
      return new boolean[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static boolean @PolyNull [] slice(boolean @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static boolean @PolyNull [] slice(boolean @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static boolean @PolyNull [] slice(boolean @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(boolean @Nullable [] arr, boolean elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(boolean @Nullable [] arr, boolean elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(boolean @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(boolean @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectboolean accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectboolean() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SideEffectFree
  public static boolean @Nullable [] collectboolean(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    boolean[] retval = collectboolean(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectboolean(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectboolean(Object, String)
   */

  @SideEffectFree
  private static boolean @Nullable [] collectboolean(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (boolean[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectboolean_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(Boolean.TYPE);
        return new boolean[] { ((Boolean)fieldObj).booleanValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        boolean[] intermediate = new boolean[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          boolean[] oneEltArray = collectboolean(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        boolean[] intermediate = new boolean[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          boolean[] oneEltArray = collectboolean(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectboolean(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectboolean_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static boolean collectboolean_field(Object object, String fieldStr) {

    if (object == null) {
      return false; // return default value
    }
    if (fieldStr == null) {
      return false; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return false; // return default value
        }

      } catch (Exception e) {
        return false; // return default value

      }

    }

    return ((Boolean)fieldObj).booleanValue();
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for "byte" (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (Byte.MAX_VALUE).
   */

  @Pure
  public static byte getElement_byte(Object o, long i) {
    if (o == null) {
      return Byte.MAX_VALUE; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getByte(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.getByte(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return Byte.MAX_VALUE; // return default value
    }
  }

  @Pure
  public static byte getElement_byte(byte[] arr, long i) {
    if (arr == null) {
      return Byte.MAX_VALUE; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(byte x, byte y) {
    return x == y;
  }

  private static boolean ne(byte x, byte y) {
    return x != y;
  }

  private static boolean lt(byte x, byte y) {
    return x < y;
  }

  private static boolean lte(byte x, byte y) {
    return x <= y;
  }

  private static boolean gt(byte x, byte y) {
    return x > y;
  }

  private static boolean gte(byte x, byte y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(byte @Nullable [] seq1, int @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(byte[] seq1, byte[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(byte[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff both sequences have the same length, and all seq1[i] == seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == seq2[i] * seq2[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(byte[] seq1, byte[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(byte[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {

      if (ne(seq1[i], seq2[i] * seq2[i])) {

        return false;
      }
    }
    return true;
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static byte @PolyNull [] concat(byte @PolyNull [] seq1, byte @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  @SideEffectFree
  public static int @PolyNull [] concat(byte @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    // Cannot just use ArraysPlume.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    int[] retval = new int[newLength];

    for (int j = 0 ; j < seq1.length ; j++) {
      retval[j] = seq1[j];
    }
    System.arraycopy(seq2, 0, retval, seq1.length, seq2.length);

    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static byte @PolyNull [] union(byte @PolyNull [] seq1, byte @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  @Pure
  public static int @PolyNull [] union(byte @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static byte @PolyNull [] intersection(byte @PolyNull [] seq1, byte @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    byte[] intermediate = new byte[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static int @PolyNull [] intersection(byte @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    int[] intermediate = new int[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static byte @PolyNull [] setDiff(byte @PolyNull [] seq1, byte @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    byte[] intermediate = new byte[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static int @PolyNull [] setDiff(byte @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    int[] intermediate = new int[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(byte[] seq1, byte[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(byte @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(byte elt, byte @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(byte elt, int @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(long elt, byte @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static byte @PolyNull [] slice(byte @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new byte[] { };
    }
    if (end > seq.length - 1) {
      return new byte[] { };
    }
    if (sliceStart > sliceEnd) {
      return new byte[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static byte @PolyNull [] slice(byte @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static byte @PolyNull [] slice(byte @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static byte @PolyNull [] slice(byte @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(byte @Nullable [] arr, byte elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(byte @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(byte @Nullable [] arr, byte elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(byte @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &gt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(byte @Nullable [] arr, byte elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(byte @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &ge; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(byte @Nullable [] arr, byte elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(byte @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &lt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(byte @Nullable [] arr, byte elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(byte @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &le; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(byte @Nullable [] arr, byte elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(byte @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &lt; seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &lt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &le; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &le; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &gt; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &gt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &ge; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &ge; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically &lt;  seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(byte @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /** Returns true iff seq1 is lexically &le; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(byte @Nullable [] seq1, byte @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(byte @Nullable [] seq1, int @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &gt; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(byte @Nullable [] seq1, byte @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(byte @Nullable [] seq1, int @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &ge; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(byte @Nullable [] seq1, byte @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(byte @Nullable [] seq1, int @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &lt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLT(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &le; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLTE(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &gt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGT(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &ge; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGTE(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqualIndex(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqualIndex(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &lt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLtIndex(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &le; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLteIndex(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &gt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGtIndex(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &ge; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGteIndex(byte @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectbyte accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectbyte() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SideEffectFree
  public static byte @Nullable [] collectbyte(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    byte[] retval = collectbyte(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectbyte(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectbyte(Object, String)
   */

  @SideEffectFree
  private static byte @Nullable [] collectbyte(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (byte[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectbyte_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(Byte.TYPE);
        return new byte[] { ((Byte)fieldObj).byteValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        byte[] intermediate = new byte[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          byte[] oneEltArray = collectbyte(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        byte[] intermediate = new byte[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          byte[] oneEltArray = collectbyte(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectbyte(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectbyte_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static byte collectbyte_field(Object object, String fieldStr) {

    if (object == null) {
      return Byte.MAX_VALUE; // return default value
    }
    if (fieldStr == null) {
      return Byte.MAX_VALUE; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return Byte.MAX_VALUE; // return default value
        }

      } catch (Exception e) {
        return Byte.MAX_VALUE; // return default value

      }

    }

    return ((Byte)fieldObj).byteValue();
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for "char" (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (Character.MAX_VALUE).
   */

  @Pure
  public static char getElement_char(Object o, long i) {
    if (o == null) {
      return Character.MAX_VALUE; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getChar(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.getChar(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return Character.MAX_VALUE; // return default value
    }
  }

  @Pure
  public static char getElement_char(char[] arr, long i) {
    if (arr == null) {
      return Character.MAX_VALUE; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(char x, char y) {
    return x == y;
  }

  private static boolean ne(char x, char y) {
    return x != y;
  }

  private static boolean lt(char x, char y) {
    return x < y;
  }

  private static boolean lte(char x, char y) {
    return x <= y;
  }

  private static boolean gt(char x, char y) {
    return x > y;
  }

  private static boolean gte(char x, char y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(char @Nullable [] seq1, char @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static char @PolyNull [] concat(char @PolyNull [] seq1, char @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static char @PolyNull [] union(char @PolyNull [] seq1, char @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static char @PolyNull [] intersection(char @PolyNull [] seq1, char @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    char[] intermediate = new char[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static char @PolyNull [] setDiff(char @PolyNull [] seq1, char @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    char[] intermediate = new char[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(char[] seq1, char[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(char @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(char elt, char @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static char @PolyNull [] slice(char @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new char[] { };
    }
    if (end > seq.length - 1) {
      return new char[] { };
    }
    if (sliceStart > sliceEnd) {
      return new char[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static char @PolyNull [] slice(char @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static char @PolyNull [] slice(char @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static char @PolyNull [] slice(char @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(char @Nullable [] arr, char elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(char @Nullable [] arr, char elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &gt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(char @Nullable [] arr, char elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &ge; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(char @Nullable [] arr, char elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &lt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(char @Nullable [] arr, char elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &le; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(char @Nullable [] arr, char elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &lt; seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &lt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &le; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &le; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &gt; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &gt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &ge; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &ge; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically &lt;  seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &le; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(char @Nullable [] seq1, char @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &gt; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(char @Nullable [] seq1, char @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &ge; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(char @Nullable [] seq1, char @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &lt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLT(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &le; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLTE(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &gt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGT(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &ge; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGTE(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqualIndex(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqualIndex(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &lt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLtIndex(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &le; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLteIndex(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &gt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGtIndex(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &ge; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGteIndex(char @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectchar accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectchar() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SideEffectFree
  public static char @Nullable [] collectchar(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    char[] retval = collectchar(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectchar(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectchar(Object, String)
   */

  @SideEffectFree
  private static char @Nullable [] collectchar(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (char[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectchar_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(Character.TYPE);
        return new char[] { ((Character)fieldObj).charValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        char[] intermediate = new char[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          char[] oneEltArray = collectchar(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        char[] intermediate = new char[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          char[] oneEltArray = collectchar(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectchar(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectchar_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static char collectchar_field(Object object, String fieldStr) {

    if (object == null) {
      return Character.MAX_VALUE; // return default value
    }
    if (fieldStr == null) {
      return Character.MAX_VALUE; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return Character.MAX_VALUE; // return default value
        }

      } catch (Exception e) {
        return Character.MAX_VALUE; // return default value

      }

    }

    return ((Character)fieldObj).charValue();
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for "double" (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (Double.NaN).
   */

  @Pure
  public static double getElement_double(Object o, long i) {
    if (o == null) {
      return Double.NaN; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getDouble(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.getDouble(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return Double.NaN; // return default value
    }
  }

  @Pure
  public static double getElement_double(double[] arr, long i) {
    if (arr == null) {
      return Double.NaN; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(double x, double y) {
    return fuzzy.eq(x,y);
  }

  private static boolean ne(double x, double y) {
    return fuzzy.ne(x,y);
  }

  private static boolean lt(double x, double y) {
    return fuzzy.lt(x,y);
  }

  private static boolean lte(double x, double y) {
    return fuzzy.lte(x,y);
  }

  private static boolean gt(double x, double y) {
    return fuzzy.gt(x,y);
  }

  private static boolean gte(double x, double y) {
    return fuzzy.gte(x,y);
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(double @Nullable [] seq1, double @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(double @Nullable [] seq1, float @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(double[] seq1, double[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(double[] seq1, float[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff both sequences have the same length, and all seq1[i] == seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == seq2[i] * seq2[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(double[] seq1, double[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(double[] seq1, float[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {

      if (ne(seq1[i], ((double) seq2[i]) * ((double) seq2[i]))) {

        return false;
      }
    }
    return true;
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static double @PolyNull [] concat(double @PolyNull [] seq1, double @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  @SideEffectFree
  public static double @PolyNull [] concat(double @PolyNull [] seq1, float @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    // Cannot just use ArraysPlume.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    double[] retval = new double[newLength];

    System.arraycopy(seq1, 0, retval, 0, seq1.length);
    for (int j = 0 ; j < seq2.length ; j++) {
      retval[seq1.length + j] = seq2[j];
    }

    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static double @PolyNull [] union(double @PolyNull [] seq1, double @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  @Pure
  public static double @PolyNull [] union(double @PolyNull [] seq1, float @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static double @PolyNull [] intersection(double @PolyNull [] seq1, double @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    double[] intermediate = new double[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static double @PolyNull [] intersection(double @PolyNull [] seq1, float @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    double[] intermediate = new double[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static double @PolyNull [] setDiff(double @PolyNull [] seq1, double @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    double[] intermediate = new double[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static double @PolyNull [] setDiff(double @PolyNull [] seq1, float @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    double[] intermediate = new double[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(double[] seq1, double[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(double @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(double elt, double @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(double elt, float @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static double @PolyNull [] slice(double @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new double[] { };
    }
    if (end > seq.length - 1) {
      return new double[] { };
    }
    if (sliceStart > sliceEnd) {
      return new double[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static double @PolyNull [] slice(double @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static double @PolyNull [] slice(double @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static double @PolyNull [] slice(double @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(double @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(double @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(double @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(double @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &gt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(double @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(double @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &ge; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(double @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(double @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &lt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(double @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(double @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &le; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(double @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(double @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (Double.isNaN(seq1[i]) && Double.isNaN(seq2[i])) {
        continue;
      }
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &lt; seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &lt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &le; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &le; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &gt; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &gt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &ge; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &ge; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically &lt;  seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(double @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /** Returns true iff seq1 is lexically &le; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(double @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(double @Nullable [] seq1, float @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &gt; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(double @Nullable [] seq1, double @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(double @Nullable [] seq1, float @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &ge; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(double @Nullable [] seq1, double @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(double @Nullable [] seq1, float @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &lt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLT(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &le; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLTE(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &gt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGT(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &ge; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGTE(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqualIndex(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqualIndex(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &lt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLtIndex(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &le; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLteIndex(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &gt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGtIndex(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &ge; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGteIndex(double @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectdouble accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectdouble() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SideEffectFree
  public static double @Nullable [] collectdouble(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    double[] retval = collectdouble(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectdouble(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectdouble(Object, String)
   */

  @SideEffectFree
  private static double @Nullable [] collectdouble(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (double[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectdouble_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(Double.TYPE);
        return new double[] { ((Double)fieldObj).doubleValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        double[] intermediate = new double[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          double[] oneEltArray = collectdouble(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        double[] intermediate = new double[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          double[] oneEltArray = collectdouble(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectdouble(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectdouble_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static double collectdouble_field(Object object, String fieldStr) {

    if (object == null) {
      return Double.NaN; // return default value
    }
    if (fieldStr == null) {
      return Double.NaN; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return Double.NaN; // return default value
        }

      } catch (Exception e) {
        return Double.NaN; // return default value

      }

    }

    return ((Double)fieldObj).doubleValue();
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for "float" (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (Float.NaN).
   */

  @Pure
  public static float getElement_float(Object o, long i) {
    if (o == null) {
      return Float.NaN; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getFloat(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.getFloat(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return Float.NaN; // return default value
    }
  }

  @Pure
  public static float getElement_float(float[] arr, long i) {
    if (arr == null) {
      return Float.NaN; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(float x, float y) {
    return fuzzy.eq(x,y);
  }

  private static boolean ne(float x, float y) {
    return fuzzy.ne(x,y);
  }

  private static boolean lt(float x, float y) {
    return fuzzy.lt(x,y);
  }

  private static boolean lte(float x, float y) {
    return fuzzy.lte(x,y);
  }

  private static boolean gt(float x, float y) {
    return fuzzy.gt(x,y);
  }

  private static boolean gte(float x, float y) {
    return fuzzy.gte(x,y);
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(float @Nullable [] seq1, float @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(float @Nullable [] seq1, double @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(float[] seq1, float[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(float[] seq1, double[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff both sequences have the same length, and all seq1[i] == seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == seq2[i] * seq2[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(float[] seq1, float[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(float[] seq1, double[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {

      if (ne(seq1[i], seq2[i] * seq2[i])) {

        return false;
      }
    }
    return true;
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static float @PolyNull [] concat(float @PolyNull [] seq1, float @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  @SideEffectFree
  public static double @PolyNull [] concat(float @PolyNull [] seq1, double @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    // Cannot just use ArraysPlume.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    double[] retval = new double[newLength];

    for (int j = 0 ; j < seq1.length ; j++) {
      retval[j] = seq1[j];
    }
    System.arraycopy(seq2, 0, retval, seq1.length, seq2.length);

    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static float @PolyNull [] union(float @PolyNull [] seq1, float @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  @Pure
  public static double @PolyNull [] union(float @PolyNull [] seq1, double @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static float @PolyNull [] intersection(float @PolyNull [] seq1, float @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    float[] intermediate = new float[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static double @PolyNull [] intersection(float @PolyNull [] seq1, double @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    double[] intermediate = new double[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static float @PolyNull [] setDiff(float @PolyNull [] seq1, float @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    float[] intermediate = new float[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static double @PolyNull [] setDiff(float @PolyNull [] seq1, double @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    double[] intermediate = new double[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(float[] seq1, float[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(float @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(float elt, float @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(float elt, double @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static float @PolyNull [] slice(float @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new float[] { };
    }
    if (end > seq.length - 1) {
      return new float[] { };
    }
    if (sliceStart > sliceEnd) {
      return new float[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static float @PolyNull [] slice(float @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static float @PolyNull [] slice(float @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static float @PolyNull [] slice(float @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(float @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(float @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(float @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(float @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &gt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(float @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(float @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &ge; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(float @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(float @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &lt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(float @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(float @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &le; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(float @Nullable [] arr, float elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(float @Nullable [] arr, double elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (Float.isNaN(seq1[i]) && Float.isNaN(seq2[i])) {
        continue;
      }
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &lt; seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &lt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &le; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &le; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &gt; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &gt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &ge; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &ge; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically &lt;  seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(float @Nullable [] seq1, double @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /** Returns true iff seq1 is lexically &le; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(float @Nullable [] seq1, float @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(float @Nullable [] seq1, double @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &gt; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(float @Nullable [] seq1, float @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(float @Nullable [] seq1, double @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &ge; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(float @Nullable [] seq1, float @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(float @Nullable [] seq1, double @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &lt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLT(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &le; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLTE(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &gt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGT(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &ge; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGTE(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqualIndex(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqualIndex(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &lt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLtIndex(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &le; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLteIndex(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &gt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGtIndex(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &ge; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGteIndex(float @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectfloat accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectfloat() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SideEffectFree
  public static float @Nullable [] collectfloat(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    float[] retval = collectfloat(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectfloat(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectfloat(Object, String)
   */

  @SideEffectFree
  private static float @Nullable [] collectfloat(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (float[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectfloat_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(Float.TYPE);
        return new float[] { ((Float)fieldObj).floatValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        float[] intermediate = new float[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          float[] oneEltArray = collectfloat(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        float[] intermediate = new float[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          float[] oneEltArray = collectfloat(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectfloat(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectfloat_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static float collectfloat_field(Object object, String fieldStr) {

    if (object == null) {
      return Float.NaN; // return default value
    }
    if (fieldStr == null) {
      return Float.NaN; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return Float.NaN; // return default value
        }

      } catch (Exception e) {
        return Float.NaN; // return default value

      }

    }

    return ((Float)fieldObj).floatValue();
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for "int" (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (Integer.MAX_VALUE).
   */

  @Pure
  public static int getElement_int(Object o, long i) {
    if (o == null) {
      return Integer.MAX_VALUE; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getInt(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.getInt(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return Integer.MAX_VALUE; // return default value
    }
  }

  @Pure
  public static int getElement_int(int[] arr, long i) {
    if (arr == null) {
      return Integer.MAX_VALUE; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(int x, int y) {
    return x == y;
  }

  private static boolean ne(int x, int y) {
    return x != y;
  }

  private static boolean lt(int x, int y) {
    return x < y;
  }

  private static boolean lte(int x, int y) {
    return x <= y;
  }

  private static boolean gt(int x, int y) {
    return x > y;
  }

  private static boolean gte(int x, int y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(int @Nullable [] seq1, int @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(int @Nullable [] seq1, long @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(int[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(int[] seq1, long[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff both sequences have the same length, and all seq1[i] == seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == seq2[i] * seq2[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(int[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(int[] seq1, long[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {

      if (ne(seq1[i], seq2[i] * seq2[i])) {

        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] == ~ seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == ~ seq2[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseComplement(int[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] != ~seq2[i]) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseComplement(int[] seq1, long[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] != ~seq2[i]) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseComplement(Object[] seq1, Object[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    if (!eltsNonNull(seq1)) {
      return false;
    }
    if (!eltsNonNull(seq2)) {
      return false;
    }
    int[] hashArr1 = new int[seq1.length];
    for (int i = 0 ; i < seq1.length ; i++) {
      hashArr1[i] = seq1[i].hashCode();
    }
    int[] hashArr2 = new int[seq2.length];
    for (int i = 0 ; i < seq2.length ; i++) {
      hashArr2[i] = seq2[i].hashCode();
    }
    return pairwiseBitwiseComplement(hashArr1, hashArr2);
  }

  /** True iff both sequences have the same length, and all seq1[i] == (seq2[i] | seq1[i]).
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == (seq2[i] | seq1[i])
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseSubset(int[] seq1, int[] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    if (seq1.length != seq2.length) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], (seq2[i] | seq1[i]))) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseSubset(int[] seq1, long[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], (seq2[i] | seq1[i]))) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseSubset(Object[] seq1, Object[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    if (!eltsNonNull(seq1)) {
      return false;
    }
    if (!eltsNonNull(seq2)) {
      return false;
    }
    int[] hashArr1 = new int[seq1.length];
    for (int i = 0 ; i < seq1.length ; i++) {
      hashArr1[i] = seq1[i].hashCode();
    }
    int[] hashArr2 = new int[seq2.length];
    for (int i = 0 ; i < seq2.length ; i++) {
      hashArr2[i] = seq2[i].hashCode();
    }
    return pairwiseBitwiseSubset(hashArr1, hashArr2);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static int @PolyNull [] concat(int @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  @SideEffectFree
  public static long @PolyNull [] concat(int @PolyNull [] seq1, long @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    // Cannot just use ArraysPlume.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    long[] retval = new long[newLength];

    for (int j = 0 ; j < seq1.length ; j++) {
      retval[j] = seq1[j];
    }
    System.arraycopy(seq2, 0, retval, seq1.length, seq2.length);

    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static int @PolyNull [] union(int @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  @Pure
  public static long @PolyNull [] union(int @PolyNull [] seq1, long @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static int @PolyNull [] intersection(int @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    int[] intermediate = new int[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static long @PolyNull [] intersection(int @PolyNull [] seq1, long @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    long[] intermediate = new long[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static int @PolyNull [] setDiff(int @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    int[] intermediate = new int[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static long @PolyNull [] setDiff(int @PolyNull [] seq1, long @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    long[] intermediate = new long[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(int[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff all elements in elts occur once or more in arr;
   * that is, elts is a subset of arr.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..elt.length-1 } : elt[i] element_of arr
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(@Nullable Object elts, @Nullable Object arr) {
    if (elts == null) {
      return false;
    }
    if (arr == null) {
      return false;
    }
    if (!(elts.getClass().isArray() && arr.getClass().isArray())) {
      // throw new IllegalArgumentException("both arguments must be arrays.");
      return false;
    }
    // We know that the two arguments are arrays of different types; if
    // they had been of the same type, then one of the more specific
    // overriding versions of this method would have been called.

    // This implementation simply calls either subsetOf(long[], long[]) or
    // subsetOf(double[], double[]).

    Class<?> eltsType = elts.getClass().getComponentType();
    Class<?> arrType = arr.getClass().getComponentType();
    if (isIntegralType(eltsType) && isIntegralType(arrType)) {
      // Both arrays are int/long.
      // Cast both arrays to long and call subsetOf(long[],long[]).
      long[] elts_long;
      if (eltsType == Long.class) {
        elts_long = (long[]) elts;
      } else {
        elts_long = new long[Array.getLength(elts)];
        for (int i = 0 ; i < elts_long.length ; i++) {
          elts_long[i] = Array.getLong(elts, i);
        }
      }
      long[] arr_long;
      if (arrType == Long.class) {
        arr_long = (long[]) arr;
      } else {
        arr_long = new long[Array.getLength(arr)];
        for (int i = 0 ; i < arr_long.length ; i++) {
          arr_long[i] = Array.getLong(arr, i);
        }
      }
      return subsetOf(elts_long, arr_long);
    } else if (isNumericType(eltsType) && isNumericType(arrType)) {
      // At least one array is float/double.
      // Cast both arrays to double and call subsetOf(double[],double[])
      double[] elts_double = new double[Array.getLength(elts)];
      for (int i = 0 ; i < elts_double.length ; i++) {
        elts_double[i] = Array.getDouble(elts, i);
      }
      double[] arr_double = new double[Array.getLength(arr)];
      for (int i = 0 ; i < arr_double.length ; i++) {
        arr_double[i] = Array.getDouble(arr, i);
      }
      return subsetOf(elts_double, arr_double);
    } else {
      // throw new IllegalArgumentException("both arguments must be arrays of numeric types.");
      return false;
    }

  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(int @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(int elt, int @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(int elt, long @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static int @PolyNull [] slice(int @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new int[] { };
    }
    if (end > seq.length - 1) {
      return new int[] { };
    }
    if (sliceStart > sliceEnd) {
      return new int[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static int @PolyNull [] slice(int @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static int @PolyNull [] slice(int @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static int @PolyNull [] slice(int @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(int @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(int @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(int @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(int @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &gt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(int @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(int @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &ge; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(int @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(int @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &lt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(int @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(int @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &le; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(int @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(int @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &lt; seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &lt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &le; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &le; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &gt; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &gt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &ge; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &ge; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically &lt;  seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(int @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /** Returns true iff seq1 is lexically &le; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(int @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(int @Nullable [] seq1, long @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &gt; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(int @Nullable [] seq1, int @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(int @Nullable [] seq1, long @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &ge; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(int @Nullable [] seq1, int @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(int @Nullable [] seq1, long @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &lt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLT(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &le; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLTE(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &gt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGT(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &ge; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGTE(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqualIndex(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqualIndex(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &lt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLtIndex(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &le; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLteIndex(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &gt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGtIndex(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &ge; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGteIndex(int @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectint accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectint() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SideEffectFree
  public static int @Nullable [] collectint(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    int[] retval = collectint(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectint(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectint(Object, String)
   */

  @SideEffectFree
  private static int @Nullable [] collectint(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (int[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectint_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(Integer.TYPE);
        return new int[] { ((Integer)fieldObj).intValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        int[] intermediate = new int[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          int[] oneEltArray = collectint(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        int[] intermediate = new int[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          int[] oneEltArray = collectint(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectint(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectint_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static int collectint_field(Object object, String fieldStr) {

    if (object == null) {
      return Integer.MAX_VALUE; // return default value
    }
    if (fieldStr == null) {
      return Integer.MAX_VALUE; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return Integer.MAX_VALUE; // return default value
        }

      } catch (Exception e) {
        return Integer.MAX_VALUE; // return default value

      }

    }

    return ((Integer)fieldObj).intValue();
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for "long" (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (Long.MAX_VALUE).
   */

  @Pure
  public static long getElement_long(Object o, long i) {
    if (o == null) {
      return Long.MAX_VALUE; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getLong(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.getLong(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return Long.MAX_VALUE; // return default value
    }
  }

  @Pure
  public static long getElement_long(long[] arr, long i) {
    if (arr == null) {
      return Long.MAX_VALUE; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(long x, long y) {
    return x == y;
  }

  private static boolean ne(long x, long y) {
    return x != y;
  }

  private static boolean lt(long x, long y) {
    return x < y;
  }

  private static boolean lte(long x, long y) {
    return x <= y;
  }

  private static boolean gt(long x, long y) {
    return x > y;
  }

  private static boolean gte(long x, long y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(long @Nullable [] seq1, long @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(long @Nullable [] seq1, int @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(long[] seq1, long[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseDivides(long[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff both sequences have the same length, and all seq1[i] == seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == seq2[i] * seq2[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(long[] seq1, long[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseSquare(long[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {

      if (ne(seq1[i], ((long) seq2[i]) * ((long) seq2[i]))) {

        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] == ~ seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == ~ seq2[i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseComplement(long[] seq1, long[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] != ~seq2[i]) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseComplement(long[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] != ~seq2[i]) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] == (seq2[i] | seq1[i]).
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == (seq2[i] | seq1[i])
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseSubset(long[] seq1, long[] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    if (seq1.length != seq2.length) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], (seq2[i] | seq1[i]))) {
        return false;
      }
    }
    return true;
  }
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseBitwiseSubset(long[] seq1, int[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], (seq2[i] | seq1[i]))) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static long @PolyNull [] concat(long @PolyNull [] seq1, long @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  @SideEffectFree
  public static long @PolyNull [] concat(long @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    // Cannot just use ArraysPlume.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    long[] retval = new long[newLength];

    System.arraycopy(seq1, 0, retval, 0, seq1.length);
    for (int j = 0 ; j < seq2.length ; j++) {
      retval[seq1.length + j] = seq2[j];
    }

    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static long @PolyNull [] union(long @PolyNull [] seq1, long @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  @Pure
  public static long @PolyNull [] union(long @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static long @PolyNull [] intersection(long @PolyNull [] seq1, long @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    long[] intermediate = new long[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static long @PolyNull [] intersection(long @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    long[] intermediate = new long[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static long @PolyNull [] setDiff(long @PolyNull [] seq1, long @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    long[] intermediate = new long[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  @Pure
  public static long @PolyNull [] setDiff(long @PolyNull [] seq1, int @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    long[] intermediate = new long[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(long[] seq1, long[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(long @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(long elt, long @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(long elt, int @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static long @PolyNull [] slice(long @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new long[] { };
    }
    if (end > seq.length - 1) {
      return new long[] { };
    }
    if (sliceStart > sliceEnd) {
      return new long[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static long @PolyNull [] slice(long @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static long @PolyNull [] slice(long @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static long @PolyNull [] slice(long @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(long @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(long @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(long @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(long @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &gt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(long @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(long @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &ge; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(long @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(long @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &lt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(long @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(long @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &le; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(long @Nullable [] arr, long elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(long @Nullable [] arr, int elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &lt; seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &lt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &le; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &le; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &gt; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &gt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &ge; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &ge; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically &lt;  seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(long @Nullable [] seq1, int @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /** Returns true iff seq1 is lexically &le; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(long @Nullable [] seq1, long @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(long @Nullable [] seq1, int @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &gt; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(long @Nullable [] seq1, long @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(long @Nullable [] seq1, int @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &ge; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(long @Nullable [] seq1, long @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(long @Nullable [] seq1, int @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &lt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLT(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &le; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLTE(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &gt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGT(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &ge; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGTE(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqualIndex(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqualIndex(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &lt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLtIndex(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &le; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLteIndex(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &gt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGtIndex(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &ge; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGteIndex(long @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectlong accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectlong() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SideEffectFree
  public static long @Nullable [] collectlong(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    long[] retval = collectlong(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectlong(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectlong(Object, String)
   */

  @SideEffectFree
  private static long @Nullable [] collectlong(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (long[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectlong_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(Long.TYPE);
        return new long[] { ((Long)fieldObj).longValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        long[] intermediate = new long[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          long[] oneEltArray = collectlong(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        long[] intermediate = new long[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          long[] oneEltArray = collectlong(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectlong(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectlong_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static long collectlong_field(Object object, String fieldStr) {

    if (object == null) {
      return Long.MAX_VALUE; // return default value
    }
    if (fieldStr == null) {
      return Long.MAX_VALUE; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return Long.MAX_VALUE; // return default value
        }

      } catch (Exception e) {
        return Long.MAX_VALUE; // return default value

      }

    }

    return ((Long)fieldObj).longValue();
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for "short" (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (Short.MAX_VALUE).
   */

  @Pure
  public static short getElement_short(Object o, long i) {
    if (o == null) {
      return Short.MAX_VALUE; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getShort(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.getShort(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return Short.MAX_VALUE; // return default value
    }
  }

  @Pure
  public static short getElement_short(short[] arr, long i) {
    if (arr == null) {
      return Short.MAX_VALUE; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(short x, short y) {
    return x == y;
  }

  private static boolean ne(short x, short y) {
    return x != y;
  }

  private static boolean lt(short x, short y) {
    return x < y;
  }

  private static boolean lte(short x, short y) {
    return x <= y;
  }

  private static boolean gt(short x, short y) {
    return x > y;
  }

  private static boolean gte(short x, short y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(short @Nullable [] seq1, short @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static short @PolyNull [] concat(short @PolyNull [] seq1, short @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static short @PolyNull [] union(short @PolyNull [] seq1, short @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static short @PolyNull [] intersection(short @PolyNull [] seq1, short @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    short[] intermediate = new short[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static short @PolyNull [] setDiff(short @PolyNull [] seq1, short @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    short[] intermediate = new short[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(short[] seq1, short[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(short @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(short elt, short @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(long elt, short @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static short @PolyNull [] slice(short @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new short[] { };
    }
    if (end > seq.length - 1) {
      return new short[] { };
    }
    if (sliceStart > sliceEnd) {
      return new short[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static short @PolyNull [] slice(short @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static short @PolyNull [] slice(short @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static short @PolyNull [] slice(short @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(short @Nullable [] arr, short elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(short @Nullable [] arr, short elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &gt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGT(short @Nullable [] arr, short elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &ge; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGTE(short @Nullable [] arr, short elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &lt; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLT(short @Nullable [] arr, short elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] &le; elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLTE(short @Nullable [] arr, short elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &lt; seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &lt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLT(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &le; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &le; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseLTE(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &gt; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &gt; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGT(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] &ge; seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] &ge; seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseGTE(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically &lt;  seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLT(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &le; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexLTE(short @Nullable [] seq1, short @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &gt; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGT(short @Nullable [] seq1, short @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /** Returns true iff seq1 is lexically &ge; to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexGTE(short @Nullable [] seq1, short @Nullable [] seq2) {
  if (seq1 == null) {
    return false;
  }
  if (seq2 == null) {
    return false;
  }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &lt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLT(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &le; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseLTE(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (gt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &gt; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGT(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lte(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] &ge; seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseGTE(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (lt(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqualIndex(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqualIndex(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &lt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &lt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLtIndex(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &le; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &le; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsLteIndex(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &gt; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &gt; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGtIndex(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] &ge; i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] &ge; i
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsGteIndex(short @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectshort accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectshort() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SideEffectFree
  public static short @Nullable [] collectshort(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    short[] retval = collectshort(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectshort(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectshort(Object, String)
   */

  @SideEffectFree
  private static short @Nullable [] collectshort(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (short[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectshort_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(Short.TYPE);
        return new short[] { ((Short)fieldObj).shortValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        short[] intermediate = new short[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          short[] oneEltArray = collectshort(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        short[] intermediate = new short[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          short[] oneEltArray = collectshort(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectshort(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectshort_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static short collectshort_field(Object object, String fieldStr) {

    if (object == null) {
      return Short.MAX_VALUE; // return default value
    }
    if (fieldStr == null) {
      return Short.MAX_VALUE; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return Short.MAX_VALUE; // return default value
        }

      } catch (Exception e) {
        return Short.MAX_VALUE; // return default value

      }

    }

    return ((Short)fieldObj).shortValue();
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for #@Interned Object (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (null).
   */

  @SuppressWarnings("interning") // reflection

  @Pure
  public static @Nullable @Interned Object getElement_Object(Object o, long i) {
    if (o == null) {
      return null; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.get(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return java.lang.reflect.Array.get(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return null; // return default value
    }
  }

  @SuppressWarnings("interning") // called reflectively

  @Pure
  public static @Nullable @Interned Object getElement_Object(Object[] arr, long i) {
    if (arr == null) {
      return null; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(@Interned Object x, @Interned Object y) {
    return x == y;
  }

  private static boolean ne(@Interned Object x, @Interned Object y) {
    return x != y;
  }

  /**
   * Returns an array of Strings, where the strings are the result of invoking
   * x.getClass().toString() for each element x in the array. If an element of the array is null,
   * its slot in the returned array is null.
   */
  @SideEffectFree
  public static @PolyNull/*("elt")*/ String @PolyNull/*("container")*/ [] typeArray(@PolyNull/*("elt")*/ @Interned Object @PolyNull/*("container")*/ [] seq) {
    if (seq == null) {
      return null;
    }
    @PolyNull/*("elt")*/ String[] retval = new @PolyNull/*("elt")*/ String[seq.length];
    for (int i = 0 ; i < seq.length ; i++) {
      if (seq[i] == null) {
        retval[i] = null;
      } else {
        retval[i] = seq[i].getClass().toString();
      }
    }
    return retval;
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(Object @Nullable [] seq1, Object @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static @Interned Object @PolyNull [] concat(@Interned Object @PolyNull [] seq1, @Interned Object @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static @Interned Object @PolyNull [] union(@Interned Object @PolyNull [] seq1, @Interned Object @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static @Interned Object @PolyNull [] intersection(@Interned Object @PolyNull [] seq1, @Interned Object @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    @Interned Object[] intermediate = new @Interned Object[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static @Interned Object @PolyNull [] setDiff(@Interned Object @PolyNull [] seq1, @Interned Object @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    @Interned Object[] intermediate = new @Interned Object[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(@Interned Object @Nullable [] seq1, @Interned Object @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(@Interned Object[] seq1, @Interned Object[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(@PolyNull Collection<? extends @Interned Object> seq1, @Interned Object @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    @Interned Object[] seq1_array = seq1.toArray(new @Interned Object[]{});
    return isReverse(seq1_array, seq2);
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(@Interned Object @Nullable [] seq1, @Interned Object @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(@Nullable Collection<? extends @Interned Object> seq1, @Interned Object @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    @Interned Object[] seq1_array = seq1.toArray(new @Interned Object[]{});
    return subsetOf(seq1_array, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(@Interned Object @Nullable [] seq1, @Nullable Collection<? extends @Interned Object> seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    @Interned Object[] seq2_array = seq2.toArray(new @Interned Object[]{});
    return subsetOf(seq1, seq2_array);
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(@Interned Object @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(@Interned Object elt, @Interned Object @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static @Interned Object @PolyNull [] slice(@Interned Object @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new @Interned Object[] { };
    }
    if (end > seq.length - 1) {
      return new @Interned Object[] { };
    }
    if (sliceStart > sliceEnd) {
      return new @Interned Object[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static @Interned Object @PolyNull [] slice(@Interned Object @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static @Interned Object @PolyNull [] slice(@Interned Object @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static @Interned Object @PolyNull [] slice(@Interned Object @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(@Interned Object @Nullable [] arr, @Interned Object elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(@Interned Object @Nullable [] arr, @Interned Object elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(@Interned Object @Nullable [] seq1, @Interned Object @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(@Nullable AbstractCollection<@Interned Object> seq1, @Interned Object @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    @SuppressWarnings("cast") // cast is redundant (except in JSR 308)
    @Interned Object[] seq1a = (@Interned Object[]) seq1.toArray(new @Interned Object[0]);
    return pairwiseEqual(seq1a, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(@Interned Object @Nullable [] seq1, @Nullable AbstractCollection<@Interned Object> seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    @SuppressWarnings("cast") // cast is redundant (except in JSR 308)
    @Interned Object[] seq2a = (@Interned Object[]) seq2.toArray();
    return pairwiseEqual(seq1, seq2a);
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(@Interned Object @Nullable [] seq1, @Interned Object @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(@Interned Object @Nullable [] seq1, @Interned Object @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(@Interned Object @Nullable [] seq1, @Interned Object @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(@Interned Object @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(@Interned Object @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectObject accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectObject() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SuppressWarnings("interning") // reflection

  @SideEffectFree
  public static Object @Nullable [] collectObject(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    @Interned Object[] retval = collectObject(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectObject(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectObject(Object, String)
   */

  @SuppressWarnings("interning") // reflection

  @SideEffectFree
  private static Object @Nullable [] collectObject(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {
        // last field is the collection
        @SuppressWarnings("unchecked")
        java.util.AbstractCollection<@Interned Object> ac = (java.util.AbstractCollection<@Interned Object>)fieldObj;
        return ac.toArray(new @Interned Object[]{});
      } else

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (@Interned Object[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectObject_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(_TYPE_WRAPPER_NAME.TYPE);
        return new @Interned Object[] { fieldObj };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        @Interned Object[] intermediate = new @Interned Object[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          Object[] oneEltArray = collectObject(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        @Interned Object[] intermediate = new @Interned Object[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          Object[] oneEltArray = collectObject(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectObject(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectObject_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static @Nullable Object collectObject_field(Object object, String fieldStr) {

    if (object == null) {
      return null; // return default value
    }
    if (fieldStr == null) {
      return null; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return null; // return default value
        }

      } catch (Exception e) {
        return null; // return default value

      }

    }

    return fieldObj;
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Methods for #@Interned String (from QuantBody.java.jpp)
  //

  /**
   * Returns the ith element of the array or collection argument. If the argument is null or not an
   * array or collection, returns a default value (null).
   */

  @SuppressWarnings("interning") // reflection

  @Pure
  public static @Nullable @Interned String getElement_String(Object o, long i) {
    if (o == null) {
      return null; // return default value
    }
    java.lang.Class<?> c = o.getClass();
    if (c.isArray()) {
      return (String)java.lang.reflect.Array.get(o, (int)i);
    } else if (o instanceof java.util.AbstractCollection<?>) {
      return (String)java.lang.reflect.Array.get(((java.util.AbstractCollection<?>)o).toArray(), (int)i);
    } else {
      return null; // return default value
    }
  }

  @SuppressWarnings("interning") // called reflectively

  @Pure
  public static @Nullable @Interned String getElement_String(String[] arr, long i) {
    if (arr == null) {
      return null; // return default value
    }
    return arr[(int)i];
  }

  private static boolean eq(@Interned String x, @Interned String y) {
    return x == y;
  }

  private static boolean ne(@Interned String x, @Interned String y) {
    return x != y;
  }

  /** True iff both sequences are non-null and have the same length. */
  @EnsuresNonNullIf(result=true, expression={"#1", "#2"})
  @Pure
  public static boolean sameLength(String @Nullable [] seq1, String @Nullable [] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] } .
   *
   * <p>If either array is null, returns null. If either array is empty, returns only those
   * elements in the other array. If both arrays are empty, returns a new empty array.
   */
  @SideEffectFree
  public static @Interned String @PolyNull [] concat(@Interned String @PolyNull [] seq1, @Interned String @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return ArraysPlume.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2. This method gives no
   * assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @SideEffectFree
  public static @Interned String @PolyNull [] union(@Interned String @PolyNull [] seq1, @Interned String @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2. This method
   * gives no assurances about the order or repetition of elements: elements may be repeated, and
   * their order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static @Interned String @PolyNull [] intersection(@Interned String @PolyNull [] seq1, @Interned String @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    @Interned String[] intermediate = new @Interned String[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2. This method gives
   * no assurances about the order or repetition of elements: elements may be repeated, and their
   * order may be different from the order of elements in seq1 and seq2.
   */
  @Pure
  public static @Interned String @PolyNull [] setDiff(@Interned String @PolyNull [] seq1, @Interned String @PolyNull [] seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    @Interned String[] intermediate = new @Interned String[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return ArraysPlume.subarray(intermediate, 0, length);
  }

  /** Returns true iff seq1 and seq2 are equal when considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(@Interned String @Nullable [] seq1, @Interned String @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean isReverse(@Interned String[] seq1, @Interned String[] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is a subset of seq2, when the sequences are considered as sets. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean subsetOf(@Interned String @Nullable [] seq1, @Interned String @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /** Returns true iff seq contains no duplicate elements. */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(@Interned String @Nullable [] seq) {
    if (seq == null) {
      return false;
    }
    return ArraysPlume.hasNoDuplicates(seq);
  }

 /** Returns true iff elt is in array arr. */
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(@Interned String elt, @Interned String @Nullable [] arr) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return true;
      }
    }
    return false;
  }

  /** Returns a subsequence of seq with first elements seq[start] and last element seq[end]. */
  @Pure
  public static @Interned String @PolyNull [] slice(@Interned String @PolyNull [] seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) {
      return new @Interned String[] { };
    }
    if (end > seq.length - 1) {
      return new @Interned String[] { };
    }
    if (sliceStart > sliceEnd) {
      return new @Interned String[] { };
    }
    int length = sliceEnd - sliceStart + 1;
    return ArraysPlume.subarray(seq, sliceStart, length);
  }

  @Pure
  public static @Interned String @PolyNull [] slice(@Interned String @PolyNull [] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  @Pure
  public static @Interned String @PolyNull [] slice(@Interned String @PolyNull [] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  @Pure
  public static @Interned String @PolyNull [] slice(@Interned String @PolyNull [] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(@Interned String @Nullable [] arr, @Interned String elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(@Interned String @Nullable [] arr, @Interned String elt) {
    if (arr == null) {
      return false;
    }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(@Interned String @Nullable [] seq1, @Interned String @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(@Nullable AbstractCollection<@Interned String> seq1, @Interned String @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    @SuppressWarnings("cast") // cast is redundant (except in JSR 308)
    @Interned String[] seq1a = (@Interned String[]) seq1.toArray(new @Interned String[0]);
    return pairwiseEqual(seq1a, seq2);
  }

  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(@Interned String @Nullable [] seq1, @Nullable AbstractCollection<@Interned String> seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    @SuppressWarnings("cast") // cast is redundant (except in JSR 308)
    @Interned String[] seq2a = (@Interned String[]) seq2.toArray();
    return pairwiseEqual(seq1, seq2a);
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(@Interned String @Nullable [] seq1, @Interned String @Nullable [] seq2) {
    if (!sameLength(seq1, seq2)) {
      return false;
    }
    assert seq1 != null && seq2 != null; // because sameLength() = true
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(@Interned String @Nullable [] seq1, @Interned String @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(seq1, seq2);
  }

  /** Returns true iff seq1 is lexically not equal to seq2. */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(@Interned String @Nullable [] seq1, @Interned String @Nullable [] seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return !lexEqual(seq1, seq2);
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(@Interned String @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (ne(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(@Interned String @Nullable [] seq) {
  if (seq == null) {
    return false;
  }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length - 1) {
        if (eq(seq[i], seq[i + 1])) {
          return false;
        }
      }
    }
    return true;
  }

  // Dereferencing (accessing) fields

  /**
   * collectString accepts an object and a list of fields (one of which is of array type, and the
   * rest of which are not), and produces an array in which the original object has had the given
   * fields accessed.
   *
   * <p>Daikon creates invariants over "variables" such as the following.
   *
   * <dl>
   * <dt>x.arr[].z</dt> <dd>The result of collecting all elements y.z
   *              for all y's in array x.arr.</dd>
   * <dt>arr[].y.z</dt> <dd>The result of collecting all elements x.y.z
   *              for all x's in array arr.</dd>
   * <dt>x.y.z[]</dt>   <dd>The result of collecting all elements in array x.y.z[]</dd>
   * </dl>
   *
   * <p>The collectString() method does this collecting work.
   *
   * <p>Given an object (x, arr, or x, correspondingly, in the above examples) and a "field string"
   * (arr.z, y.z, or y.z, correspondingly, in the above example), the collect method collects the
   * elements that result from following the fields, one of which is assumed to be an array.
   *
   * <p>
   * requires: fieldStr.length() &gt; 0 and object != null
   * <p>
   * requires: fieldStr contains only field names, no "[]" strings.
   * <p>
   * requires: the method only works for field sequences with exactly one field representing an
   * array. For example, the collection a[].b[].c will fail.
   *
   * @return if the resulting collection is of non-primitive type, then returns an array of type
   * Object[]. Returns null if any array or field access causes an exception.
   */

  @SuppressWarnings("interning") // reflection

  @SideEffectFree
  public static String @Nullable [] collectString(@Nullable Object object, @Nullable String fieldStr) {

    if (object == null) {
      return null;
    }
    if (fieldStr == null) {
      return null;
    }

    // assert fieldStr != null && !"".equals(fieldStr);
    String[] fieldNames = fieldStr.split("\\.");
    @Interned String[] retval = collectString(object, fieldNames, 0);
    // System.err.println("%%% fieldArray returned: " + plume.Arrays.toString(retval));
    return retval;
  }

  // @PolyNull does not work for return type, because null is returned on error.
  /** Helper method for collectString(Object, String).
   * Operates on a subset of the fields: those in fields[fieldsStartIdx..].
   * @see collectString(Object, String)
   */

  @SuppressWarnings("interning") // reflection

  @SideEffectFree
  private static String @Nullable [] collectString(@Nullable Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) {
      return null;
    }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj;
    try {
      Field field = (object instanceof java.lang.Class<?>)
        ? ((Class<?>)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      // System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {
        // last field is the collection
        @SuppressWarnings("unchecked")
        java.util.AbstractCollection<@Interned String> ac = (java.util.AbstractCollection<@Interned String>)fieldObj;
        return ac.toArray(new @Interned String[]{});
      } else

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (@Interned String[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectString_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        // assert cls.equals(_TYPE_WRAPPER_NAME.TYPE);
        return new @Interned String[] { (String)fieldObj };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection<? extends Object> collection = (java.util.AbstractCollection<? extends Object>)fieldObj;
        @Interned String[] intermediate = new @Interned String[collection.size()];
        int index = 0;
        for (Iterator<? extends Object> i = collection.iterator() ; i.hasNext() ; ) {
          Object obj = i.next();
          String[] oneEltArray = collectString(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        @Interned String[] intermediate = new @Interned String[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object obj = Array.get(fieldObj, i);
          String[] oneEltArray = collectString(obj, fields, fieldsStartIdx + 1);
          if (oneEltArray == null) {
            throw new Error("bad fields: " + (obj == null ? "null" : obj.getClass()) + " " + Arrays.toString(fields) + " " + (fieldsStartIdx + 1));
          }
          // assert oneEltArray.length == 1;
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectString(fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  /**
   * Returns the results of dereferencing the fields for 'object'. For example, the call
   *
   * <pre>collectString_field(x, "f.g.h")</pre>
   *
   * has the same value as
   *
   * <pre>x.f.g.h</pre>.
   * Returns a default value if any field access causes an exception.
   */
  @SideEffectFree
  public static @Nullable String collectString_field(Object object, String fieldStr) {

    if (object == null) {
      return null; // return default value
    }
    if (fieldStr == null) {
      return null; // return default value
    }

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class<?>)
          ? ((Class<?>)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

        if (fieldObj == null) {
          return null; // return default value
        }

      } catch (Exception e) {
        return null; // return default value

      }

    }

    return (String)fieldObj;
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Collection methods (dispatch to array methods)
  //

  // These methods handle calls to quant methods that instead of passing an
  // Object[] array, pass a Collection.  Each method handles arrays or
  // Collections.  They convert any Collection to an array, then invoke the
  // Object[] version of the method.

  /**
   * See {@link #noDups(Object[])}.
   * @see #noDups(Object[])
   */
  @SuppressWarnings("interning") // cast from Object
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean noDups(Object seq) {
    if (seq == null) {
      return false;
    }
    return noDups(toObjArray(seq));
  }

  /**
   * See {@link #typeArray(Object[])}.
   * @see #typeArray(Object[])
   */
  @SuppressWarnings("interning") // cast from Object
  /*TODO: @AssertNonNullIfNonNull({"#1"})*/
  /* pure */ public static String @PolyNull [] typeArray(@PolyNull Object seq) {
    if (seq == null) {
      return null;
    }
    return typeArray(toObjArray(seq));
  }

  /**
   * See {@link #eltwiseEqual(Object[])}.
   * @see #eltwiseEqual(Object[])
   */
  @SuppressWarnings("interning") // cast from Object
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseEqual(Object seq) {
    if (seq == null) {
      return false;
    }
    return eltwiseEqual(toObjArray(seq));
  }

  /**
   * See {@link #eltwiseNotEqual(Object[])}.
   * @see #eltwiseNotEqual(Object[])
   */
  @SuppressWarnings("interning") // cast from Object
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltwiseNotEqual(Object seq) {
    if (seq == null) {
      return false;
    }
    return eltwiseNotEqual(toObjArray(seq));
  }

  /**
   * See {@link #concat(Object[], Object[])}.
   * @see #concat(Object[], Object[])
   */
  @SuppressWarnings("interning") // cast from Object
  /*TODO: @AssertNonNullIfNonNull({"#1","#2"})*/
  @SideEffectFree
  public static java.lang.Object @PolyNull [] concat(@PolyNull Object seq1, @PolyNull Object seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return concat(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #union(Object[], Object[])}.
   * @see #union(Object[], Object[])
   */
  @SuppressWarnings("interning") // cast from Object
  /*TODO: @AssertNonNullIfNonNull({"#1","#2"})*/
  @SideEffectFree
  public static java.lang.Object @PolyNull [] union(@PolyNull Object seq1, @PolyNull Object seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return union(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #intersection(Object[], Object[])}.
   * @see #intersection(Object[], Object[])
   */
  @SuppressWarnings("interning") // cast from Object
  /*TODO: @AssertNonNullIfNonNull({"#1","#2"})*/
  @SideEffectFree
  public static java.lang.Object @PolyNull [] intersection(@PolyNull Object seq1, @PolyNull Object seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return intersection(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #setDiff(Object[], Object[])}.
   * @see #setDiff(Object[], Object[])
   */
  @SuppressWarnings("interning") // cast from Object
  /*TODO: @AssertNonNullIfNonNull({"#1","#2"})*/
  @SideEffectFree
  public static java.lang.Object @PolyNull [] setDiff(@PolyNull Object seq1, @PolyNull Object seq2) {
    if (seq1 == null) {
      return null;
    }
    if (seq2 == null) {
      return null;
    }
    return setDiff(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #setEqual(Object[], Object[])}.
   * @see #setEqual(Object[], Object[])
   */
  @SuppressWarnings("interning")  // cast from Object
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean setEqual(@Nullable Object seq1, @Nullable Object seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return setEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #isReverse(Object[], Object[])}.
   * @see #isReverse(Object[], Object[])
   */
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @SuppressWarnings("interning")  // cast from Object
  @Pure
  public static boolean isReverse(@Nullable Object seq1, @Nullable Object seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return isReverse(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #pairwiseEqual(Object[], Object[])}.
   * @see #pairwiseEqual(Object[], Object[])
   */
  @SuppressWarnings("interning")  // cast from Object
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseEqual(@Nullable Object seq1, @Nullable Object seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #pairwiseNotEqual(Object[], Object[])}.
   * @see #pairwiseNotEqual(Object[], Object[])
   */
  @SuppressWarnings("interning")  // cast from Object
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean pairwiseNotEqual(@Nullable Object seq1, @Nullable Object seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return pairwiseNotEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #lexEqual(Object[], Object[])}.
   * @see #lexEqual(Object[], Object[])
   */
  @SuppressWarnings("interning")  // cast from Object
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexEqual(@Nullable Object seq1, @Nullable Object seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return lexEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #lexNotEqual(Object[], Object[])}.
   * @see #lexNotEqual(Object[], Object[])
   */
  @SuppressWarnings("interning")  // cast from Object
  @EnsuresNonNullIf(result=true, expression={"#1","#2"})
  @Pure
  public static boolean lexNotEqual(@Nullable Object seq1, @Nullable Object seq2) {
    if (seq1 == null) {
      return false;
    }
    if (seq2 == null) {
      return false;
    }
    return lexNotEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /**
   * See {@link #memberOf(Object, Object[])}.
   * @see #memberOf(Object, Object[])
   */
  @SuppressWarnings("interning")  // cast from Object
  @EnsuresNonNullIf(result=true, expression="#2")
  @Pure
  public static boolean memberOf(Object elt, @Nullable Object arr) {
    if (arr == null) {
      return false;
    }
    return memberOf(elt, toObjArray(arr));
  }

  /**
   * See {@link #slice(Object[], int, int)}.
   * @see #slice(Object[], int, int)
   */
  @SuppressWarnings("interning")  // cast from Object
  /*TODO: @AssertNonNullIfNonNull("#1")*/
  @SideEffectFree
  public static Object @PolyNull [] slice(@PolyNull Object seq, int start, int end) {
    if (seq == null) {
      return null;
    }
    return slice(toObjArray(seq), start, end);
  }

  /**
   * See {@link #eltsEqual(Object[], Object)}.
   * @see #eltsEqual(Object[], Object)
   */
  @SuppressWarnings("interning")  // cast from Object
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsEqual(@Nullable Object arr, Object elt) {
    if (arr == null) {
      return false;
    }
    return eltsEqual(toObjArray(arr), elt);
  }

  /**
   * See {@link #eltsNotEqual(Object[], Object)}.
   * @see #eltsNotEqual(Object[], Object)
   */
  @SuppressWarnings("interning")  // cast from Object
  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean eltsNotEqual(@Nullable Object arr, Object elt) {
    if (arr == null) {
      return false;
    }
    return eltsNotEqual(toObjArray(arr), elt);
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean isIntegralType(@Nullable Class<?> c) {
    if (c == null) {
      return false;
    }
    return
      (c.equals(Byte.TYPE)
       || c.equals(Short.TYPE)
       || c.equals(Integer.TYPE)
       || c.equals(Long.TYPE));
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean isRealType(@Nullable Class<?> c) {
    if (c == null) {
      return false;
    }
    return
      (c.equals(Float.TYPE)
       || c.equals(Double.TYPE));
  }

  @EnsuresNonNullIf(result=true, expression="#1")
  @Pure
  public static boolean isNumericType(Class<?> c) {
    if (c == null) {
      return false;
    }
    return isIntegralType(c) || isRealType(c);
  }

  /** Returns an Object[] array, either by casting or by conversion from an
   * AbstractCollection.
   */
  /*TODO: @AssertNonNullIfNonNull("#1")*/
  public static Object @PolyNull [] toObjArray(@PolyNull Object o) {
    if (o == null) {
      return null;
    }
    if (o instanceof java.util.AbstractCollection<?>) {
      @SuppressWarnings({"unchecked"})
        AbstractCollection<Object> ac = (AbstractCollection<Object>)o;
      Object [] result = ac.toArray(new java.lang.Object []{});
      return result;
    } else if (o.getClass().isArray()) {
      return (Object[])o;
    } else {
      throw new IllegalArgumentException("not an array or collection: " + o);
    }
  }
}
