All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.data.Array Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
package fj.data;

import fj.Equal;
import fj.F;
import fj.F0;
import fj.F2;
import fj.Hash;
import fj.P;
import fj.P2;
import fj.Show;
import fj.Unit;
import fj.function.Effect1;

import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;

import static fj.Function.constant;
import static fj.Function.curry;
import static fj.Function.identity;
import static fj.P.p;
import static fj.P.p2;
import static fj.Unit.unit;
import static fj.data.List.iterableList;
import static fj.data.Option.none;
import static fj.data.Option.some;
import static java.lang.Math.min;
import static java.lang.System.arraycopy;

/**
 * Provides an interface to arrays.
 *
 * @version %build.number%
 */
public final class Array implements Iterable {
  private final Object[] a;

  private Array(final Object[] a) {
    this.a = a;
  }

  /**
   * Returns an iterator for this array. This method exists to permit the use in a for-each loop.
   *
   * @return A iterator for this array.
   */
  public Iterator iterator() {
    return toCollection().iterator();
  }

  /**
   * Returns the element at the given index if it exists, fails otherwise.
   *
   * @param index The index at which to get the element to return.
   * @return The element at the given index if it exists, fails otherwise.
   */
  @SuppressWarnings("unchecked")
  public A get(final int index) {
    return (A) a[index];
  }

  @Override
  public int hashCode() {
    return Hash.arrayHash(Hash.anyHash()).hash(this);
  }

  /**
   * Sets the element at the given index to the given value.
   *
   * @param index The index at which to set the given value.
   * @param a     The value to set at the given index.
   * @return The unit value.
   */
  public Unit set(final int index, final A a) {
    this.a[index] = a;
    return unit();
  }

  /**
   * Returns the length of this array.
   *
   * @return The length of this array.
   */
  public int length() {
    return a.length;
  }

  public ImmutableProjection immutable() {
    return new ImmutableProjection<>(this);
  }

  /**
   * Returns true is this array is empty, false otherwise.
   *
   * @return true is this array is empty, false otherwise.
   */
  public boolean isEmpty() {
    return a.length == 0;
  }

  /**
   * Returns false is this array is empty, true otherwise.
   *
   * @return false is this array is empty, true otherwise.
   */
  public boolean isNotEmpty() {
    return a.length != 0;
  }

  /**
   * Returns a copy of the underlying primitive array.
   *
   * @param c A class for the returned array.
   * @return A copy of the underlying primitive array.
   */
  public A[] array(final Class c) {
    return copyOf(a, a.length, c);
  }

  /**
   * Returns a copy of the underlying primitive array.
   *
   * @return A copy of the underlying primitive array;
   */
  public Object[] array() {
    return copyOf(a, a.length);
  }

  /**
   * To be removed in future release:
   * affectation of the result of this method to a non generic array
   * will result in runtime error (ClassCastException).
   *
   * @deprecated As of release 4.6, use {@link #array(Class)}.
   */
  @SuppressWarnings("unchecked")
  @Deprecated
  public A[] toJavaArray() {
    return (A[]) array();
  }

  /**
   * Returns an option projection of this array; None if empty, or the first element in
   * Some.
   *
   * @return An option projection of this array.
   */
  @SuppressWarnings("unchecked")
  public Option toOption() {
    return a.length == 0 ? Option.none() : some((A) a[0]);
  }

  /**
   * Returns an either projection of this array; the given argument in Left if empty,
   * or the first element in Right.
   *
   * @param x The value to return in left if this array is empty.
   * @return An either projection of this array.
   */
  @SuppressWarnings("unchecked")
  public  Either toEither(final F0 x) {
    return a.length == 0 ? Either.left(x.f()) : Either.right((A) a[0]);
  }

  /**
   * Returns a list projection of this array.
   *
   * @return A list projection of this array.
   */
  @SuppressWarnings("unchecked")
  public List toList() {
    List x = List.nil();

    for (int i = a.length - 1; i >= 0; i--) {
      x = x.cons((A) a[i]);
    }

    return x;
  }

