package fj.data; import fj.*; import static fj.Bottom.error; import static fj.Monoid.intAdditionMonoid; import static fj.data.fingertrees.FingerTree.measured; import fj.data.List.Buffer; import fj.data.fingertrees.FingerTree; import fj.data.fingertrees.MakeTree; import fj.data.fingertrees.Measured; import java.util.AbstractList; import java.util.Iterator; import java.util.NoSuchElementException; /** * Provides an immutable finite sequence, implemented as a finger tree. This structure gives O(1) access to * the head and tail, as well as O(log n) random access and concatenation of sequences. */ public final class Seq<A> implements Iterable<A> { private static final Measured<Integer, Object> ELEM_MEASURED = measured(intAdditionMonoid, Function.constant(1)); private static final MakeTree<Integer, Object> MK_TREE = FingerTree.mkTree(ELEM_MEASURED); private static final Seq<Object> EMPTY = new Seq<>(MK_TREE.empty()); @SuppressWarnings("unchecked") private static <A> MakeTree<Integer, A> mkTree() { return (MakeTree<Integer, A>) MK_TREE; } private final FingerTree<Integer, A> ftree; private Seq(final FingerTree<Integer, A> ftree) { this.ftree = ftree; } @SuppressWarnings("unchecked") private static <A> Measured<Integer, A> elemMeasured() { return (Measured<Integer, A>) ELEM_MEASURED; } /** * The empty sequence. * * @return A sequence with no elements. */ @SuppressWarnings("unchecked") public static <A> Seq<A> empty() { return (Seq<A>) EMPTY; } @Override public boolean equals(Object other) { return Equal.equals0(Seq.class, this, other, () -> Equal.seqEqual(Equal.anyEqual())); } /** * A singleton sequence. * * @param a The single element in the sequence. * @return A new sequence with the given element in it. */ public static <A> Seq<A> single(final A a) { return new Seq<>(Seq.<A>mkTree().single(a)); } /** * Constructs a sequence from the given elements. * @param as The elements to create the sequence from. * @return A sequence with the given elements. */ @SafeVarargs public static <A> Seq<A> seq(final A... as) { return arraySeq(as); } /** * Constructs a sequence from the given list. * * @deprecated As of release 4.5, use {@link #listSeq(List)} * * @param list The list to create the sequence from. * @return A sequence with the given elements in the list. */ @Deprecated public static <A>Seq<A> seq(final List<A> list) { return iterableSeq(list); } /** * Constructs a sequence from the given list. * * @deprecated As of release 4.5, use {@link #iterableSeq} * * @param list The list to create the sequence from. * @return A sequence with the elements of the list. */ @Deprecated public static <A>Seq<A> listSeq(final List<A> list) { return iterableSeq(list); } /** * Constructs a sequence from the iterable. * @param i The iterable to create the sequence from. * @return A sequence with the elements of the iterable. */ public static <A>Seq<A> iterableSeq(final Iterable<A> i) { Seq<A> s = empty(); for (final A a: i) { s = s.snoc(a); } return s; } /** * Constructs a sequence from the iterator. * @param i The iterator to create the sequence from. * @return A sequence with the elements of the iterator. */ public static <A>Seq<A> iteratorSeq(final Iterator<A> i) { return iterableSeq(() -> i); } /** * Constructs a sequence from the array. */ @SafeVarargs public static <A>Seq<A> arraySeq(A... as) { return iterableSeq(Array.array(as)); } /** * Constructs a sequence from the given list. * @param list The list to create the sequence from. * @return A sequence with the elements of the list. */ public static <A>Seq<A> fromJavaList(final java.util.List<A> list) { return iterableSeq(list); } /** * Inserts the given element at the front of this sequence. * * @param a An element to insert at the front of this sequence. * @return A new sequence with the given element at the front. */ public Seq<A> cons(final A a) { return new Seq<>(ftree.cons(a)); } /** * Inserts the given element at the end of this sequence. * * @param a An element to insert at the end of this sequence. * @return A new sequence with the given element at the end. */ public Seq<A> snoc(final A a) { return new Seq<>(ftree.snoc(a)); } /** * The first element of this sequence. This is an O(1) operation. * * @return The first element if this sequence is nonempty, otherwise throws an error. */ public A head() { return ftree.head(); } public Option<A> headOption() { return ftree.headOption(); } /** * The last element of this sequence. This is an O(1) operation. * * @return The last element if this sequence is nonempty, otherwise throws an error. */ public A last() { return ftree.last(); } /** * The sequence without the first element. This is an O(1) operation. * * @return The sequence without the first element if this sequence is nonempty, otherwise throws an error. */ public Seq<A> tail() { return (length() == 1) ? empty() : new Seq<>(ftree.tail()); } /** * The sequence without the last element. This is an O(1) operation. * * @return The sequence without the last element if this sequence is nonempty, otherwise throws an error. */ public Seq<A> init() { return (length() == 1) ? empty() : new Seq<>(ftree.init()); } /** * Converts this sequence to a Stream */ public Stream<A> toStream() { return ftree.foldLeft((b, a) -> b.cons(a), Stream.<A>nil()).reverse(); } /** * Converts this sequence to a List */ public List<A> toList() { final Buffer<A> buf = Buffer.empty(); for (final A a : this) { buf.snoc(a); } return buf.toList(); } /** * Converts the sequence to a java.util.List */ public java.util.List<A> toJavaList() { return new AbstractList<A>() { @Override public A get(int i) { return index(i); } @Override public Iterator<A> iterator() { return Seq.this.iterator(); } @Override public int size() { return length(); } }; } /** * Returns an iterator for this seq. This method exists to permit the use in a <code>for</code>-each loop. * * @return A iterator for this seq. */ public Iterator<A> iterator() { return new Iterator<A>() { private FingerTree<Integer, A> ftree = Seq.this.ftree; public boolean hasNext() { return !ftree.isEmpty(); } public A next() { if (ftree.isEmpty()) throw new NoSuchElementException(); else { final A a = ftree.head(); ftree = ftree.tail(); return a; } } public void remove() { throw new UnsupportedOperationException(); } }; } @Override public String toString() { return Show.seqShow(Show.<A>anyShow()).showS(this); } /** * Appends the given sequence to this sequence. * * @param as A sequence to append to this one. * @return A new sequence with the given sequence appended to this one. */ public Seq<A> append(final Seq<A> as) { return new Seq<>(ftree.append(as.ftree)); } /** * Checks if this is the empty sequence. * * @return True if this sequence is empty, otherwise false. */ public boolean isEmpty() { return ftree.isEmpty(); } /** * Inserts the element at the given index. This is an O(log(n)) operation. * * @param index The index of the element to return. * @return The sequence with the element inserted at the given index, * or throws an error if the index is out of bounds. */ public Seq<A> insert(int index, A a) { final P2<Seq<A>, Seq<A>> p = split(index); return p._1().append(single(a)).append(p._2()); } /** * Checks if this sequence is not empty. * * @return True if this sequence is not empty, otherwise false. */ public boolean isNotEmpty() { return !ftree.isEmpty(); } /** * Returns the number of elements in this sequence. * * @return the number of elements in this sequence. */ public int length() { return ftree.measure(); } /** * Splits this sequence into a pair of sequences at the given position. This is a O(log(n)) operation. * * @return Pair: the subsequence containing elements with indices less than <code>i</code> * and the subsequence containing elements with indices greater than or equal to <code>i</code>. */ public P2<Seq<A>, Seq<A>> split(final int i) { final P2<FingerTree<Integer, A>, FingerTree<Integer, A>> lr = ftree.split(index -> index > i); return P.p(new Seq<>(lr._1()), new Seq<>(lr._2())); } /** * Returns the element at the given index. This is an O(log(n)) operation. * * @param i The index of the element to return. * @return The element at the given index, or throws an error if the index is out of bounds. */ public A index(final int i) { checkBounds(i); return ftree.lookup(Function.identity(), i)._2(); } /** * Replace the element at the given index with the supplied value. This is an O(log(n)) operation. * * @param i The index of the element to update. * @param a The new value. * * @return The updated sequence, or throws an error if the index is out of bounds. */ public Seq<A> update(final int i, final A a) { checkBounds(i); final P3<FingerTree<Integer, A>, A, FingerTree<Integer, A>> lxr = ftree.split1(index -> index > i); return new Seq<>(lxr._1().append(lxr._3().cons(a))); } /** * Delete the element at the given index. This is an O(log(n)) operation. * * @param i The index of the element to update. * * @return The updated sequence, or throws an error if the index is out of bounds. */ public Seq<A> delete(final int i) { checkBounds(i); final P3<FingerTree<Integer, A>, A, FingerTree<Integer, A>> lxr = ftree.split1(index -> index > i); return new Seq<>(lxr._1().append(lxr._3())); } /** * Takes the given number of elements from the head of this sequence if they are available. * * @param n The maximum number of elements to take from this sequence. * @return A sequence consisting only of the first n elements of this sequence, or else the whole sequence, * if it has less than n elements. */ public Seq<A> take(final int n) { return split(n)._1(); } /** * Drops the given number of elements from the head of this sequence if they are available. * * @param n The number of elements to drop from this sequence. * @return A sequence consisting of all elements of this sequence except the first n ones, or else the empty sequence, * if this sequence has less than n elements. */ public Seq<A> drop(final int n) { return split(n)._2(); } private void checkBounds(final int i) { if (i < 0 || i >= length()) throw error("Index " + i + " is out of bounds."); } public <B> B foldLeft(final F2<B, A, B> f, final B z) { return ftree.foldLeft(f, z); } public <B> B foldRight(final F2<A, B, B> f, final B z) { return ftree.foldRight(f, z); } public Seq<A> filter(F<A, Boolean> f) { return foldLeft((acc, a) -> f.f(a) ? acc.snoc(a) : acc, empty()); } @Override public int hashCode() { return Hash.seqHash(Hash.<A>anyHash()).hash(this); } public <B> Seq<B> map(F<A, B> f) { return new Seq<>(ftree.map(f, Seq.elemMeasured())); } }