  /**
   * Returns a stream projection of this array.
   *
   * @return A stream projection of this array.
   */
  @SuppressWarnings("unchecked")
  public Stream toStream() {
    return Stream.unfold(o ->
        a.length > o ? some(p((A) a[o], o + 1)) : Option.none(), 0
    );
  }

  @Override
  public String toString() {
    return Show.arrayShow(Show.anyShow()).showS(this);
  }

  /**
   * Maps the given function across this array.
   *
   * @param f The function to map across this array.
   * @return A new array after the given function has been applied to each element.
   */
  @SuppressWarnings("unchecked")
  public  Array map(final F f) {
    final Object[] bs = new Object[a.length];

    for (int i = 0; i < a.length; i++) {
      bs[i] = f.f((A) a[i]);
    }

    return new Array<>(bs);
  }

  /**
   * Filters elements from this array by returning only elements which produce true
   * when the given function is applied to them.
   *
   * @param f The predicate function to filter on.
   * @return A new array whose elements all match the given predicate.
   */
  @SuppressWarnings("unchecked")
  public Array filter(final F f) {
    List x = List.nil();

    for (int i = a.length - 1; i >= 0; i--) {
      if (f.f((A) a[i]))
        x = x.cons((A) a[i]);
    }

    return x.toArray();
  }

  /**
   * Performs a side-effect for each element of this array.
   *
   * @param f The side-effect to perform for the given element.
   * @return The unit value.
   */
  @SuppressWarnings("unchecked")
  public Unit foreach(final F f) {
    for (final Object x : a) {
      f.f((A) x);
    }

    return unit();
  }

  /**
   * Performs a side-effect for each element of this array.
   *
   * @param f The side-effect to perform for the given element.
   */
  @SuppressWarnings("unchecked")
  public void foreachDoEffect(final Effect1 f) {
    for (final Object x : a) {
      f.f((A) x);
    }
  }

  /**
   * Performs a right-fold reduction across this array. This function runs in constant stack space.
   *
   * @param f The function to apply on each element of the array.
   * @param b The beginning value to start the application from.
   * @return The final result after the right-fold reduction.
   */
  @SuppressWarnings("unchecked")
  public  B foldRight(final F> f, final B b) {
    B x = b;

    for (int i = a.length - 1; i >= 0; i--)
      x = f.f((A) a[i]).f(x);

    return x;
  }

  /**
   * Performs a right-fold reduction across this array. This function runs in constant stack space.
   *
   * @param f The function to apply on each element of the array.
   * @param b The beginning value to start the application from.
   * @return The final result after the right-fold reduction.
   */
  public  B foldRight(final F2 f, final B b) {
    return foldRight(curry(f), b);
  }

  /**
   * Performs a left-fold reduction across this array. This function runs in constant space.
   *
   * @param f The function to apply on each element of the array.
   * @param b The beginning value to start the application from.
   * @return The final result after the left-fold reduction.
   */
  @SuppressWarnings("unchecked")
  public  B foldLeft(final F> f, final B b) {
    B x = b;

    for (final Object aa : a)
      x = f.f(x).f((A) aa);

    return x;
  }

  /**
   * Performs a left-fold reduction across this array. This function runs in constant space.
   *
   * @param f The function to apply on each element of the array.
   * @param b The beginning value to start the application from.
   * @return The final result after the left-fold reduction.
   */
  public  B foldLeft(final F2 f, final B b) {
    return foldLeft(curry(f), b);
  }

  /**
   * Performs a fold left accummulating and returns an array of the intermediate results.
   * This function runs in constant stack space.
   *
   * @param f The function to apply on each argument pair (initial value/previous result and next array element)
   * @param b The beginning value to start the application from.
   * @return The array containing all intermediate results of the left-fold reduction.
   */
  @SuppressWarnings("unchecked")
  public  Array scanLeft(final F> f, final B b) {
    final Object[] bs = new Object[a.length];
    B x = b;
    
    for (int i = 0; i < a.length; i++) {
      x = f.f(x).f((A) a[i]);
      bs[i] = x;
    }
    
    return new Array<>(bs);
  }

  /**
   * Performs a left-fold accummulating and returns an array of the intermediate results.
   * This function runs in constant stack space.
   *
   * @param f The function to apply on each argument pair (initial value/previous result and next array element)
   * @param b The beginning value to start the application from.
   * @return The array containing all intermediate results of the left-fold reduction.
   */
  public  Array scanLeft(final F2 f, final B b) {
    return scanLeft(curry(f), b);
  }

  /**
   * Performs a left-fold accummulating using first array element as a starting value
   * and returns an array of the intermediate results.
   * It will fail for empty arrays.
   * This function runs in constant stack space.
   *
   * @param f The function to apply on each argument pair (next array element and first array element/previous result)
   * @return The array containing all intermediate results of the left-fold reduction.
   */
  @SuppressWarnings("unchecked")
  public Array scanLeft1(final F> f) {
    final Object[] bs = new Object[a.length];
    A x = get(0);
    bs[0] = x;

    for (int i = 1; i < a.length; i++) {
      x = f.f(x).f((A) a[i]);
      bs[i] = x;
    }

    return new Array<>(bs);
  }

  /**
   * Performs a left-fold accummulating using first array element as a starting value
   * and returns an array of the intermediate results.
   * It will fail for empty arrays.
   * This function runs in constant stack space.
   *
   * @param f The function to apply on each argument pair (next array element and first array element/previous result)
   * @return The array containing all intermediate results of the left-fold reduction.
   */
  public Array scanLeft1(final F2 f) {
    return scanLeft1(curry(f));
  }

  /**
   * Performs a right-fold accummulating and returns an array of the intermediate results.
   * This function runs in constant stack space.
   *
   * @param f The function to apply on each argument pair (previous array element and initial value/previous result)
   * @param b The beginning value to start the application from.
   * @return The array containing all intermediate results of the right-fold reduction.
   */
  @SuppressWarnings("unchecked")
  public  Array scanRight(final F>f, final B b) {
    final Object[] bs = new Object[a.length];
    B x = b;

    for (int i = a.length - 1; i >= 0; i--) {
      x = f.f((A) a[i]).f(x);
      bs[i] = x;
    }

    return new Array<>(bs);
  }

  /**
   * Performs a right-fold accummulating and returns an array of the intermediate results.
   * This function runs in constant stack space.
   *
   * @param f The function to apply on each argument pair (previous array element and initial value/previous result)
   * @param b The beginning value to start the application from.
   * @return The array containing all intermediate results of the right-fold reduction.
   */
  public  Array scanRight(final F2 f, final B b) {
    return scanRight(curry(f), b);
  }

  /**
   * Performs a right-fold accummulating using last array element as a starting value
   * and returns an array of the intermediate results.
   * It will fail for empty arrays.
   * This function runs in constant stack space.
   *
   * @param f The function to apply on each argument pair (previous array element and last array element/previous result)
   * @return The array containing all intermediate results of the right-fold reduction.
   */
  @SuppressWarnings("unchecked")
  public Array scanRight1(final F>f) {
    final Object[] bs = new Object[a.length];
    A x = get(length() - 1);
    bs[length() - 1] = x;

    for (int i = a.length - 2; i >= 0; i--) {
      x = f.f((A) a[i]).f(x);
      bs[i] = x;
    }

    return new Array<>(bs);
  }

  /**
   * Performs a right-fold accummulating using last array element as a starting value
   * and returns an array of the intermediate results.
   * It will fail for empty arrays.
   * This function runs in constant stack space.
   *
   * @param f The function to apply on each argument pair (previous array element and last array element/previous result)
   * @return The array containing all intermediate results of the right-fold reduction.
   */
  public Array scanRight1(final F2 f) {
    return scanRight1(curry(f));
  }

  /**
   * Binds the given function across each element of this array with a final join.
   *
   * @param f The function to apply to each element of this array.
   * @return A new array after performing the map, then final join.
   */
  @SuppressWarnings("unchecked")
  public  Array bind(final F> f) {
    List> x = List.nil();
    int len = 0;

    for (int i = a.length - 1; i >= 0; i--) {
      final Array bs = f.f((A) a[i]);
      len = len + bs.length();
      x = x.cons(bs);
    }

    final Object[] bs = new Object[len];

    x.foreach(new F, Unit>() {
      private int i;

      public Unit f(final Array x) {
        arraycopy(x.a, 0, bs, i, x.a.length);
        i = i + x.a.length;
        return unit();
      }
    });

    return new Array<>(bs);
  }

  /**
   * Performs a bind across each array element, but ignores the element value each time.
   *
   * @param bs The array to apply in the final join.
   * @return A new array after the final join.
   */
  public  Array sequence(final Array bs) {
    final F> c = constant(bs);
    return bind(c);
  }

  /**
   * Binds the given function across each element of this array and the given array with a final
   * join.
   *
   * @param sb A given array to bind the given function with.
   * @param f  The function to apply to each element of this array and the given array.
   * @return A new array after performing the map, then final join.
   */
  public  Array bind(final Array sb, final F> f) {
    return sb.apply(map(f));
  }

  /**
   * Binds the given function across each element of this array and the given array with a final
   * join.
   *
   * @param sb A given array to bind the given function with.
   * @param f  The function to apply to each element of this array and the given array.
   * @return A new array after performing the map, then final join.
   */
  public  Array bind(final Array sb, final F2 f) {
    return bind(sb, curry(f));
  }

  /**
   * Performs function application within an array (applicative functor pattern).
   *
   * @param lf The array of functions to apply.
   * @return A new array after applying the given array of functions through this array.
   */
  public  Array apply(final Array> lf) {
    return lf.bind(f -> map(f));
  }

  /**
   * Reverse this array in constant stack space.
   *
   * @return A new array that is the reverse of this one.
   */
  public Array reverse() {
    final Object[] x = new Object[a.length];

    for (int i = 0; i < a.length; i++) {
      x[a.length - 1 - i] = a[i];
    }

    return new Array<>(x);
  }

  /**
   * Appends the given array to this array.
   *
   * @param aas The array to append to this one.
   * @return A new array that has appended the given array.
   */
  public Array append(final Array aas) {
    final Object[] x = new Object[a.length + aas.a.length];

    arraycopy(a, 0, x, 0, a.length);
    arraycopy(aas.a, 0, x, a.length, aas.a.length);

    return new Array<>(x);
  }

  /**
   * Returns an empty array.
   *
   * @return An empty array.
   */
  public static  Array empty() {
    return new Array<>(new Object[0]);
  }

  /**
   * Constructs an array from the given elements.
   *
   * @param as The elements to construct the array with.
   * @return A new array of the given elements.
   */
  @SafeVarargs
  public static  Array array(final A...as) {
    return arrayArray(as);
  }

  /**
   * Unsafe package-private constructor. The elements of the given array must be assignable to the given type.
   *
   * @param a An array with elements of the given type.
   * @return A wrapped array.
   */
  static  Array mkArray(final Object[] a) {
    return new Array<>(a);
  }

  /**
   * Constructs a singleton array.
   *
   * @param a The element to put in the array.
   * @return An array with the given single element.
   */
  public static  Array single(final A a) {
    return new Array<>(new Object[]{a});
  }

  /**
   * First-class wrapper function for arrays.
   *
   * @return A function that wraps a given array.
   */
  public static  F> wrap() {
    return Array::array;
  }

  /**
   * First-class map function for Arrays.
   *
   * @return A function that maps a given function across a given array.
   */
  public static  F, F, Array>> map() {
    return curry((abf, array) -> array.map(abf));
  }

  /**
   * Joins the given array of arrays using a bind operation.
   *
   * @param o The array of arrays to join.
   * @return A new array that is the join of the given arrays.
   */
  public static  Array join(final Array> o) {
    final F, Array> id = identity();
    return o.bind(id);
  }

  /**
   * A first-class version of join
   *
   * @return A function that joins a array of arrays using a bind operation.
   */
  public static  F>, Array> join() {
    return Array::join;
  }

  /**
   * Returns true if the predicate holds for all of the elements of this array,
   * false otherwise (true for the empty array).
   *
   * @param f the predicate function to test on each element of this array.
   * @return true if the predicate holds for all of the elements of this array,
   *         false otherwise.
   */
  @SuppressWarnings("unchecked")
  public boolean forall(final F f) {
    for (final Object x : a)
      if (!f.f((A) x))
        return false;

    return true;
  }

  /**
   * Returns true if the predicate holds for at least one of the elements of this
   * array, false otherwise (false for the empty array).
   *
   * @param f the predicate function to test on the elements of this array.
   * @return true if the predicate holds for at least one of the elements of this
   *         array.
   */
  @SuppressWarnings("unchecked")
  public boolean exists(final F f) {
    for (final Object x : a)
      if (f.f((A) x))
        return true;

    return false;
  }

  @Override
  public boolean equals(Object o) {
    return Equal.equals0(Array.class, this, o, () -> Equal.arrayEqual(Equal.anyEqual()));
  }

  /**
   * Finds the first occurrence of an element that matches the given predicate or no value if no
   * elements match.
   *
   * @param f The predicate function to test on elements of this array.
   * @return The first occurrence of an element that matches the given predicate or no value if no
   *         elements match.
   */
  @SuppressWarnings("unchecked")
  public Option find(final F f) {
    for (final Object x : a)
      if (f.f((A) x))
        return some((A) x);

    return none();
  }

  /**
   * Returns an array of integers from the given from value (inclusive) to the given
   * to value (exclusive).
   *
   * @param from The minimum value for the array (inclusive).
   * @param to   The maximum value for the array (exclusive).
   * @return An array of integers from the given from value (inclusive) to the given
   *         to value (exclusive).
   */
  public static Array range(final int from, final int to) {
    if (from >= to)
      return empty();
    else {
      final Array a = new Array<>(new Integer[to - from]);

      for (int i = from; i < to; i++)
        a.set(i - from, i);

      return a;
    }
  }

  /**
   * Zips this array with the given array using the given function to produce a new array. If this
   * array and the given array have different lengths, then the longer array is normalised so this
   * function never fails.
   *
   * @param bs The array to zip this array with.
   * @param f  The function to zip this array and the given array with.
   * @return A new array with a length the same as the shortest of this array and the given array.
   */
  public  Array zipWith(final Array bs, final F> f) {
    final int len = min(a.length, bs.length());
    final Array x = new Array<>(new Object[len]);

    for (int i = 0; i < len; i++) {
      x.set(i, f.f(get(i)).f(bs.get(i)));
    }

    return x;
  }

  /**
   * Zips this array with the given array using the given function to produce a new array. If this
   * array and the given array have different lengths, then the longer array is normalised so this
   * function never fails.
   *
   * @param bs The array to zip this array with.
   * @param f  The function to zip this array and the given array with.
   * @return A new array with a length the same as the shortest of this array and the given array.
   */
  public  Array zipWith(final Array bs, final F2 f) {
    return zipWith(bs, curry(f));
  }

  /**
   * Zips this array with the given array to produce an array of pairs. If this array and the given
   * array have different lengths, then the longer array is normalised so this function never fails.
   *
   * @param bs The array to zip this array with.
   * @return A new array with a length the same as the shortest of this array and the given array.
   */
  public  Array> zip(final Array bs) {
    final F>> __2 = p2();
    return zipWith(bs, __2);
  }

  /**
   * Zips this array with the index of its element as a pair.
   *
   * @return A new array with the same length as this array.
   */
  public Array> zipIndex() {
    return zipWith(range(0, length()), a -> i -> p(a, i));
  }

  /**
   * Projects an immutable collection of this array.
   *
   * @return An immutable collection of this array.
   */
  public Collection toCollection() {
    return asJavaList();
  }

  /**
   * Projects an unmodifiable list view of this array.
   *
   * @return An unmodifiable list view of this array.
   */
  @SuppressWarnings("unchecked")
  public java.util.List asJavaList() {
    return Collections.unmodifiableList(Arrays.asList((A[]) a));
  }

  /**
   * Returns a java.util.ArrayList projection of this array.
   */
  public ArrayList toJavaList() {
    return new ArrayList<>(asJavaList());
  }

  /**
   * Takes the given iterable to an array.
   *
   * @param i The iterable to take to an array.
   * @return An array from the given iterable.
   */
  public static  Array iterableArray(final Iterable i) {
    return iterableList(i).toArray();
  }

  /**
   * Creates an Array from the iterator.
   */
  public static  Array iteratorArray(final Iterator i) {
    return iterableArray(() -> i);
  }

  /**
   * Returns a copy of the underlying primitive array.
   * Equivalent to array(A...)
   *
   * @return A copy of the underlying primitive array.
   */
  @SafeVarargs
  public static  Array arrayArray(final A...as) {
    return new Array<>(as);
  }

  /**
   * Transforms an array of pairs into an array of first components and an array of second components.
   *
   * @param xs The array of pairs to transform.
   * @return An array of first components and an array of second components.
   */
  @SuppressWarnings("unchecked")
  public static  P2, Array> unzip(final Array> xs) {
    final int len = xs.length();
    final Array aa = new Array<>(new Object[len]);
    final Array ab = new Array<>(new Object[len]);
    for (int i = len - 1; i >= 0; i--) {
      final P2 p = xs.get(i);
      aa.set(i, p._1());
      ab.set(i, p._2());
    }
    return p(aa, ab);
  }

  /**
   * Projects an array by providing only operations which do not mutate.
   */
  public static final class ImmutableProjection implements Iterable {
    private final Array a;

    private ImmutableProjection(final Array a) {
      this.a = a;
    }

    /**
     * Returns an iterator for this array. This method exists to permit the use in a for-each loop.
     *
     * @return A iterator for this array.
     */
    public Iterator iterator() {
      return a.iterator();
    }

    /**
     * Returns the element at the given index if it exists, fails otherwise.
     *
     * @param index The index at which to get the element to return.
     * @return The element at the given index if it exists, fails otherwise.
     */
    public A get(final int index) {
      return a.get(index);
    }

    /**
     * Returns the length of this array.
     *
     * @return The length of this array.
     */
    public int length() {
      return a.length();
    }

    /**
     * Returns true is this array is empty, false otherwise.
     *
     * @return true is this array is empty, false otherwise.
     */
    public boolean isEmpty() {
      return a.isEmpty();
    }

    /**
     * Returns false is this array is empty, true otherwise.
     *
     * @return false is this array is empty, true otherwise.
     */
    public boolean isNotEmpty() {
      return a.isNotEmpty();
    }

    /**
     * Returns an option projection of this array; None if empty, or the first element
     * in Some.
     *
     * @return An option projection of this array.
     */
    public Option toOption() {
      return a.toOption();
    }

    /**
     * Returns an either projection of this array; the given argument in Left if empty,
     * or the first element in Right.
     *
     * @param x The value to return in left if this array is empty.
     * @return An either projection of this array.
     */
    public  Either toEither(final F0 x) {
      return a.toEither(x);
    }

    /**
     * Returns a list projection of this array.
     *
     * @return A list projection of this array.
     */
    public List toList() {
      return a.toList();
    }

    /**
     * Returns a stream projection of this array.
     *
     * @return A stream projection of this array.
     */
    public Stream toStream() {
      return a.toStream();
    }

    /**
     * Maps the given function across this array.
     *
     * @param f The function to map across this array.
     * @return A new array after the given function has been applied to each element.
     */
    public  Array map(final F f) {
      return a.map(f);
    }

    /**
     * Filters elements from this array by returning only elements which produce true
     * when the given function is applied to them.
     *
     * @param f The predicate function to filter on.
     * @return A new array whose elements all match the given predicate.
     */
    public Array filter(final F f) {
      return a.filter(f);
    }

    /**
     * Performs a side-effect for each element of this array.
     *
     * @param f The side-effect to perform for the given element.
     * @return The unit value.
     */
    public Unit foreach(final F f) {
      return a.foreach(f);
    }

    /**
     * Performs a right-fold reduction across this array. This function uses O(length) stack space.
     *
     * @param f The function to apply on each element of the array.
     * @param b The beginning value to start the application from.
     * @return The final result after the right-fold reduction.
     */
    public  B foldRight(final F> f, final B b) {
      return a.foldRight(f, b);
    }

    /**
     * Performs a left-fold reduction across this array. This function runs in constant space.
     *
     * @param f The function to apply on each element of the array.
     * @param b The beginning value to start the application from.
     * @return The final result after the left-fold reduction.
     */
    public  B foldLeft(final F> f, final B b) {
      return a.foldLeft(f, b);
    }

    /**
     * Binds the given function across each element of this array with a final join.
     *
     * @param f The function to apply to each element of this array.
     * @return A new array after performing the map, then final join.
     */
    public  Array bind(final F> f) {
      return a.bind(f);
    }

    /**
     * Performs a bind across each array element, but ignores the element value each time.
     *
     * @param bs The array to apply in the final join.
     * @return A new array after the final join.
     */
    public  Array sequence(final Array bs) {
      return a.sequence(bs);
    }

    /**
     * Performs function application within an array (applicative functor pattern).
     *
     * @param lf The array of functions to apply.
     * @return A new array after applying the given array of functions through this array.
     */
    public  Array apply(final Array> lf) {
      return a.apply(lf);
    }

    /**
     * Reverse this array in constant stack space.
     *
     * @return A new array that is the reverse of this one.
     */
    public Array reverse() {
      return a.reverse();
    }

    /**
     * Appends the given array to this array.
     *
     * @param aas The array to append to this one.
     * @return A new array that has appended the given array.
     */
    public Array append(final Array aas) {
      return a.append(aas);
    }

    /**
     * Projects an immutable collection of this array.
     *
     * @return An immutable collection of this array.
     */
    public Collection toCollection() {
      return a.toCollection();
    }
  }

  @SuppressWarnings({"SuspiciousSystemArraycopy", "unchecked", "ObjectEquality", "RedundantCast"})
  public static  T[] copyOf(final U[] a, final int len, final Class newType) {
    final T[] copy = (Object)newType == Object[].class
        ? (T[]) new Object[len]
        : (T[]) java.lang.reflect.Array.newInstance(newType.getComponentType(), len);
    arraycopy(a, 0, copy, 0,
        min(a.length, len));
    return copy;
  }

  @SuppressWarnings("unchecked")
  public static  T[] copyOf(final T[] a, final int len) {
      return (T[]) copyOf(a, len, a.getClass());
  }

  public static char[] copyOfRange(final char[] a, final int from, final int to) {
      final int len = to - from;
      if (len < 0)
          throw new IllegalArgumentException(from + " > " + to);
      final char[] copy = new char[len];
      arraycopy(a, from, copy, 0,
                       min(a.length - from, len));
      return copy;
  }
}