diff --git a/pom.xml b/pom.xml index 30e8fbe6..23e8b770 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ org.junit.jupiter junit-jupiter - 5.7.0 + 5.10.1 test @@ -55,7 +55,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.11.0 1.8 1.8 @@ -64,12 +64,12 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.2.3 org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.11 diff --git a/src/main/java/com/bestvike/Index.java b/src/main/java/com/bestvike/Index.java new file mode 100644 index 00000000..2211960a --- /dev/null +++ b/src/main/java/com/bestvike/Index.java @@ -0,0 +1,99 @@ +package com.bestvike; + +import com.bestvike.linq.exception.ExceptionArgument; +import com.bestvike.linq.exception.ThrowHelper; + +/** + * Represent a type can be used to index a collection either from the start or the end. + *

+ *

+ * {@code
+ * List list = Arrays.asList(1, 2, 3, 4, 5);
+ * Integer lastElement = Linq.of(list).elementAt(Index.fromEnd(1)); // lastElement = 5
+ * }
+ * 
+ *

+ * Created by 许崇雷 on 2023-05-31. + */ +public final class Index { + public static final Index Start = new Index(0); + public static final Index End = new Index(~0); + private final int value; + + // The following private constructors mainly created for perf reason to avoid the checks + private Index(int value) { + this.value = value; + } + + public Index(int value, boolean fromEnd) { + if (value < 0) + ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.value); + if (fromEnd) { + this.value = ~value; + } else { + this.value = value; + } + } + + public static Index fromStart(int value) { + if (value < 0) + ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.value); + return new Index(value); + } + + public static Index fromEnd(int value) { + if (value < 0) + ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.value); + return new Index(~value); + } + + public int getValue() { + if (this.value < 0) + return ~this.value; + else + return this.value; + } + + public boolean isFromEnd() { + return this.value < 0; + } + + public int getOffset(int length) { + int offset = this.value; + if (this.isFromEnd()) { + offset += length + 1; + } + return offset; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Index) { + return this.value == ((Index) obj).value; + } + return false; + } + + @Override + public int hashCode() { + return this.value; + } + + @Override + @SuppressWarnings("MethodDoesntCallSuperMethod") + protected Index clone() { + return new Index(this.value); + } + + @Override + public String toString() { + if (this.isFromEnd()) { + return this.toStringFromEnd(); + } + return String.valueOf(this.value); + } + + private String toStringFromEnd() { + return '^' + String.valueOf(this.getValue()); + } +} diff --git a/src/main/java/com/bestvike/Range.java b/src/main/java/com/bestvike/Range.java new file mode 100644 index 00000000..d0a73dc2 --- /dev/null +++ b/src/main/java/com/bestvike/Range.java @@ -0,0 +1,106 @@ +package com.bestvike; + +import com.bestvike.linq.exception.ExceptionArgument; +import com.bestvike.linq.exception.ThrowHelper; + +/** + * Represent a range has start and end indexes. + *

+ *

+ * {@code
+ * List someArray = Arrays.asList(1, 2, 3, 4, 5);
+ * Integer[] subArray1 = Linq.of(someArray).take(Range.endAt(Index.fromStart(2))).toArray(Integer.class); // { 1, 2 }
+ * Integer[] subArray2 = Linq.of(someArray).take(Range.startAt(Index.fromStart(1))).toArray(Integer.class); // { 2, 3, 4, 5 }
+ * }
+ * 
+ *

+ * Created by 许崇雷 on 2023-05-31. + */ +public final class Range { + public static final Range All = new Range(Index.Start, Index.End); + private final Index start; + private final Index end; + + public Range(Index start, Index end) { + if (start == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.start); + if (end == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.end); + this.start = start; + this.end = end; + } + + public static Range startAt(Index start) { + return new Range(start, Index.End); + } + + public static Range endAt(Index end) { + return new Range(Index.Start, end); + } + + public Index getStart() { + return this.start; + } + + public Index getEnd() { + return this.end; + } + + public OffsetLength getOffsetAndLength(int length) { + int start; + Index startIndex = this.getStart(); + if (startIndex.isFromEnd()) + start = length - startIndex.getValue(); + else + start = startIndex.getValue(); + + int end; + Index endIndex = this.getEnd(); + if (endIndex.isFromEnd()) + end = length - endIndex.getValue(); + else + end = endIndex.getValue(); + + if (Integer.compareUnsigned(end, length) > 0 || Integer.compareUnsigned(start, end) > 0) + ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.length); + + return new OffsetLength(start, end - start); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Range) { + Range r = (Range) obj; + return this.start.equals(r.start) && this.end.equals(r.end); + } + return false; + } + + @Override + public int hashCode() { + int h1 = this.start.hashCode(); + int h2 = this.end.hashCode(); + return (h1 << 5) + h1 ^ h2; + } + + @Override + @SuppressWarnings("MethodDoesntCallSuperMethod") + protected Range clone() { + return new Range(this.start, this.end); + } + + @Override + public String toString() { + return this.start.toString() + ".." + this.end.toString(); + } + + public static final class OffsetLength { + public final int Offset; + public final int Length; + + public OffsetLength(int offset, int length) { + this.Offset = offset; + this.Length = length; + } + } +} diff --git a/src/main/java/com/bestvike/collections/generic/Comparer.java b/src/main/java/com/bestvike/collections/generic/Comparer.java index 32614731..257dd1e1 100644 --- a/src/main/java/com/bestvike/collections/generic/Comparer.java +++ b/src/main/java/com/bestvike/collections/generic/Comparer.java @@ -14,6 +14,8 @@ public final class Comparer implements Comparator { private static final Comparer DEFAULT = new Comparer<>(null); private static final Comparer DEFAULT_INVARIANT = new Comparer<>(CultureInfo.getInvariantCulture()); + private static final Comparator FLOAT = new FloatComparer(); + private static final Comparator DOUBLE = new DoubleComparer(); private final Collator collator; @@ -31,6 +33,14 @@ public static Comparator DefaultInvariant() { return (Comparator) DEFAULT_INVARIANT; } + public static Comparator Float() { + return FLOAT; + } + + public static Comparator Double() { + return DOUBLE; + } + public static Comparator create(Collator collator) { if (collator == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.collator); @@ -62,4 +72,64 @@ public int compare(T x, T y) { ThrowHelper.throwImplementComparableException(); return 0; } + + + private static final class FloatComparer implements Comparator { + @Override + public int compare(Float x, Float y) { + if (x != null) { + if (y != null) + return this.comparePrimitive(x, y); + return 1; + } + if (y != null) + return -1; + return 0; + } + + private int comparePrimitive(float x, float y) { + if (x < y) + return -1; + if (x > y) + return 1; + if (x == y) + return 0; + + // At least one of the values is NaN. + if (Float.isNaN(x)) + return Float.isNaN(y) ? 0 : -1; + else // y is NaN. + return 1; + } + } + + + private static final class DoubleComparer implements Comparator { + @Override + public int compare(Double x, Double y) { + if (x != null) { + if (y != null) + return this.comparePrimitive(x, y); + return 1; + } + if (y != null) + return -1; + return 0; + } + + private int comparePrimitive(double x, double y) { + if (x < y) + return -1; + if (x > y) + return 1; + if (x == y) + return 0; + + // At least one of the values is NaN. + if (Double.isNaN(x)) + return Double.isNaN(y) ? 0 : -1; + else // y is NaN. + return 1; + } + } } diff --git a/src/main/java/com/bestvike/collections/generic/Queue.java b/src/main/java/com/bestvike/collections/generic/Queue.java new file mode 100644 index 00000000..43769e84 --- /dev/null +++ b/src/main/java/com/bestvike/collections/generic/Queue.java @@ -0,0 +1,78 @@ +package com.bestvike.collections.generic; + +import com.bestvike.linq.exception.InvalidOperationException; +import com.bestvike.linq.resources.SR; +import com.bestvike.out; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by 许崇雷 on 2023-10-10. + */ +public final class Queue { + private final List list; + + public Queue() { + this.list = new ArrayList<>(); + } + + public Queue(int capacity) { + this.list = new ArrayList<>(capacity); + } + + public int size() { + return this.list.size(); + } + + public boolean isEmpty() { + return this.list.isEmpty(); + } + + public void clear() { + this.list.clear(); + } + + public void enqueue(T item) { + this.list.add(item); + } + + public T dequeue() { + if (this.list.isEmpty()) + this.throwForEmptyQueue(); + return this.list.remove(0); + } + + public boolean tryDequeue(out result) { + if (this.list.isEmpty()) { + result.value = null; + return false; + } + result.value = this.list.remove(0); + return true; + } + + public T peek() { + if (this.list.isEmpty()) + this.throwForEmptyQueue(); + return this.list.get(0); + } + + public boolean tryPeek(out result) { + if (this.list.isEmpty()) { + result.value = null; + return false; + } + result.value = this.list.get(0); + return true; + } + + public boolean contains(T item) { + return this.list.contains(item); + } + + private void throwForEmptyQueue() { + assert this.list.isEmpty(); + throw new InvalidOperationException(SR.InvalidOperation_EmptyQueue); + } +} diff --git a/src/main/java/com/bestvike/linq/IEnumerable.java b/src/main/java/com/bestvike/linq/IEnumerable.java index aad54ac7..2ee01a59 100644 --- a/src/main/java/com/bestvike/linq/IEnumerable.java +++ b/src/main/java/com/bestvike/linq/IEnumerable.java @@ -1,5 +1,7 @@ package com.bestvike.linq; +import com.bestvike.Index; +import com.bestvike.Range; import com.bestvike.collections.generic.Array; import com.bestvike.collections.generic.IEqualityComparer; import com.bestvike.function.Action2; @@ -23,6 +25,7 @@ import com.bestvike.linq.enumerable.AppendPrepend; import com.bestvike.linq.enumerable.Average; import com.bestvike.linq.enumerable.Cast; +import com.bestvike.linq.enumerable.Chunk; import com.bestvike.linq.enumerable.Concat; import com.bestvike.linq.enumerable.Contains; import com.bestvike.linq.enumerable.Count; @@ -237,6 +240,10 @@ default IEnumerable cast(Class clazz) { return Cast.cast(this, clazz); } + default IEnumerable chunk(int size, Class clazz) { + return Chunk.chunk(this, size, clazz); + } + default IEnumerable concat(IEnumerable second) { return Concat.concat(this, (IEnumerable) second); } @@ -289,10 +296,18 @@ default TSource elementAt(int index) { return ElementAt.elementAt(this, index); } + default TSource elementAt(Index index) { + return ElementAt.elementAt(this, index); + } + default TSource elementAtOrDefault(int index) { return ElementAt.elementAtOrDefault(this, index); } + default TSource elementAtOrDefault(Index index) { + return ElementAt.elementAtOrDefault(this, index); + } + default IEnumerable except(IEnumerable second) { return Except.except(this, (IEnumerable) second); } @@ -301,12 +316,12 @@ default IEnumerable except(IEnumerable second, IEqua return Except.except(this, (IEnumerable) second, (IEqualityComparer) comparer); } - default IEnumerable exceptBy(IEnumerable second, Func1 keySelector) { - return ExceptBy.exceptBy(this, (IEnumerable) second, (Func1) keySelector); + default IEnumerable exceptBy(IEnumerable second, Func1 keySelector) { + return ExceptBy.exceptBy(this, (IEnumerable) second, (Func1) keySelector); } - default IEnumerable exceptBy(IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { - return ExceptBy.exceptBy(this, (IEnumerable) second, (Func1) keySelector, (IEqualityComparer) comparer); + default IEnumerable exceptBy(IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { + return ExceptBy.exceptBy(this, (IEnumerable) second, (Func1) keySelector, (IEqualityComparer) comparer); } default int findIndex(Predicate1 predicate) { @@ -329,10 +344,18 @@ default TSource firstOrDefault() { return First.firstOrDefault(this); } + default TSource firstOrDefault(TSource defaultValue) { + return First.firstOrDefault(this, defaultValue); + } + default TSource firstOrDefault(Predicate1 predicate) { return First.firstOrDefault(this, (Predicate1) predicate); } + default TSource firstOrDefault(Predicate1 predicate, TSource defaultValue) { + return First.firstOrDefault(this, (Predicate1) predicate, defaultValue); + } + default String format() { return Format.format(this); } @@ -413,12 +436,12 @@ default IEnumerable intersect(IEnumerable second, IE return Intersect.intersect(this, (IEnumerable) second, (IEqualityComparer) comparer); } - default IEnumerable intersectBy(IEnumerable second, Func1 keySelector) { - return IntersectBy.intersectBy(this, (IEnumerable) second, (Func1) keySelector); + default IEnumerable intersectBy(IEnumerable second, Func1 keySelector) { + return IntersectBy.intersectBy(this, (IEnumerable) second, (Func1) keySelector); } - default IEnumerable intersectBy(IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { - return IntersectBy.intersectBy(this, (IEnumerable) second, (Func1) keySelector, (IEqualityComparer) comparer); + default IEnumerable intersectBy(IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { + return IntersectBy.intersectBy(this, (IEnumerable) second, (Func1) keySelector, (IEqualityComparer) comparer); } default IEnumerable join(IEnumerable inner, Func1 outerKeySelector, Func1 innerKeySelector, Func2 resultSelector) { @@ -461,10 +484,18 @@ default TSource lastOrDefault() { return Last.lastOrDefault(this); } + default TSource lastOrDefault(TSource defaultValue) { + return Last.lastOrDefault(this, defaultValue); + } + default TSource lastOrDefault(Predicate1 predicate) { return Last.lastOrDefault(this, (Predicate1) predicate); } + default TSource lastOrDefault(Predicate1 predicate, TSource defaultValue) { + return Last.lastOrDefault(this, (Predicate1) predicate, defaultValue); + } + default IEnumerable leftJoin(IEnumerable inner, Func1 outerKeySelector, Func1 innerKeySelector, Func2 resultSelector) { return Join.leftJoin(this, (IEnumerable) inner, (Func1) outerKeySelector, (Func1) innerKeySelector, (Func2) resultSelector); } @@ -533,10 +564,18 @@ default TSource max() { return Max.max(this); } + default TSource max(Comparator comparer) { + return Max.max(this, comparer); + } + default TSource maxNull() { return Max.maxNull(this); } + default TSource maxNull(Comparator comparer) { + return Max.maxNull(this, comparer); + } + default int maxInt(IntFunc1 selector) { return Max.maxInt(this, (IntFunc1) selector); } @@ -581,10 +620,18 @@ default TResult max(Func1 selector return Max.max(this, (Func1) selector); } + default TResult max(Func1 selector, Comparator comparer) { + return Max.max(this, (Func1) selector, comparer); + } + default TResult maxNull(Func1 selector) { return Max.maxNull(this, (Func1) selector); } + default TResult maxNull(Func1 selector, Comparator comparer) { + return Max.maxNull(this, (Func1) selector, comparer); + } + default TSource maxByInt(IntFunc1 keySelector) { return MaxBy.maxByInt(this, (IntFunc1) keySelector); } @@ -629,10 +676,18 @@ default TSource maxBy(Func1 keySelector) return MaxBy.maxBy(this, (Func1) keySelector); } + default TSource maxBy(Func1 keySelector, Comparator comparer) { + return MaxBy.maxBy(this, (Func1) keySelector, comparer); + } + default TSource maxByNull(Func1 keySelector) { return MaxBy.maxByNull(this, (Func1) keySelector); } + default TSource maxByNull(Func1 keySelector, Comparator comparer) { + return MaxBy.maxByNull(this, (Func1) keySelector, comparer); + } + default int minInt() { return Min.minInt((IEnumerable) this); } @@ -677,10 +732,18 @@ default TSource min() { return Min.min(this); } + default TSource min(Comparator comparer) { + return Min.min(this, comparer); + } + default TSource minNull() { return Min.minNull(this); } + default TSource minNull(Comparator comparer) { + return Min.minNull(this, comparer); + } + default int minInt(IntFunc1 selector) { return Min.minInt(this, (IntFunc1) selector); } @@ -725,10 +788,18 @@ default TResult min(Func1 selector return Min.min(this, (Func1) selector); } + default TResult min(Func1 selector, Comparator comparer) { + return Min.min(this, (Func1) selector, comparer); + } + default TResult minNull(Func1 selector) { return Min.minNull(this, (Func1) selector); } + default TResult minNull(Func1 selector, Comparator comparer) { + return Min.minNull(this, (Func1) selector, comparer); + } + default TSource minByInt(IntFunc1 keySelector) { return MinBy.minByInt(this, (IntFunc1) keySelector); } @@ -773,10 +844,18 @@ default TSource minBy(Func1 keySelector) return MinBy.minBy(this, (Func1) keySelector); } + default TSource minBy(Func1 keySelector, Comparator comparer) { + return MinBy.minBy(this, (Func1) keySelector, comparer); + } + default TSource minByNull(Func1 keySelector) { return MinBy.minByNull(this, (Func1) keySelector); } + default TSource minByNull(Func1 keySelector, Comparator comparer) { + return MinBy.minByNull(this, (Func1) keySelector, comparer); + } + default IEnumerable ofType(Class clazz) { return Cast.ofType(this, clazz); } @@ -877,10 +956,18 @@ default TSource singleOrDefault() { return Single.singleOrDefault(this); } + default TSource singleOrDefault(TSource defaultValue) { + return Single.singleOrDefault(this, defaultValue); + } + default TSource singleOrDefault(Predicate1 predicate) { return Single.singleOrDefault(this, (Predicate1) predicate); } + default TSource singleOrDefault(Predicate1 predicate, TSource defaultValue) { + return Single.singleOrDefault(this, (Predicate1) predicate, defaultValue); + } + default IEnumerable skip(int count) { return Skip.skip(this, count); } @@ -981,6 +1068,10 @@ default IEnumerable take(int count) { return Take.take(this, count); } + default IEnumerable take(Range range) { + return Take.take(this, range); + } + default IEnumerable takeLast(int count) { return Take.takeLast(this, count); } @@ -1026,6 +1117,10 @@ default Map toLinkedMap(Func1) keySelector, (Func1) elementSelector); } + default Map toLinkedMap(Func1 keySelector, Func1 elementSelector, Func2 resultElementSelector) { + return ToCollection.toLinkedMap(this, (Func1) keySelector, (Func1) elementSelector, (Func2< TElement, TElement, TElement>) resultElementSelector); + } + default Set toLinkedSet() { return ToCollection.toLinkedSet(this); } @@ -1058,6 +1153,10 @@ default Map toMap(Func1) keySelector, (Func1) elementSelector); } + default Map toMap(Func1 keySelector, Func1 elementSelector, Func2 resultElementSelector) { + return ToCollection.toMap(this, (Func1) keySelector, (Func1) elementSelector, (Func2< TElement, TElement, TElement>) resultElementSelector); + } + default Set toSet() { return ToCollection.toSet(this); } diff --git a/src/main/java/com/bestvike/linq/enumerable/Chunk.java b/src/main/java/com/bestvike/linq/enumerable/Chunk.java new file mode 100644 index 00000000..ddf60a3f --- /dev/null +++ b/src/main/java/com/bestvike/linq/enumerable/Chunk.java @@ -0,0 +1,85 @@ +package com.bestvike.linq.enumerable; + +import com.bestvike.linq.IEnumerable; +import com.bestvike.linq.IEnumerator; +import com.bestvike.linq.exception.ExceptionArgument; +import com.bestvike.linq.exception.ThrowHelper; +import com.bestvike.linq.util.ArrayUtils; + +import java.util.Arrays; + +/** + * Created by 许崇雷 on 2023-05-31. + */ +public final class Chunk { + private Chunk() { + } + + public static IEnumerable chunk(IEnumerable source, int size, Class clazz) { + if (source == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + if (size < 1) + ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.size); + if (clazz == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.clazz); + + return new ChunkIterator<>(source, size, clazz); + } +} + + +final class ChunkIterator extends AbstractIterator { + private final IEnumerable source; + private final int size; + private final Class clazz; + private IEnumerator enumerator; + + ChunkIterator(IEnumerable source, int size, Class clazz) { + this.source = source; + this.size = size; + this.clazz = clazz; + } + + @Override + public AbstractIterator clone() { + return new ChunkIterator<>(this.source, this.size, this.clazz); + } + + @Override + public boolean moveNext() { + switch (this.state) { + case 1: + this.enumerator = this.source.enumerator(); + this.state = 2; + case 2: + if (!this.enumerator.moveNext()) { + this.close(); + return false; + } + TSource[] chunk = ArrayUtils.newInstance(this.clazz, this.size); + chunk[0] = this.enumerator.current(); + int chunkSize = 1; + while (chunkSize < chunk.length && this.enumerator.moveNext()) { + chunk[chunkSize] = this.enumerator.current(); + chunkSize++; + } + if (chunkSize == chunk.length) { + this.current = chunk; + return true; + } + this.current = Arrays.copyOf(chunk, chunkSize); + return true; + default: + return false; + } + } + + @Override + public void close() { + if (this.enumerator != null) { + this.enumerator.close(); + this.enumerator = null; + } + super.close(); + } +} diff --git a/src/main/java/com/bestvike/linq/enumerable/Concat.java b/src/main/java/com/bestvike/linq/enumerable/Concat.java index 9d9b042c..aeddd42f 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Concat.java +++ b/src/main/java/com/bestvike/linq/enumerable/Concat.java @@ -149,13 +149,13 @@ public IEnumerable getEnumerable(int index) { public int _getCount(boolean onlyIfCheap) { out firstCountRef = out.init(); out secondCountRef = out.init(); - if (!EnumerableHelpers.tryGetCount(this.first, firstCountRef)) { + if (!Count.tryGetNonEnumeratedCount(this.first, firstCountRef)) { if (onlyIfCheap) return -1; firstCountRef.value = this.first.count(); } - if (!EnumerableHelpers.tryGetCount(this.second, secondCountRef)) { + if (!Count.tryGetNonEnumeratedCount(this.second, secondCountRef)) { if (onlyIfCheap) return -1; secondCountRef.value = this.second.count(); diff --git a/src/main/java/com/bestvike/linq/enumerable/Count.java b/src/main/java/com/bestvike/linq/enumerable/Count.java index a252a1f9..7ce4ba32 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Count.java +++ b/src/main/java/com/bestvike/linq/enumerable/Count.java @@ -6,6 +6,7 @@ import com.bestvike.linq.IEnumerator; import com.bestvike.linq.exception.ExceptionArgument; import com.bestvike.linq.exception.ThrowHelper; +import com.bestvike.out; /** * Created by 许崇雷 on 2018-04-27. @@ -83,4 +84,27 @@ public static long longCount(IEnumerable source, Predicate1 boolean tryGetNonEnumeratedCount(IEnumerable source, out count) { + if (source == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + + if (source instanceof ICollection) { + ICollection collection = (ICollection) source; + count.value = collection._getCount(); + return true; + } + + if (source instanceof IIListProvider) { + IIListProvider provider = (IIListProvider) source; + int c = provider._getCount(true); + if (c >= 0) { + count.value = c; + return true; + } + } + + count.value = 0; + return false; + } } diff --git a/src/main/java/com/bestvike/linq/enumerable/Distinct.java b/src/main/java/com/bestvike/linq/enumerable/Distinct.java index 767c7255..fa133d91 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Distinct.java +++ b/src/main/java/com/bestvike/linq/enumerable/Distinct.java @@ -106,8 +106,6 @@ public int _getCount(boolean onlyIfCheap) { } private Set fillSet() { - Set set = new Set<>(this.comparer); - set.unionWith(this.source); - return set; + return new Set<>(this.source, this.comparer); } } diff --git a/src/main/java/com/bestvike/linq/enumerable/DistinctBy.java b/src/main/java/com/bestvike/linq/enumerable/DistinctBy.java index d6a3a45c..e441f913 100644 --- a/src/main/java/com/bestvike/linq/enumerable/DistinctBy.java +++ b/src/main/java/com/bestvike/linq/enumerable/DistinctBy.java @@ -7,9 +7,6 @@ import com.bestvike.linq.exception.ExceptionArgument; import com.bestvike.linq.exception.ThrowHelper; -import java.util.ArrayList; -import java.util.List; - /** * Created by 许崇雷 on 2018-05-09. */ @@ -32,7 +29,7 @@ public static IEnumerable distinctBy(IEnumerable extends Iterator implements IIListProvider { +final class DistinctByIterator extends AbstractIterator { private final IEnumerable source; private final Func1 keySelector; private final IEqualityComparer comparer; @@ -46,12 +43,13 @@ final class DistinctByIterator extends Iterator implemen } @Override - public Iterator clone() { + public AbstractIterator clone() { return new DistinctByIterator<>(this.source, this.keySelector, this.comparer); } @Override public boolean moveNext() { + TSource element; switch (this.state) { case 1: this.enumerator = this.source.enumerator(); @@ -59,7 +57,7 @@ public boolean moveNext() { this.close(); return false; } - TSource element = this.enumerator.current(); + element = this.enumerator.current(); this.set = new Set<>(this.comparer); this.set.add(this.keySelector.apply(element)); this.current = element; @@ -89,60 +87,4 @@ public void close() { } super.close(); } - - @Override - public TSource[] _toArray(Class clazz) { - LargeArrayBuilder builder = new LargeArrayBuilder<>(); - Set set = new Set<>(this.comparer); - try (IEnumerator e = this.source.enumerator()) { - while (e.moveNext()) { - TSource element = e.current(); - if (set.add(this.keySelector.apply(element))) - builder.add(element); - } - } - - return builder.toArray(clazz); - } - - @Override - public Object[] _toArray() { - LargeArrayBuilder builder = new LargeArrayBuilder<>(); - Set set = new Set<>(this.comparer); - try (IEnumerator e = this.source.enumerator()) { - while (e.moveNext()) { - TSource element = e.current(); - if (set.add(this.keySelector.apply(element))) - builder.add(element); - } - } - - return builder.toArray(); - } - - @Override - public List _toList() { - List list = new ArrayList<>(); - Set set = new Set<>(this.comparer); - try (IEnumerator e = this.source.enumerator()) { - while (e.moveNext()) { - TSource element = e.current(); - if (set.add(this.keySelector.apply(element))) - list.add(element); - } - } - - return list; - } - - @Override - public int _getCount(boolean onlyIfCheap) { - return onlyIfCheap ? -1 : this.fillSet().getCount(); - } - - private Set fillSet() { - Set set = new Set<>(this.comparer); - set.unionWith(this.source, this.keySelector); - return set; - } } diff --git a/src/main/java/com/bestvike/linq/enumerable/ElementAt.java b/src/main/java/com/bestvike/linq/enumerable/ElementAt.java index 7d03dca2..72bad16f 100644 --- a/src/main/java/com/bestvike/linq/enumerable/ElementAt.java +++ b/src/main/java/com/bestvike/linq/enumerable/ElementAt.java @@ -1,6 +1,8 @@ package com.bestvike.linq.enumerable; +import com.bestvike.Index; import com.bestvike.collections.generic.IList; +import com.bestvike.collections.generic.Queue; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.IEnumerator; import com.bestvike.linq.exception.ExceptionArgument; @@ -24,26 +26,36 @@ public static TSource elementAt(IEnumerable source, int index TSource element = partition._tryGetElementAt(index, foundRef); if (foundRef.value) return element; + } else if (source instanceof IList) { + IList list = (IList) source; + return list.get(index); } else { - if (source instanceof IList) { - IList list = (IList) source; - return list.get(index); - } - - if (index >= 0) { - try (IEnumerator e = source.enumerator()) { - while (e.moveNext()) { - if (index == 0) - return e.current(); - index--; - } - } - } + out elementRef = out.init(); + if (tryGetElement(source, index, elementRef)) + return elementRef.value; } ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.index); return null; } + public static TSource elementAt(IEnumerable source, Index index) { + if (source == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + + if (!index.isFromEnd()) + return elementAt(source, index.getValue()); + + out countRef = out.init(); + if (Count.tryGetNonEnumeratedCount(source, countRef)) + return elementAt(source, countRef.value - index.getValue()); + + out elementRef = out.init(); + if (!tryGetElementFromEnd(source, index.getValue(), elementRef)) + ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.index); + + return elementRef.value; + } + public static TSource elementAtOrDefault(IEnumerable source, int index) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); @@ -52,23 +64,73 @@ public static TSource elementAtOrDefault(IEnumerable source, IPartition partition = (IPartition) source; out foundRef = out.init(); return partition._tryGetElementAt(index, foundRef); + } else if (source instanceof IList) { + IList list = (IList) source; + return index >= 0 && index < list._getCount() ? list.get(index) : null; } + out elementRef = out.init(); + tryGetElement(source, index, elementRef); + return elementRef.value; + } + + public static TSource elementAtOrDefault(IEnumerable source, Index index) { + if (source == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + + if (!index.isFromEnd()) + return elementAtOrDefault(source, index.getValue()); + + out countRef = out.init(); + if (Count.tryGetNonEnumeratedCount(source, countRef)) + return elementAtOrDefault(source, countRef.value - index.getValue()); + + out elementRef = out.init(); + tryGetElementFromEnd(source, index.getValue(), elementRef); + return elementRef.value; + } + + private static boolean tryGetElement(IEnumerable source, int index, out element) { + assert source != null; + if (index >= 0) { - if (source instanceof IList) { - IList list = (IList) source; - if (index < list._getCount()) - return list.get(index); - } else { - try (IEnumerator e = source.enumerator()) { + try (IEnumerator e = source.enumerator()) { + while (e.moveNext()) { + if (index == 0) { + element.value = e.current(); + return true; + } + index--; + } + } + } + element.value = null; + return false; + } + + private static boolean tryGetElementFromEnd(IEnumerable source, int indexFromEnd, out element) { + assert source != null; + if (indexFromEnd > 0) { + try (IEnumerator e = source.enumerator()) { + if (e.moveNext()) { + Queue queue = new Queue<>(); + queue.enqueue(e.current()); while (e.moveNext()) { - if (index == 0) - return e.current(); - index--; + if (queue.size() == indexFromEnd) { + queue.dequeue(); + } + queue.enqueue(e.current()); + } + + if (queue.size() == indexFromEnd) { + element.value = queue.dequeue(); + return true; } } } } - return null; + + element.value = null; + return false; } } diff --git a/src/main/java/com/bestvike/linq/enumerable/Except.java b/src/main/java/com/bestvike/linq/enumerable/Except.java index 7d2d71be..8535e013 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Except.java +++ b/src/main/java/com/bestvike/linq/enumerable/Except.java @@ -50,8 +50,7 @@ public AbstractIterator clone() { public boolean moveNext() { switch (this.state) { case 1: - this.set = new Set<>(this.comparer); - this.set.unionWith(this.second); + this.set = new Set<>(this.second, this.comparer); this.enumerator = this.first.enumerator(); this.state = 2; case 2: diff --git a/src/main/java/com/bestvike/linq/enumerable/ExceptBy.java b/src/main/java/com/bestvike/linq/enumerable/ExceptBy.java index 5c91e1c9..23b78419 100644 --- a/src/main/java/com/bestvike/linq/enumerable/ExceptBy.java +++ b/src/main/java/com/bestvike/linq/enumerable/ExceptBy.java @@ -14,11 +14,11 @@ public final class ExceptBy { private ExceptBy() { } - public static IEnumerable exceptBy(IEnumerable first, IEnumerable second, Func1 keySelector) { + public static IEnumerable exceptBy(IEnumerable first, IEnumerable second, Func1 keySelector) { return exceptBy(first, second, keySelector, null); } - public static IEnumerable exceptBy(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { + public static IEnumerable exceptBy(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { if (first == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.first); if (second == null) @@ -33,13 +33,13 @@ public static IEnumerable exceptBy(IEnumerable final class ExceptByIterator extends AbstractIterator { private final IEnumerable first; - private final IEnumerable second; + private final IEnumerable second; private final Func1 keySelector; private final IEqualityComparer comparer; private Set set; private IEnumerator enumerator; - ExceptByIterator(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { + ExceptByIterator(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { this.first = first; this.second = second; this.keySelector = keySelector; @@ -55,8 +55,7 @@ public AbstractIterator clone() { public boolean moveNext() { switch (this.state) { case 1: - this.set = new Set<>(this.comparer); - this.set.unionWith(this.second, this.keySelector); + this.set = new Set<>(this.second, this.comparer); this.enumerator = this.first.enumerator(); this.state = 2; case 2: diff --git a/src/main/java/com/bestvike/linq/enumerable/First.java b/src/main/java/com/bestvike/linq/enumerable/First.java index 58bdf2d1..91a28536 100644 --- a/src/main/java/com/bestvike/linq/enumerable/First.java +++ b/src/main/java/com/bestvike/linq/enumerable/First.java @@ -36,11 +36,23 @@ public static TSource firstOrDefault(IEnumerable source) { return tryGetFirst(source, foundRef); } + public static TSource firstOrDefault(IEnumerable source, TSource defaultValue) { + out foundRef = out.init(); + TSource first = tryGetFirst(source, foundRef); + return foundRef.value ? first : defaultValue; + } + public static TSource firstOrDefault(IEnumerable source, Predicate1 predicate) { out foundRef = out.init(); return tryGetFirst(source, predicate, foundRef); } + public static TSource firstOrDefault(IEnumerable source, Predicate1 predicate, TSource defaultValue) { + out foundRef = out.init(); + TSource first = tryGetFirst(source, predicate, foundRef); + return foundRef.value ? first : defaultValue; + } + private static TSource tryGetFirst(IEnumerable source, out found) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); diff --git a/src/main/java/com/bestvike/linq/enumerable/Intersect.java b/src/main/java/com/bestvike/linq/enumerable/Intersect.java index 924c75cc..20ffd9b9 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Intersect.java +++ b/src/main/java/com/bestvike/linq/enumerable/Intersect.java @@ -50,8 +50,7 @@ public AbstractIterator clone() { public boolean moveNext() { switch (this.state) { case 1: - this.set = new Set<>(this.comparer); - this.set.unionWith(this.second); + this.set = new Set<>(this.second, this.comparer); this.enumerator = this.first.enumerator(); this.state = 2; case 2: diff --git a/src/main/java/com/bestvike/linq/enumerable/IntersectBy.java b/src/main/java/com/bestvike/linq/enumerable/IntersectBy.java index 80dae6c6..4c43ca3f 100644 --- a/src/main/java/com/bestvike/linq/enumerable/IntersectBy.java +++ b/src/main/java/com/bestvike/linq/enumerable/IntersectBy.java @@ -14,11 +14,11 @@ public final class IntersectBy { private IntersectBy() { } - public static IEnumerable intersectBy(IEnumerable first, IEnumerable second, Func1 keySelector) { + public static IEnumerable intersectBy(IEnumerable first, IEnumerable second, Func1 keySelector) { return intersectBy(first, second, keySelector, null); } - public static IEnumerable intersectBy(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { + public static IEnumerable intersectBy(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { if (first == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.first); if (second == null) @@ -33,13 +33,13 @@ public static IEnumerable intersectBy(IEnumerable extends AbstractIterator { private final IEnumerable first; - private final IEnumerable second; + private final IEnumerable second; private final Func1 keySelector; private final IEqualityComparer comparer; private Set set; private IEnumerator enumerator; - IntersectByIterator(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { + IntersectByIterator(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { this.first = first; this.second = second; this.keySelector = keySelector; @@ -55,8 +55,7 @@ public AbstractIterator clone() { public boolean moveNext() { switch (this.state) { case 1: - this.set = new Set<>(this.comparer); - this.set.unionWith(this.second, this.keySelector); + this.set = new Set<>(this.second, this.comparer); this.enumerator = this.first.enumerator(); this.state = 2; case 2: diff --git a/src/main/java/com/bestvike/linq/enumerable/Last.java b/src/main/java/com/bestvike/linq/enumerable/Last.java index caf1e4c8..6673b660 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Last.java +++ b/src/main/java/com/bestvike/linq/enumerable/Last.java @@ -39,11 +39,23 @@ public static TSource lastOrDefault(IEnumerable source) { return tryGetLast(source, foundRef); } + public static TSource lastOrDefault(IEnumerable source, TSource defaultValue) { + out foundRef = out.init(); + TSource last = tryGetLast(source, foundRef); + return foundRef.value ? last : defaultValue; + } + public static TSource lastOrDefault(IEnumerable source, Predicate1 predicate) { out foundRef = out.init(); return tryGetLast(source, predicate, foundRef); } + public static TSource lastOrDefault(IEnumerable source, Predicate1 predicate, TSource defaultValue) { + out foundRef = out.init(); + TSource last = tryGetLast(source, predicate, foundRef); + return foundRef.value ? last : defaultValue; + } + private static TSource tryGetLast(IEnumerable source, out found) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); diff --git a/src/main/java/com/bestvike/linq/enumerable/Max.java b/src/main/java/com/bestvike/linq/enumerable/Max.java index e6bd90e6..628efc66 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Max.java +++ b/src/main/java/com/bestvike/linq/enumerable/Max.java @@ -273,10 +273,15 @@ public static BigDecimal maxDecimalNull(IEnumerable source) { } public static TSource max(IEnumerable source) { + return max(source, (Comparator) null); + } + + public static TSource max(IEnumerable source, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); TSource value; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) @@ -298,10 +303,15 @@ public static TSource max(IEnumerable source) { } public static TSource maxNull(IEnumerable source) { + return maxNull(source, (Comparator) null); + } + + public static TSource maxNull(IEnumerable source, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); TSource value = null; try (IEnumerator e = source.enumerator()) { do { @@ -587,12 +597,17 @@ public static BigDecimal maxDecimalNull(IEnumerable source, N } public static TResult max(IEnumerable source, Func1 selector) { + return max(source, selector, null); + } + + public static TResult max(IEnumerable source, Func1 selector, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (selector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.selector); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); TResult value; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) @@ -614,12 +629,17 @@ public static TResult max(IEnumerable source, Func1< } public static TResult maxNull(IEnumerable source, Func1 selector) { + return maxNull(source, selector, null); + } + + public static TResult maxNull(IEnumerable source, Func1 selector, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (selector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.selector); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); TResult value = null; try (IEnumerator e = source.enumerator()) { do { diff --git a/src/main/java/com/bestvike/linq/enumerable/MaxBy.java b/src/main/java/com/bestvike/linq/enumerable/MaxBy.java index 509fe7ad..2c3bda2f 100644 --- a/src/main/java/com/bestvike/linq/enumerable/MaxBy.java +++ b/src/main/java/com/bestvike/linq/enumerable/MaxBy.java @@ -33,14 +33,12 @@ public static TSource maxByInt(IEnumerable source, IntFunc1 e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); + TSource value = e.current(); + int key = keySelector.apply(value); while (e.moveNext()) { TSource curValue = e.current(); int curKey = keySelector.apply(curValue); @@ -49,9 +47,8 @@ public static TSource maxByInt(IEnumerable source, IntFunc1 TSource maxByIntNull(IEnumerable source, NullableIntFunc1 keySelector) { @@ -60,16 +57,18 @@ public static TSource maxByIntNull(IEnumerable source, Nullab if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - Integer key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + Integer key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); while (e.moveNext()) { TSource curValue = e.current(); @@ -79,9 +78,8 @@ public static TSource maxByIntNull(IEnumerable source, Nullab key = curKey; } } + return value; } - - return value; } public static TSource maxByLong(IEnumerable source, LongFunc1 keySelector) { @@ -90,14 +88,12 @@ public static TSource maxByLong(IEnumerable source, LongFunc1 if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - long key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); + TSource value = e.current(); + long key = keySelector.apply(value); while (e.moveNext()) { TSource curValue = e.current(); long curKey = keySelector.apply(curValue); @@ -106,9 +102,8 @@ public static TSource maxByLong(IEnumerable source, LongFunc1 key = curKey; } } + return value; } - - return value; } public static TSource maxByLongNull(IEnumerable source, NullableLongFunc1 keySelector) { @@ -117,16 +112,18 @@ public static TSource maxByLongNull(IEnumerable source, Nulla if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - Long key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + Long key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); while (e.moveNext()) { TSource curValue = e.current(); @@ -136,9 +133,8 @@ public static TSource maxByLongNull(IEnumerable source, Nulla key = curKey; } } + return value; } - - return value; } public static TSource maxByFloat(IEnumerable source, FloatFunc1 keySelector) { @@ -147,36 +143,23 @@ public static TSource maxByFloat(IEnumerable source, FloatFun if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - float key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); - while (Float.isNaN(key)) { - if (!e.moveNext()) - return value; - TSource curValue = e.current(); - float curKey = keySelector.apply(curValue); - if (!Float.isNaN(curKey)) { - value = curValue; - key = curKey; - } - } - + TSource value = e.current(); + float key = keySelector.apply(value); + Comparator comparer = Comparer.Float(); while (e.moveNext()) { TSource curValue = e.current(); float curKey = keySelector.apply(curValue); - if (curKey > key) { + if (comparer.compare(curKey, key) > 0) { value = curValue; key = curKey; } } + return value; } - - return value; } public static TSource maxByFloatNull(IEnumerable source, NullableFloatFunc1 keySelector) { @@ -185,39 +168,30 @@ public static TSource maxByFloatNull(IEnumerable source, Null if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - Float key; try (IEnumerator e = source.enumerator()) { - do { - if (!e.moveNext()) - return null; - value = e.current(); - key = keySelector.apply(value); - } - while (key == null); + if (!e.moveNext()) + return null; - while (Float.isNaN(key)) { + TSource value = e.current(); + Float key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) return value; - TSource curValue = e.current(); - Float curKey = keySelector.apply(curValue); - if (curKey != null && !Float.isNaN(curKey)) { - value = curValue; - key = curKey; - } + value = e.current(); + key = keySelector.apply(value); } + Comparator comparer = Comparer.Float(); while (e.moveNext()) { TSource curValue = e.current(); Float curKey = keySelector.apply(curValue); - if (curKey != null && curKey > key) { + if (curKey != null && comparer.compare(curKey, key) > 0) { value = curValue; key = curKey; } } + return value; } - - return value; } public static TSource maxByDouble(IEnumerable source, DoubleFunc1 keySelector) { @@ -226,36 +200,23 @@ public static TSource maxByDouble(IEnumerable source, DoubleF if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - double key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); - while (Double.isNaN(key)) { - if (!e.moveNext()) - return value; - TSource curValue = e.current(); - double curKey = keySelector.apply(curValue); - if (!Double.isNaN(curKey)) { - value = curValue; - key = curKey; - } - } - + TSource value = e.current(); + double key = keySelector.apply(value); + Comparator comparer = Comparer.Double(); while (e.moveNext()) { TSource curValue = e.current(); double curKey = keySelector.apply(curValue); - if (curKey > key) { + if (comparer.compare(curKey, key) > 0) { value = curValue; key = curKey; } } + return value; } - - return value; } public static TSource maxByDoubleNull(IEnumerable source, NullableDoubleFunc1 keySelector) { @@ -264,39 +225,30 @@ public static TSource maxByDoubleNull(IEnumerable source, Nul if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - Double key; try (IEnumerator e = source.enumerator()) { - do { - if (!e.moveNext()) - return null; - value = e.current(); - key = keySelector.apply(value); - } - while (key == null); + if (!e.moveNext()) + return null; - while (Double.isNaN(key)) { + TSource value = e.current(); + Double key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) return value; - TSource curValue = e.current(); - Double curKey = keySelector.apply(curValue); - if (curKey != null && !Double.isNaN(curKey)) { - value = curValue; - key = curKey; - } + value = e.current(); + key = keySelector.apply(value); } + Comparator comparer = Comparer.Double(); while (e.moveNext()) { TSource curValue = e.current(); Double curKey = keySelector.apply(curValue); - if (curKey != null && curKey > key) { + if (curKey != null && comparer.compare(curKey, key) > 0) { value = curValue; key = curKey; } } + return value; } - - return value; } public static TSource maxByDecimal(IEnumerable source, DecimalFunc1 keySelector) { @@ -305,27 +257,26 @@ public static TSource maxByDecimal(IEnumerable source, Decima if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - BigDecimal key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); + TSource value = e.current(); + BigDecimal key = keySelector.apply(value); if (key == null) ThrowHelper.throwNullPointerException(); while (e.moveNext()) { TSource curValue = e.current(); BigDecimal curKey = keySelector.apply(curValue); + if (curKey == null) + ThrowHelper.throwNullPointerException(); if (curKey.compareTo(key) > 0) { value = curValue; key = curKey; } } + return value; } - - return value; } public static TSource maxByDecimalNull(IEnumerable source, NullableDecimalFunc1 keySelector) { @@ -334,16 +285,18 @@ public static TSource maxByDecimalNull(IEnumerable source, Nu if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - BigDecimal key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + BigDecimal key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); while (e.moveNext()) { TSource curValue = e.current(); @@ -353,26 +306,28 @@ public static TSource maxByDecimalNull(IEnumerable source, Nu key = curKey; } } + return value; } - - return value; } public static TSource maxBy(IEnumerable source, Func1 keySelector) { + return maxBy(source, keySelector, null); + } + + public static TSource maxBy(IEnumerable source, Func1 keySelector, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); - TSource value; - TKey key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); + TSource value = e.current(); + TKey key = keySelector.apply(value); if (key == null) ThrowHelper.throwNullPointerException(); while (e.moveNext()) { @@ -385,28 +340,34 @@ public static TSource maxBy(IEnumerable source, Func1 TSource maxByNull(IEnumerable source, Func1 keySelector) { + return maxByNull(source, keySelector, null); + } + + public static TSource maxByNull(IEnumerable source, Func1 keySelector, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); - TSource value; - TKey key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + TKey key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); while (e.moveNext()) { TSource curValue = e.current(); @@ -416,8 +377,7 @@ public static TSource maxByNull(IEnumerable source, Fun key = curKey; } } + return value; } - - return value; } } diff --git a/src/main/java/com/bestvike/linq/enumerable/Min.java b/src/main/java/com/bestvike/linq/enumerable/Min.java index 50772b67..5a45a7b1 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Min.java +++ b/src/main/java/com/bestvike/linq/enumerable/Min.java @@ -269,10 +269,15 @@ public static BigDecimal minDecimalNull(IEnumerable source) { } public static TSource min(IEnumerable source) { + return min(source, (Comparator) null); + } + + public static TSource min(IEnumerable source, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); TSource value; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) @@ -294,10 +299,15 @@ public static TSource min(IEnumerable source) { } public static TSource minNull(IEnumerable source) { + return minNull(source, (Comparator) null); + } + + public static TSource minNull(IEnumerable source, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); TSource value = null; try (IEnumerator e = source.enumerator()) { do { @@ -579,12 +589,17 @@ public static BigDecimal minDecimalNull(IEnumerable source, N } public static TResult min(IEnumerable source, Func1 selector) { + return min(source, selector, null); + } + + public static TResult min(IEnumerable source, Func1 selector, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (selector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.selector); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); TResult value; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) @@ -605,12 +620,17 @@ public static TResult min(IEnumerable source, Func1< } public static TResult minNull(IEnumerable source, Func1 selector) { + return minNull(source, selector, null); + } + + public static TResult minNull(IEnumerable source, Func1 selector, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (selector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.selector); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); TResult value = null; try (IEnumerator e = source.enumerator()) { do { diff --git a/src/main/java/com/bestvike/linq/enumerable/MinBy.java b/src/main/java/com/bestvike/linq/enumerable/MinBy.java index db79addd..e6463cd1 100644 --- a/src/main/java/com/bestvike/linq/enumerable/MinBy.java +++ b/src/main/java/com/bestvike/linq/enumerable/MinBy.java @@ -33,14 +33,12 @@ public static TSource minByInt(IEnumerable source, IntFunc1 e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); + TSource value = e.current(); + int key = keySelector.apply(value); while (e.moveNext()) { TSource curValue = e.current(); int curKey = keySelector.apply(curValue); @@ -49,9 +47,8 @@ public static TSource minByInt(IEnumerable source, IntFunc1 TSource minByIntNull(IEnumerable source, NullableIntFunc1 keySelector) { @@ -60,16 +57,18 @@ public static TSource minByIntNull(IEnumerable source, Nullab if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - Integer key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + Integer key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); while (e.moveNext()) { TSource curValue = e.current(); @@ -79,9 +78,8 @@ public static TSource minByIntNull(IEnumerable source, Nullab key = curKey; } } + return value; } - - return value; } public static TSource minByLong(IEnumerable source, LongFunc1 keySelector) { @@ -90,14 +88,12 @@ public static TSource minByLong(IEnumerable source, LongFunc1 if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - long key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); + TSource value = e.current(); + long key = keySelector.apply(value); while (e.moveNext()) { TSource curValue = e.current(); long curKey = keySelector.apply(curValue); @@ -106,9 +102,8 @@ public static TSource minByLong(IEnumerable source, LongFunc1 key = curKey; } } + return value; } - - return value; } public static TSource minByLongNull(IEnumerable source, NullableLongFunc1 keySelector) { @@ -117,16 +112,18 @@ public static TSource minByLongNull(IEnumerable source, Nulla if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - Long key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + Long key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); while (e.moveNext()) { TSource curValue = e.current(); @@ -136,9 +133,8 @@ public static TSource minByLongNull(IEnumerable source, Nulla key = curKey; } } + return value; } - - return value; } public static TSource minByFloat(IEnumerable source, FloatFunc1 keySelector) { @@ -147,30 +143,23 @@ public static TSource minByFloat(IEnumerable source, FloatFun if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - float key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); - if (Float.isNaN(key)) - return value; - + TSource value = e.current(); + float key = keySelector.apply(value); + Comparator comparer = Comparer.Float(); while (e.moveNext()) { TSource curValue = e.current(); float curKey = keySelector.apply(curValue); - if (curKey < key) { + if (comparer.compare(curKey, key) < 0) { value = curValue; key = curKey; - } else if (Float.isNaN(curKey)) { - return curValue; } } + return value; } - - return value; } public static TSource minByFloatNull(IEnumerable source, NullableFloatFunc1 keySelector) { @@ -179,35 +168,30 @@ public static TSource minByFloatNull(IEnumerable source, Null if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - Float key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + Float key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); - - if (Float.isNaN(key)) - return value; + Comparator comparer = Comparer.Float(); while (e.moveNext()) { TSource curValue = e.current(); Float curKey = keySelector.apply(curValue); - if (curKey != null) { - if (curKey < key) { - value = curValue; - key = curKey; - } else if (Float.isNaN(curKey)) { - return curValue; - } + if (curKey != null && comparer.compare(curKey, key) < 0) { + value = curValue; + key = curKey; } } + return value; } - - return value; } public static TSource minByDouble(IEnumerable source, DoubleFunc1 keySelector) { @@ -216,30 +200,23 @@ public static TSource minByDouble(IEnumerable source, DoubleF if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - double key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); - if (Double.isNaN(key)) - return value; - + TSource value = e.current(); + double key = keySelector.apply(value); + Comparator comparer = Comparer.Double(); while (e.moveNext()) { TSource curValue = e.current(); double curKey = keySelector.apply(curValue); - if (curKey < key) { + if (comparer.compare(curKey, key) < 0) { value = curValue; key = curKey; - } else if (Double.isNaN(curKey)) { - return curValue; } } + return value; } - - return value; } public static TSource minByDoubleNull(IEnumerable source, NullableDoubleFunc1 keySelector) { @@ -248,35 +225,30 @@ public static TSource minByDoubleNull(IEnumerable source, Nul if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - Double key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + Double key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); - - if (Double.isNaN(key)) - return value; + Comparator comparer = Comparer.Double(); while (e.moveNext()) { TSource curValue = e.current(); Double curKey = keySelector.apply(curValue); - if (curKey != null) { - if (curKey < key) { - value = curValue; - key = curKey; - } else if (Double.isNaN(curKey)) { - return curValue; - } + if (curKey != null && comparer.compare(curKey, key) < 0) { + value = curValue; + key = curKey; } } + return value; } - - return value; } public static TSource minByDecimal(IEnumerable source, DecimalFunc1 keySelector) { @@ -285,27 +257,26 @@ public static TSource minByDecimal(IEnumerable source, Decima if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - BigDecimal key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); + TSource value = e.current(); + BigDecimal key = keySelector.apply(value); if (key == null) ThrowHelper.throwNullPointerException(); while (e.moveNext()) { TSource curValue = e.current(); BigDecimal curKey = keySelector.apply(curValue); + if (curKey == null) + ThrowHelper.throwNullPointerException(); if (curKey.compareTo(key) < 0) { value = curValue; key = curKey; } } + return value; } - - return value; } public static TSource minByDecimalNull(IEnumerable source, NullableDecimalFunc1 keySelector) { @@ -314,16 +285,18 @@ public static TSource minByDecimalNull(IEnumerable source, Nu if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - TSource value; - BigDecimal key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + BigDecimal key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); while (e.moveNext()) { TSource curValue = e.current(); @@ -333,26 +306,28 @@ public static TSource minByDecimalNull(IEnumerable source, Nu key = curKey; } } + return value; } - - return value; } public static TSource minBy(IEnumerable source, Func1 keySelector) { + return minBy(source, keySelector, null); + } + + public static TSource minBy(IEnumerable source, Func1 keySelector, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); - TSource value; - TKey key; try (IEnumerator e = source.enumerator()) { if (!e.moveNext()) ThrowHelper.throwNoElementsException(); - value = e.current(); - key = keySelector.apply(value); + TSource value = e.current(); + TKey key = keySelector.apply(value); if (key == null) ThrowHelper.throwNullPointerException(); while (e.moveNext()) { @@ -365,28 +340,34 @@ public static TSource minBy(IEnumerable source, Func1 TSource minByNull(IEnumerable source, Func1 keySelector) { + return minByNull(source, keySelector, null); + } + + public static TSource minByNull(IEnumerable source, Func1 keySelector, Comparator comparer) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); + if (comparer == null) + comparer = Comparer.Default(); - Comparator comparer = Comparer.Default(); - TSource value; - TKey key; try (IEnumerator e = source.enumerator()) { - do { + if (!e.moveNext()) + return null; + + TSource value = e.current(); + TKey key = keySelector.apply(value); + while (key == null) { if (!e.moveNext()) - return null; + return value; value = e.current(); key = keySelector.apply(value); } - while (key == null); while (e.moveNext()) { TSource curValue = e.current(); @@ -396,8 +377,7 @@ public static TSource minByNull(IEnumerable source, Fun key = curKey; } } + return value; } - - return value; } } diff --git a/src/main/java/com/bestvike/linq/enumerable/Single.java b/src/main/java/com/bestvike/linq/enumerable/Single.java index 68cd5b73..aff3d839 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Single.java +++ b/src/main/java/com/bestvike/linq/enumerable/Single.java @@ -6,6 +6,7 @@ import com.bestvike.linq.IEnumerator; import com.bestvike.linq.exception.ExceptionArgument; import com.bestvike.linq.exception.ThrowHelper; +import com.bestvike.out; /** * Created by 许崇雷 on 2018-05-04. @@ -15,56 +16,44 @@ private Single() { } public static TSource single(IEnumerable source) { - if (source == null) - ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + out foundRef = out.init(); + TSource single = tryGetSingle(source, foundRef); + if (!foundRef.value) + ThrowHelper.throwNoElementsException(); + return single; + } - if (source instanceof IList) { - IList list = (IList) source; - switch (list._getCount()) { - case 0: - ThrowHelper.throwNoElementsException(); - return null; - case 1: - return list.get(0); - } - } else { - try (IEnumerator e = source.enumerator()) { - if (!e.moveNext()) - ThrowHelper.throwNoElementsException(); - TSource result = e.current(); - if (!e.moveNext()) - return result; - } - } + public static TSource single(IEnumerable source, Predicate1 predicate) { + out foundRef = out.init(); + TSource single = tryGetSingle(source, predicate, foundRef); + if (!foundRef.value) + ThrowHelper.throwNoElementsException(); + return single; + } - ThrowHelper.throwMoreThanOneElementException(); - return null; + public static TSource singleOrDefault(IEnumerable source) { + out foundRef = out.init(); + return tryGetSingle(source, foundRef); } - public static TSource single(IEnumerable source, Predicate1 predicate) { - if (source == null) - ThrowHelper.throwArgumentNullException(ExceptionArgument.source); - if (predicate == null) - ThrowHelper.throwArgumentNullException(ExceptionArgument.predicate); + public static TSource singleOrDefault(IEnumerable source, TSource defaultValue) { + out foundRef = out.init(); + TSource single = tryGetSingle(source, foundRef); + return foundRef.value ? single : defaultValue; + } - try (IEnumerator e = source.enumerator()) { - while (e.moveNext()) { - TSource result = e.current(); - if (predicate.apply(result)) { - while (e.moveNext()) { - if (predicate.apply(e.current())) - ThrowHelper.throwMoreThanOneMatchException(); - } - return result; - } - } - } + public static TSource singleOrDefault(IEnumerable source, Predicate1 predicate) { + out foundRef = out.init(); + return tryGetSingle(source, predicate, foundRef); + } - ThrowHelper.throwNoMatchException(); - return null; + public static TSource singleOrDefault(IEnumerable source, Predicate1 predicate, TSource defaultValue) { + out foundRef = out.init(); + TSource single = tryGetSingle(source, predicate, foundRef); + return foundRef.value ? single : defaultValue; } - public static TSource singleOrDefault(IEnumerable source) { + private static TSource tryGetSingle(IEnumerable source, out found) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); @@ -72,25 +61,32 @@ public static TSource singleOrDefault(IEnumerable source) { IList list = (IList) source; switch (list._getCount()) { case 0: + found.value = false; return null; case 1: + found.value = true; return list.get(0); } } else { try (IEnumerator e = source.enumerator()) { - if (!e.moveNext()) + if (!e.moveNext()) { + found.value = false; return null; + } TSource result = e.current(); - if (!e.moveNext()) + if (!e.moveNext()) { + found.value = true; return result; + } } } + found.value = false; ThrowHelper.throwMoreThanOneElementException(); return null; } - public static TSource singleOrDefault(IEnumerable source, Predicate1 predicate) { + private static TSource tryGetSingle(IEnumerable source, Predicate1 predicate, out found) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); if (predicate == null) @@ -104,11 +100,13 @@ public static TSource singleOrDefault(IEnumerable source, Pre if (predicate.apply(e.current())) ThrowHelper.throwMoreThanOneMatchException(); } + found.value = true; return result; } } } + found.value = false; return null; } } diff --git a/src/main/java/com/bestvike/linq/enumerable/Skip.java b/src/main/java/com/bestvike/linq/enumerable/Skip.java index b796a534..c70bd145 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Skip.java +++ b/src/main/java/com/bestvike/linq/enumerable/Skip.java @@ -9,9 +9,6 @@ import com.bestvike.linq.exception.ExceptionArgument; import com.bestvike.linq.exception.ThrowHelper; -import java.util.ArrayDeque; -import java.util.Queue; - /** * Created by 许崇雷 on 2018-05-04. */ @@ -32,17 +29,7 @@ public static IEnumerable skip(IEnumerable source, i return partition._skip(count); } - if (source instanceof IList) { - if (source instanceof IArrayList) { - IArrayList sourceList = (IArrayList) source; - return new ListPartition<>(sourceList, count, Integer.MAX_VALUE); - } - - IList sourceList = (IList) source; - return new IListPartition<>(sourceList, count, Integer.MAX_VALUE); - } - - return new EnumerablePartition<>(source, count, -1); + return skipIterator(source, count); } public static IEnumerable skipWhile(IEnumerable source, Predicate1 predicate) { @@ -67,31 +54,26 @@ public static IEnumerable skipLast(IEnumerable sourc if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); - if (count <= 0) - return source.skip(0); + return count <= 0 + ? source.skip(0) + : new TakeRangeFromEndIterator<>(source, false, 0, true, count); + } + + private static IEnumerable skipIterator(IEnumerable source, int count) { + assert source != null; + assert count >= 0; - if (source instanceof IPartition) { - IPartition partition = (IPartition) source; - int length = partition._getCount(true); - if (length >= 0) - return length - count > 0 ? partition.take(length - count) : EmptyPartition.instance(); - } else if (source instanceof IList) { + if (source instanceof IList) { if (source instanceof IArrayList) { IArrayList sourceList = (IArrayList) source; - int sourceCount = sourceList._getCount(); - return sourceCount > count - ? new ListPartition<>(sourceList, 0, sourceCount - count - 1) - : EmptyPartition.instance(); + return new ListPartition<>(sourceList, count, Integer.MAX_VALUE); } IList sourceList = (IList) source; - int sourceCount = sourceList._getCount(); - return sourceCount > count - ? new IListPartition<>(sourceList, 0, sourceCount - count - 1) - : EmptyPartition.instance(); + return new IListPartition<>(sourceList, count, Integer.MAX_VALUE); } - return new SkipLastIterator<>(source, count); + return new EnumerablePartition<>(source, count, -1); } } @@ -206,62 +188,3 @@ public void close() { super.close(); } } - - -final class SkipLastIterator extends AbstractIterator { - private final IEnumerable source; - private final int count; - private IEnumerator enumerator; - private Queue queue; - - SkipLastIterator(IEnumerable source, int count) { - assert source != null; - assert count > 0; - this.source = source; - this.count = count; - } - - @Override - public AbstractIterator clone() { - return new SkipLastIterator<>(this.source, this.count); - } - - @Override - public boolean moveNext() { - switch (this.state) { - case 1: - this.queue = new ArrayDeque<>(); - this.enumerator = this.source.enumerator(); - while (this.enumerator.moveNext()) { - if (this.queue.size() == this.count) { - this.current = this.queue.remove(); - this.state = 2; - return true; - } - this.queue.add(this.enumerator.current()); - } - this.close(); - return false; - case 2: - this.queue.add(this.enumerator.current()); - if (this.enumerator.moveNext()) { - this.current = this.queue.remove(); - return true; - } - this.close(); - return false; - default: - return false; - } - } - - @Override - public void close() { - if (this.enumerator != null) { - this.enumerator.close(); - this.enumerator = null; - this.queue = null; - } - super.close(); - } -} diff --git a/src/main/java/com/bestvike/linq/enumerable/Take.java b/src/main/java/com/bestvike/linq/enumerable/Take.java index 30da5f86..c82f2a6c 100644 --- a/src/main/java/com/bestvike/linq/enumerable/Take.java +++ b/src/main/java/com/bestvike/linq/enumerable/Take.java @@ -1,16 +1,17 @@ package com.bestvike.linq.enumerable; +import com.bestvike.Index; +import com.bestvike.Range; import com.bestvike.collections.generic.IArrayList; import com.bestvike.collections.generic.IList; +import com.bestvike.collections.generic.Queue; import com.bestvike.function.IndexPredicate2; import com.bestvike.function.Predicate1; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.IEnumerator; import com.bestvike.linq.exception.ExceptionArgument; import com.bestvike.linq.exception.ThrowHelper; - -import java.util.ArrayDeque; -import java.util.Queue; +import com.bestvike.out; /** * Created by 许崇雷 on 2018-05-08. @@ -23,25 +24,37 @@ public static IEnumerable take(IEnumerable source, i if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); - if (count <= 0) - return EmptyPartition.instance(); - - if (source instanceof IPartition) { - IPartition partition = (IPartition) source; - return partition._take(count); - } + return count <= 0 + ? EmptyPartition.instance() + : takeIterator(source, count); + } - if (source instanceof IList) { - if (source instanceof IArrayList) { - IArrayList sourceList = (IArrayList) source; - return new ListPartition<>(sourceList, 0, count - 1); + public static IEnumerable take(IEnumerable source, Range range) { + if (source == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + if (range == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.range); + + Index start = range.getStart(); + Index end = range.getEnd(); + boolean isStartIndexFromEnd = start.isFromEnd(); + boolean isEndIndexFromEnd = end.isFromEnd(); + int startIndex = start.getValue(); + int endIndex = end.getValue(); + assert startIndex >= 0; + assert endIndex >= 0; + + if (isStartIndexFromEnd) { + if (startIndex == 0 || (isEndIndexFromEnd && endIndex >= startIndex)) { + return EmptyPartition.instance(); } - - IList sourceList = (IList) source; - return new IListPartition<>(sourceList, 0, count - 1); + } else if (!isEndIndexFromEnd) { + return startIndex >= endIndex + ? EmptyPartition.instance() + : takeRangeIterator(source, startIndex, endIndex); } - return new EnumerablePartition<>(source, 0, count - 1); + return new TakeRangeFromEndIterator<>(source, isStartIndexFromEnd, startIndex, isEndIndexFromEnd, endIndex); } public static IEnumerable takeWhile(IEnumerable source, Predicate1 predicate) { @@ -66,31 +79,58 @@ public static IEnumerable takeLast(IEnumerable sourc if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); - if (count <= 0) - return EmptyPartition.instance(); + return count <= 0 + ? EmptyPartition.instance() + : new TakeRangeFromEndIterator<>(source, true, count, true, 0); + } + + static IEnumerable takeIterator(IEnumerable source, int count) { + assert source != null; + assert count > 0; if (source instanceof IPartition) { IPartition partition = (IPartition) source; - int length = partition._getCount(true); - if (length >= 0) - return length - count > 0 ? partition.skip(length - count) : partition; - } else if (source instanceof IList) { + return partition._take(count); + } + + if (source instanceof IList) { + if (source instanceof IArrayList) { + IArrayList sourceList = (IArrayList) source; + return new ListPartition<>(sourceList, 0, count - 1); + } + + IList sourceList = (IList) source; + return new IListPartition<>(sourceList, 0, count - 1); + } + + return new EnumerablePartition<>(source, 0, count - 1); + } + + static IEnumerable takeRangeIterator(IEnumerable source, int startIndex, int endIndex) { + assert source != null; + assert startIndex >= 0 && startIndex < endIndex; + + if (source instanceof IPartition) { + IPartition partition = (IPartition) source; + return takePartitionRange(partition, startIndex, endIndex); + } + + if (source instanceof IList) { if (source instanceof IArrayList) { IArrayList sourceList = (IArrayList) source; - int sourceCount = sourceList._getCount(); - return sourceCount > count - ? new ListPartition<>(sourceList, sourceCount - count, sourceCount) - : new ListPartition<>(sourceList, 0, sourceCount); + return new ListPartition<>(sourceList, startIndex, endIndex - 1); } IList sourceList = (IList) source; - int sourceCount = sourceList._getCount(); - return sourceCount > count - ? new IListPartition<>(sourceList, sourceCount - count, sourceCount) - : new IListPartition<>(sourceList, 0, sourceCount); + return new IListPartition<>(sourceList, startIndex, endIndex - 1); } - return new TakeLastIterator<>(source, count); + return new EnumerablePartition<>(source, startIndex, endIndex - 1); + } + + static IPartition takePartitionRange(IPartition partition, int startIndex, int endIndex) { + partition = endIndex == 0 ? EmptyPartition.instance() : partition._take(endIndex); + return startIndex == 0 ? partition : partition._skip(startIndex); } } @@ -192,65 +232,162 @@ public void close() { } -final class TakeLastIterator extends AbstractIterator { +final class TakeRangeFromEndIterator extends AbstractIterator { private final IEnumerable source; - private final int count; + private final boolean isStartIndexFromEnd; + private final int startIndex; + private final boolean isEndIndexFromEnd; + private final int endIndex; + private IEnumerator enumerator; private Queue queue; + private int startIndexCalculated; + private int endIndexCalculated; - TakeLastIterator(IEnumerable source, int count) { - assert source != null; - assert count > 0; + TakeRangeFromEndIterator(IEnumerable source, boolean isStartIndexFromEnd, int startIndex, boolean isEndIndexFromEnd, int endIndex) { this.source = source; - this.count = count; + this.isStartIndexFromEnd = isStartIndexFromEnd; + this.startIndex = startIndex; + this.isEndIndexFromEnd = isEndIndexFromEnd; + this.endIndex = endIndex; + } + + private static int calculateStartIndex(boolean isStartIndexFromEnd, int startIndex, int count) { + return Math.max(0, isStartIndexFromEnd ? count - startIndex : startIndex); + } + + private static int calculateEndIndex(boolean isEndIndexFromEnd, int endIndex, int count) { + return Math.min(count, isEndIndexFromEnd ? count - endIndex : endIndex); } @Override public AbstractIterator clone() { - return new TakeLastIterator<>(this.source, this.count); + return new TakeRangeFromEndIterator<>(this.source, this.isStartIndexFromEnd, this.startIndex, this.isEndIndexFromEnd, this.endIndex); } @Override public boolean moveNext() { - switch (this.state) { - case 1: - try (IEnumerator e = this.source.enumerator()) { - if (!e.moveNext()) { + do { + switch (this.state) { + case 1: + assert this.source != null; + assert this.isStartIndexFromEnd || this.isEndIndexFromEnd; + assert this.isStartIndexFromEnd + ? this.startIndex > 0 && (!this.isEndIndexFromEnd || this.startIndex > this.endIndex) + : this.startIndex >= 0 && (this.isEndIndexFromEnd || this.startIndex < this.endIndex); + // Attempt to extract the count of the source enumerator, + // in order to convert fromEnd indices to regular indices. + // Enumerable counts can change over time, so it is very + // important that this check happens at enumeration time; + // do not move it outside of the iterator method. + out countRef = out.init(); + if (Count.tryGetNonEnumeratedCount(this.source, countRef)) { + int startIndexCalculated = calculateStartIndex(this.isStartIndexFromEnd, this.startIndex, countRef.value); + int endIndexCalculated = calculateEndIndex(this.isEndIndexFromEnd, this.endIndex, countRef.value); + if (startIndexCalculated < endIndexCalculated) { + this.enumerator = Take.takeRangeIterator(this.source, startIndexCalculated, endIndexCalculated).enumerator(); + this.state = 2; + break; + } this.close(); return false; } - this.queue = new ArrayDeque<>(); - this.queue.add(e.current()); - while (e.moveNext()) { - if (this.queue.size() < this.count) { - this.queue.add(e.current()); - continue; + // start index is from end + if (this.isStartIndexFromEnd) { + int count; + try (IEnumerator e = this.source.enumerator()) { + if (!e.moveNext()) { + this.close(); + return false; + } + this.queue = new Queue<>(); + this.queue.enqueue(e.current()); + count = 1; + while (e.moveNext()) { + if (count < this.startIndex) { + this.queue.enqueue(e.current()); + ++count; + } else { + do { + this.queue.dequeue(); + this.queue.enqueue(e.current()); + count = Math.addExact(count, 1); + } while (e.moveNext()); + break; + } + } + assert this.queue.size() == Math.min(count, this.startIndex); } - do { - this.queue.remove(); - this.queue.add(e.current()); - } while (e.moveNext()); + this.startIndexCalculated = calculateStartIndex(true, this.startIndex, count); + this.endIndexCalculated = calculateEndIndex(this.isEndIndexFromEnd, this.endIndex, count); + assert this.endIndexCalculated - this.startIndexCalculated <= this.queue.size(); + this.state = 3; break; } - } - assert this.queue.size() <= this.count; - this.current = this.queue.remove(); - this.state = 2; - return true; - case 2: - if (this.queue.size() > 0) { - this.current = this.queue.remove(); - return true; - } - this.close(); - return false; - default: - return false; - } + // start index not from end + assert !this.isStartIndexFromEnd && this.isEndIndexFromEnd; + this.enumerator = this.source.enumerator(); + int count = 0; + while (count < this.startIndex && this.enumerator.moveNext()) { + count++; + } + if (count != this.startIndex) { + this.close(); + return false; + } + this.queue = new Queue<>(); + while (this.enumerator.moveNext()) { + if (this.queue.size() != this.endIndex) { + this.queue.enqueue(this.enumerator.current()); + continue; + } + this.queue.enqueue(this.enumerator.current()); + this.current = this.queue.dequeue(); + this.state = 4; + return true; + } + this.close(); + return false; + case 2: + if (this.enumerator.moveNext()) { + this.current = this.enumerator.current(); + return true; + } + this.close(); + return false; + case 3: + if (this.startIndexCalculated < this.endIndexCalculated) { + this.startIndexCalculated++; + this.current = this.queue.dequeue(); + return true; + } + this.close(); + return false; + case 4: + if (this.enumerator.moveNext()) { + this.queue.enqueue(this.enumerator.current()); + this.current = this.queue.dequeue(); + return true; + } + this.close(); + return false; + default: + return false; + } + } while (true); } @Override public void close() { - this.queue = null; + if (this.enumerator != null) { + this.enumerator.close(); + this.enumerator = null; + } + if (this.queue != null) { + this.queue.clear(); + this.queue = null; + } + this.startIndexCalculated = 0; + this.endIndexCalculated = 0; super.close(); } } diff --git a/src/main/java/com/bestvike/linq/enumerable/ToCollection.java b/src/main/java/com/bestvike/linq/enumerable/ToCollection.java index 5a9d845b..0bb18d6b 100644 --- a/src/main/java/com/bestvike/linq/enumerable/ToCollection.java +++ b/src/main/java/com/bestvike/linq/enumerable/ToCollection.java @@ -4,6 +4,7 @@ import com.bestvike.collections.generic.ICollection; import com.bestvike.function.Action2; import com.bestvike.function.Func1; +import com.bestvike.function.Func2; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.IEnumerator; import com.bestvike.linq.exception.ExceptionArgument; @@ -183,6 +184,52 @@ public static Map toMap(IEnumerable Map toMap(IEnumerable source, Func1 keySelector, Func1 elementSelector, Func2 resultElementSelector) { + if (source == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + if (keySelector == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); + if (elementSelector == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.elementSelector); + if (resultElementSelector == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.resultElementSelector); + + if (source instanceof ICollection) { + ICollection collection = (ICollection) source; + int capacity = collection._getCount(); + if (capacity == 0) + return new HashMap<>(); + + if (collection instanceof IArrayList) { + IArrayList list = (IArrayList) collection; + Map map = new HashMap<>(initialCapacity(capacity)); + for (int i = 0; i < capacity; i++) { + TSource element = list.get(i); + map.merge(keySelector.apply(element), elementSelector.apply(element), resultElementSelector::apply); + } + return map; + } + + Map map = new HashMap<>(initialCapacity(capacity)); + try (IEnumerator e = source.enumerator()) { + while (e.moveNext()) { + TSource element = e.current(); + map.merge(keySelector.apply(element), elementSelector.apply(element), resultElementSelector::apply); + } + } + return map; + } + + Map map = new HashMap<>(); + try (IEnumerator e = source.enumerator()) { + while (e.moveNext()) { + TSource element = e.current(); + map.merge(keySelector.apply(element), elementSelector.apply(element), resultElementSelector::apply); + } + } + return map; + } + public static Map toLinkedMap(IEnumerable source, Func1 keySelector) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); @@ -269,6 +316,50 @@ public static Map toLinkedMap(IEnumera return map; } + public static Map toLinkedMap(IEnumerable source, Func1 keySelector, Func1 elementSelector, Func2 resultElementSelector) { + if (source == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.source); + if (keySelector == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); + if (elementSelector == null) + ThrowHelper.throwArgumentNullException(ExceptionArgument.elementSelector); + + if (source instanceof ICollection) { + ICollection collection = (ICollection) source; + int capacity = collection._getCount(); + if (capacity == 0) + return new LinkedHashMap<>(); + + if (collection instanceof IArrayList) { + IArrayList list = (IArrayList) collection; + Map map = new LinkedHashMap<>(initialCapacity(capacity)); + for (int i = 0; i < capacity; i++) { + TSource element = list.get(i); + map.merge(keySelector.apply(element), elementSelector.apply(element), resultElementSelector::apply); + } + return map; + } + + Map map = new LinkedHashMap<>(initialCapacity(capacity)); + try (IEnumerator e = source.enumerator()) { + while (e.moveNext()) { + TSource element = e.current(); + map.merge(keySelector.apply(element), elementSelector.apply(element), resultElementSelector::apply); + } + } + return map; + } + + Map map = new LinkedHashMap<>(); + try (IEnumerator e = source.enumerator()) { + while (e.moveNext()) { + TSource element = e.current(); + map.merge(keySelector.apply(element), elementSelector.apply(element), resultElementSelector::apply); + } + } + return map; + } + public static Set toSet(IEnumerable source) { if (source == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.source); diff --git a/src/main/java/com/bestvike/linq/enumerable/UnionBy.java b/src/main/java/com/bestvike/linq/enumerable/UnionBy.java index 0893ce1b..dc8c0469 100644 --- a/src/main/java/com/bestvike/linq/enumerable/UnionBy.java +++ b/src/main/java/com/bestvike/linq/enumerable/UnionBy.java @@ -6,10 +6,6 @@ import com.bestvike.linq.IEnumerator; import com.bestvike.linq.exception.ExceptionArgument; import com.bestvike.linq.exception.ThrowHelper; -import com.bestvike.linq.util.Utilities; - -import java.util.ArrayList; -import java.util.List; /** * Created by 许崇雷 on 2018-05-09. @@ -30,238 +26,71 @@ public static IEnumerable unionBy(IEnumerable if (keySelector == null) ThrowHelper.throwArgumentNullException(ExceptionArgument.keySelector); - UnionByIterator unionBy; - return first instanceof UnionByIterator && keySelector == (unionBy = (UnionByIterator) first).keySelector && Utilities.areEqualityComparersEqual(comparer, unionBy.comparer) - ? unionBy._unionBy(second) - : new UnionByIterator2<>(first, second, keySelector, comparer); + return new UnionByIterator<>(first, second, keySelector, comparer); } } -abstract class UnionByIterator extends Iterator implements IIListProvider { - final Func1 keySelector; - final IEqualityComparer comparer; - private IEnumerator enumerator; +final class UnionByIterator extends AbstractIterator { + private final IEnumerable first; + private final IEnumerable second; + private final Func1 keySelector; + private final IEqualityComparer comparer; private Set set; + private IEnumerator enumerator; - UnionByIterator(Func1 keySelector, IEqualityComparer comparer) { + UnionByIterator(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { + this.first = first; + this.second = second; this.keySelector = keySelector; this.comparer = comparer; } @Override - public void close() { - if (this.enumerator != null) { - this.enumerator.close(); - this.enumerator = null; - this.set = null; - } - super.close(); - } - - abstract IEnumerable getEnumerable(int index); - - abstract UnionByIterator _unionBy(IEnumerable next); - - private void setEnumerator(IEnumerator enumerator) { - if (this.enumerator != null) - this.enumerator.close(); - this.enumerator = enumerator; - } - - private void storeFirst() { - Set set = new Set<>(this.comparer); - TSource element = this.enumerator.current(); - set.add(this.keySelector.apply(element)); - this.current = element; - this.set = set; - } - - private boolean getNext() { - Set set = this.set; - assert set != null; - - while (this.enumerator.moveNext()) { - TSource element = this.enumerator.current(); - if (set.add(this.keySelector.apply(element))) { - this.current = element; - return true; - } - } - - return false; + public AbstractIterator clone() { + return new UnionByIterator<>(this.first, this.second, this.keySelector, this.comparer); } @Override public boolean moveNext() { - if (this.state == 1) { - for (IEnumerable enumerable = this.getEnumerable(0); enumerable != null; enumerable = this.getEnumerable(this.state - 1)) { - IEnumerator enumerator = enumerable.enumerator(); - this.setEnumerator(enumerator); - ++this.state; - if (enumerator.moveNext()) { - this.storeFirst(); - return true; - } - } - } else if (this.state > 0) { - while (true) { - if (this.getNext()) - return true; - - IEnumerable enumerable = this.getEnumerable(this.state - 1); - if (enumerable == null) - break; - - this.setEnumerator(enumerable.enumerator()); - ++this.state; - } - } - - this.close(); - return false; - } - - private Set fillSet() { - Set set = new Set<>(this.comparer); - for (int index = 0; ; ++index) { - IEnumerable enumerable = this.getEnumerable(index); - if (enumerable == null) - return set; - set.unionWith(enumerable, this.keySelector); - } - } - - @Override - public TSource[] _toArray(Class clazz) { - LargeArrayBuilder builder = new LargeArrayBuilder<>(); - Set set = new Set<>(this.comparer); - for (int index = 0; ; ++index) { - IEnumerable enumerable = this.getEnumerable(index); - if (enumerable == null) - return builder.toArray(clazz); - try (IEnumerator e = enumerable.enumerator()) { - while (e.moveNext()) { - TSource element = e.current(); - if (set.add(this.keySelector.apply(element))) - builder.add(element); - } - } - } - } - - @Override - public Object[] _toArray() { - LargeArrayBuilder builder = new LargeArrayBuilder<>(); - Set set = new Set<>(this.comparer); - for (int index = 0; ; ++index) { - IEnumerable enumerable = this.getEnumerable(index); - if (enumerable == null) - return builder.toArray(); - try (IEnumerator e = enumerable.enumerator()) { - while (e.moveNext()) { - TSource element = e.current(); - if (set.add(this.keySelector.apply(element))) - builder.add(element); + switch (this.state) { + case 1: + this.set = new Set<>(this.comparer); + this.enumerator = this.first.enumerator(); + this.state = 2; + case 2: + while (this.enumerator.moveNext()) { + TSource element = this.enumerator.current(); + if (this.set.add(this.keySelector.apply(element))) { + this.current = element; + return true; + } } - } - } - } - - @Override - public List _toList() { - List list = new ArrayList<>(); - Set set = new Set<>(this.comparer); - for (int index = 0; ; ++index) { - IEnumerable enumerable = this.getEnumerable(index); - if (enumerable == null) - return list; - try (IEnumerator e = enumerable.enumerator()) { - while (e.moveNext()) { - TSource element = e.current(); - if (set.add(this.keySelector.apply(element))) - list.add(element); + this.enumerator.close(); + this.enumerator = this.second.enumerator(); + this.state = 3; + case 3: + while (this.enumerator.moveNext()) { + TSource element = this.enumerator.current(); + if (this.set.add(this.keySelector.apply(element))) { + this.current = element; + return true; + } } - } - } - } - - @Override - public int _getCount(boolean onlyIfCheap) { - return onlyIfCheap ? -1 : this.fillSet().getCount(); - } -} - - -final class UnionByIterator2 extends UnionByIterator { - private final IEnumerable first; - private final IEnumerable second; - - UnionByIterator2(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer) { - super(keySelector, comparer); - assert first != null; - assert second != null; - this.first = first; - this.second = second; - } - - @Override - public Iterator clone() { - return new UnionByIterator2<>(this.first, this.second, this.keySelector, this.comparer); - } - - @Override - IEnumerable getEnumerable(int index) { - assert index >= 0 && index <= 2; - switch (index) { - case 0: - return this.first; - case 1: - return this.second; + this.close(); + return false; default: - return null; + return false; } } @Override - UnionByIterator _unionBy(IEnumerable next) { - SingleLinkedNode> sources = new SingleLinkedNode<>(this.first).add(this.second).add(next); - return new UnionByIteratorN<>(sources, 2, this.keySelector, this.comparer); - } -} - - -final class UnionByIteratorN extends UnionByIterator { - private final SingleLinkedNode> sources; - private final int headIndex; - - UnionByIteratorN(SingleLinkedNode> sources, int headIndex, Func1 keySelector, IEqualityComparer comparer) { - super(keySelector, comparer); - assert headIndex >= 2; - assert sources != null && sources.getCount() == headIndex + 1; - this.sources = sources; - this.headIndex = headIndex; - } - - @Override - public Iterator clone() { - return new UnionByIteratorN<>(this.sources, this.headIndex, this.keySelector, this.comparer); - } - - @Override - IEnumerable getEnumerable(int index) { - return index > this.headIndex ? null : this.sources.getNode(this.headIndex - index).getItem(); - } - - @Override - UnionByIterator _unionBy(IEnumerable next) { - if (this.headIndex == Integer.MAX_VALUE - 2) { - // In the unlikely case of this many unions, if we produced a UnionIteratorN - // with int.MaxValue then state would overflow before it matched it's index. - // So we use the naive approach of just having a left and right sequence. - return new UnionByIterator2<>(this, next, this.keySelector, this.comparer); + public void close() { + if (this.enumerator != null) { + this.enumerator.close(); + this.enumerator = null; + this.set = null; } - - return new UnionByIteratorN<>(this.sources.add(next), this.headIndex + 1, this.keySelector, this.comparer); + super.close(); } } diff --git a/src/main/java/com/bestvike/linq/enumerable/_Set.java b/src/main/java/com/bestvike/linq/enumerable/_Set.java index a591ab52..2c767c42 100644 --- a/src/main/java/com/bestvike/linq/enumerable/_Set.java +++ b/src/main/java/com/bestvike/linq/enumerable/_Set.java @@ -2,7 +2,6 @@ import com.bestvike.collections.generic.EqualityComparer; import com.bestvike.collections.generic.IEqualityComparer; -import com.bestvike.function.Func1; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.IEnumerator; import com.bestvike.linq.util.ArrayUtils; @@ -29,6 +28,12 @@ final class Set { this.slots[i] = new Slot(); } + // Constructs a set that compares items with the specified collection and comparer. + Set(IEnumerable collection, IEqualityComparer comparer) { + this(comparer); + this.unionWith(collection); + } + // If value is not in set, add it and return true; otherwise return false public boolean add(TElement value) { int hashCode = this.internalGetHashCode(value); @@ -130,17 +135,6 @@ public void unionWith(IEnumerable other) { } } - // Unions this set with an enumerable and selector. - public void unionWith(IEnumerable other, Func1 selector) { - assert other != null; - assert selector != null; - - try (IEnumerator e = other.enumerator()) { - while (e.moveNext()) - this.add(selector.apply(e.current())); - } - } - // Gets the hash code of the provided value with its sign bit zeroed out, so that modulo has a positive result. private int internalGetHashCode(TElement value) { return value == null ? 0 : this.comparer.hashCode(value) & 0x7FFFFFFF; diff --git a/src/main/java/com/bestvike/linq/enumerable/__ArrayBuilder.java b/src/main/java/com/bestvike/linq/enumerable/__ArrayBuilder.java index 50fbe1e4..1c1361df 100644 --- a/src/main/java/com/bestvike/linq/enumerable/__ArrayBuilder.java +++ b/src/main/java/com/bestvike/linq/enumerable/__ArrayBuilder.java @@ -7,7 +7,7 @@ */ final class ArrayBuilder {//struct private static final int DefaultCapacity = 4; - private static final int MaxCoreClrArrayLength = 0x7fefffff; // For byte arrays the limit is slightly larger + private static final int MaxArrayLength = 0x7fffffc7;// All attempts to allocate a larger array will fail. private Object[] array; // Starts out null, initialized on first Add. private int count; // Number of items into array we're using. @@ -81,8 +81,8 @@ private void ensureCapacity(int minimum) { assert minimum > this.getCapacity(); int capacity = this.getCapacity(); int nextCapacity = capacity == 0 ? DefaultCapacity : 2 * capacity; - if (Integer.compareUnsigned(nextCapacity, MaxCoreClrArrayLength) > 0) - nextCapacity = Math.max(capacity + 1, MaxCoreClrArrayLength); + if (Integer.compareUnsigned(nextCapacity, MaxArrayLength) > 0) + nextCapacity = Math.max(capacity + 1, MaxArrayLength); nextCapacity = Math.max(nextCapacity, minimum); Object[] next = new Object[nextCapacity]; if (this.count > 0) diff --git a/src/main/java/com/bestvike/linq/enumerable/__EnumerableHelpers.java b/src/main/java/com/bestvike/linq/enumerable/__EnumerableHelpers.java index af178b70..40c7426f 100644 --- a/src/main/java/com/bestvike/linq/enumerable/__EnumerableHelpers.java +++ b/src/main/java/com/bestvike/linq/enumerable/__EnumerableHelpers.java @@ -14,26 +14,9 @@ * Created by 许崇雷 on 2018-05-07. */ final class EnumerableHelpers { - private EnumerableHelpers() { - } - - // Tries to get the count of the enumerable cheaply. - public static boolean tryGetCount(IEnumerable source, out count) { - assert source != null; + private static final int MaxArrayLength = 0x7fffffc7;// All attempts to allocate a larger array will fail. - if (source instanceof ICollection) { - ICollection collection = (ICollection) source; - count.value = collection._getCount(); - return true; - } - - if (source instanceof IIListProvider) { - IIListProvider provider = (IIListProvider) source; - return (count.value = provider._getCount(true)) >= 0; - } - - count.value = -1; - return false; + private EnumerableHelpers() { } //Copies items from an enumerable to an array. @@ -124,22 +107,12 @@ public static Object[] toArray(IEnumerable source, out length) { while (en.moveNext()) { if (count == arr.length) { - // MaxArrayLength is defined in Array.MaxArrayLength and in gchelpers in CoreCLR. - // It represents the maximum number of elements that can be in an array where - // the size of the element is greater than one byte; a separate, slightly larger constant, - // is used when the size of the element is one. - final int MaxArrayLength = 0x7FEFFFFF; // This is the same growth logic as in List: // If the array is currently empty, we make it a default size. Otherwise, we attempt to // double the size of the array. Doubling will overflow once the size of the array reaches // 2^30, since doubling to 2^31 is 1 larger than Int32.MaxValue. In that case, we instead // constrain the length to be MaxArrayLength (this overflow check works because of the - // cast to uint). Because a slightly larger constant is used when T is one byte in size, we - // could then end up in a situation where arr.Length is MaxArrayLength or slightly larger, such - // that we constrain newLength to be MaxArrayLength but the needed number of elements is actually - // larger than that. For that case, we then ensure that the newLength is large enough to hold - // the desired capacity. This does mean that in the very rare case where we've grown to such a - // large size, each new element added after MaxArrayLength will end up doing a resize. + // cast to uint). int newLength = count << 1; if (Integer.compareUnsigned(newLength, MaxArrayLength) > 0) newLength = MaxArrayLength <= count ? count + 1 : MaxArrayLength; diff --git a/src/main/java/com/bestvike/linq/enumerable/__SparseArrayBuilder.java b/src/main/java/com/bestvike/linq/enumerable/__SparseArrayBuilder.java index 9032514a..85bff0c2 100644 --- a/src/main/java/com/bestvike/linq/enumerable/__SparseArrayBuilder.java +++ b/src/main/java/com/bestvike/linq/enumerable/__SparseArrayBuilder.java @@ -130,7 +130,7 @@ public void reserve(int count) { public boolean reserveOrAdd(IEnumerable items) { out itemCountRef = out.init(); - if (EnumerableHelpers.tryGetCount(items, itemCountRef)) { + if (Count.tryGetNonEnumeratedCount(items, itemCountRef)) { int itemCount = itemCountRef.value; if (itemCount > 0) { this.reserve(itemCount); diff --git a/src/main/java/com/bestvike/linq/exception/ExceptionArgument.java b/src/main/java/com/bestvike/linq/exception/ExceptionArgument.java index 429571c1..e4d09730 100644 --- a/src/main/java/com/bestvike/linq/exception/ExceptionArgument.java +++ b/src/main/java/com/bestvike/linq/exception/ExceptionArgument.java @@ -22,6 +22,7 @@ public enum ExceptionArgument { selector, source, third, + size, //extension action, array, @@ -32,6 +33,7 @@ public enum ExceptionArgument { condition, current, elements, + end, formatter, hasNext, iterable, @@ -44,6 +46,9 @@ public enum ExceptionArgument { obj, options, other, + range, + resultElementSelector, + start, startIndex, value, } diff --git a/src/main/java/com/bestvike/linq/resources/SR.java b/src/main/java/com/bestvike/linq/resources/SR.java index 12957ec6..5fffdce1 100644 --- a/src/main/java/com/bestvike/linq/resources/SR.java +++ b/src/main/java/com/bestvike/linq/resources/SR.java @@ -12,6 +12,7 @@ public final class SR { public static final String Arg_InvalidOperationException = "Operation is not valid due to the current state of the object."; public static final String Arg_NotSupportedException = "Specified method is not supported."; public static final String Arg_RepeatInvokeException = "Specified method cannot be invoked repeatedly."; + public static final String InvalidOperation_EmptyQueue = "Queue empty."; public static final String EmptyIterable = "Iteration yielded no results."; public static final String EmptyEnumerable = "Enumeration yielded no results."; diff --git a/src/test/java/com/bestvike/IndexTest.java b/src/test/java/com/bestvike/IndexTest.java new file mode 100644 index 00000000..1b134d14 --- /dev/null +++ b/src/test/java/com/bestvike/IndexTest.java @@ -0,0 +1,125 @@ +package com.bestvike; + +import com.bestvike.linq.exception.ArgumentOutOfRangeException; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +/** + * Created by 许崇雷 on 2023-05-31. + */ +class IndexTest extends TestCase { + @Test + void CreationTest() { + Index index = new Index(1, false); + assertEquals(1, index.getValue()); + assertFalse(index.isFromEnd()); + + index = new Index(11, true); + assertEquals(11, index.getValue()); + assertTrue(index.isFromEnd()); + + index = Index.Start; + assertEquals(0, index.getValue()); + assertFalse(index.isFromEnd()); + + index = Index.End; + assertEquals(0, index.getValue()); + assertTrue(index.isFromEnd()); + + index = Index.fromStart(3); + assertEquals(3, index.getValue()); + assertFalse(index.isFromEnd()); + + index = Index.fromEnd(10); + assertEquals(10, index.getValue()); + assertTrue(index.isFromEnd()); + + assertThrows(ArgumentOutOfRangeException.class, () -> { + new Index(-1, false); + }); + assertThrows(ArgumentOutOfRangeException.class, () -> { + Index.fromStart(-3); + }); + assertThrows(ArgumentOutOfRangeException.class, () -> { + Index.fromEnd(-1); + }); + } + + @Test + void GetOffsetTest() { + Index index = Index.fromStart(3); + assertEquals(3, index.getOffset(3)); + assertEquals(3, index.getOffset(10)); + assertEquals(3, index.getOffset(20)); + + // we don't validate the length in the getOffset so passing short length will just return the regular calculation according to the length value. + assertEquals(3, index.getOffset(2)); + + index = Index.fromEnd(3); + assertEquals(0, index.getOffset(3)); + assertEquals(7, index.getOffset(10)); + assertEquals(17, index.getOffset(20)); + + // we don't validate the length in the getOffset so passing short length will just return the regular calculation according to the length value. + assertEquals(-1, index.getOffset(2)); + } + + @Test + void EqualityTest() { + Index index1 = Index.fromStart(10); + Index index2 = Index.fromStart(10); + assertTrue(index1.equals(index2)); + + index2 = new Index(10, true); + assertFalse(index1.equals(index2)); + + index2 = new Index(9, false); + assertFalse(index1.equals(index2)); + } + + @Test + void HashCodeTest() { + Index index1 = Index.fromStart(10); + Index index2 = Index.fromStart(10); + assertEquals(index1.hashCode(), index2.hashCode()); + + index2 = new Index(10, true); + assertNotEquals(index1.hashCode(), index2.hashCode()); + + index2 = new Index(99999, false); + assertNotEquals(index1.hashCode(), index2.hashCode()); + } + + @Test + void CloneTest() { + Index index1 = Index.fromStart(10); + assertEquals(index1, index1.clone()); + } + + @Test + void ToStringTest() { + Index index1 = Index.fromStart(100); + assertEquals(new Index(100, false).toString(), index1.toString()); + + index1 = new Index(50, true); + assertEquals("^50", index1.toString()); + } + + @Test + void CollectionTest() { + Integer[] array = new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + List list = Arrays.asList(array); + + for (int i = 0; i < list.size(); i++) { + assertEquals(i, list.get(Index.fromStart(i).getValue())); + // + int offset = Index.fromEnd(i + 1).getOffset(list.size()); + assertEquals(list.size() - i - 1, list.get(offset)); + // + assertEquals(i, list.get(Index.fromStart(i).getValue())); + assertEquals(list.size() - i - 1, array[offset]); + } + } +} diff --git a/src/test/java/com/bestvike/RangeTest.java b/src/test/java/com/bestvike/RangeTest.java new file mode 100644 index 00000000..1eb28834 --- /dev/null +++ b/src/test/java/com/bestvike/RangeTest.java @@ -0,0 +1,106 @@ +package com.bestvike; + +import com.bestvike.linq.exception.ArgumentOutOfRangeException; +import org.junit.jupiter.api.Test; + +/** + * Created by 许崇雷 on 2023-05-31. + */ +class RangeTest extends TestCase { + @Test + void CreationTest() { + Range range = new Range(new Index(10, false), new Index(2, true)); + assertEquals(10, range.getStart().getValue()); + assertFalse(range.getStart().isFromEnd()); + assertEquals(2, range.getEnd().getValue()); + assertTrue(range.getEnd().isFromEnd()); + + range = Range.startAt(new Index(7, false)); + assertEquals(7, range.getStart().getValue()); + assertFalse(range.getStart().isFromEnd()); + assertEquals(0, range.getEnd().getValue()); + assertTrue(range.getEnd().isFromEnd()); + + range = Range.endAt(new Index(3, true)); + assertEquals(0, range.getStart().getValue()); + assertFalse(range.getStart().isFromEnd()); + assertEquals(3, range.getEnd().getValue()); + assertTrue(range.getEnd().isFromEnd()); + + range = Range.All; + assertEquals(0, range.getStart().getValue()); + assertFalse(range.getStart().isFromEnd()); + assertEquals(0, range.getEnd().getValue()); + assertTrue(range.getEnd().isFromEnd()); + } + + @Test + void GetOffsetAndLengthTest() { + Range range = Range.startAt(Index.fromStart(5)); + Range.OffsetLength offsetLength = range.getOffsetAndLength(20); + assertEquals(5, offsetLength.Offset); + assertEquals(15, offsetLength.Length); + + offsetLength = range.getOffsetAndLength(5); + assertEquals(5, offsetLength.Offset); + assertEquals(0, offsetLength.Length); + + // we don't validate the length in the GetOffsetAndLength so passing negative length will just return the regular calculation according to the length value. + offsetLength = range.getOffsetAndLength(-10); + assertEquals(5, offsetLength.Offset); + assertEquals(-15, offsetLength.Length); + + assertThrows(ArgumentOutOfRangeException.class, () -> { + range.getOffsetAndLength(4); + }); + + Range range2 = Range.endAt(Index.fromStart(4)); + offsetLength = range2.getOffsetAndLength(20); + assertEquals(0, offsetLength.Offset); + assertEquals(4, offsetLength.Length); + assertThrows(ArgumentOutOfRangeException.class, () -> { + range2.getOffsetAndLength(1); + }); + } + + @Test + void EqualityTest() { + Range range1 = new Range(new Index(10, false), new Index(20, false)); + Range range2 = new Range(new Index(10, false), new Index(20, false)); + assertTrue(range1.equals(range2)); + + range2 = new Range(new Index(10, false), new Index(20, true)); + assertFalse(range1.equals(range2)); + + range2 = new Range(new Index(10, false), new Index(21, false)); + assertFalse(range1.equals(range2)); + } + + @Test + void HashCodeTest() { + Range range1 = new Range(new Index(10, false), new Index(20, false)); + Range range2 = new Range(new Index(10, false), new Index(20, false)); + assertEquals(range1.hashCode(), range2.hashCode()); + + range2 = new Range(new Index(10, false), new Index(20, true)); + assertNotEquals(range1.hashCode(), range2.hashCode()); + + range2 = new Range(new Index(10, false), new Index(21, false)); + assertNotEquals(range1.hashCode(), range2.hashCode()); + } + + @Test + void CloneTest() { + Range range1 = new Range(new Index(10, false), new Index(20, true)); + assertEquals(range1, range1.clone()); + } + + @Test + void ToStringTest() { + Range range1 = new Range(new Index(10, false), new Index(20, false)); + assertEquals("10..20", range1.toString()); + + range1 = new Range(new Index(10, false), new Index(20, true)); + assertEquals("10..^20", range1.toString()); + } +} diff --git a/src/test/java/com/bestvike/TestCase.java b/src/test/java/com/bestvike/TestCase.java index e67d346f..874b98cd 100644 --- a/src/test/java/com/bestvike/TestCase.java +++ b/src/test/java/com/bestvike/TestCase.java @@ -4,6 +4,7 @@ import com.bestvike.collections.generic.Comparer; import com.bestvike.collections.generic.ICollection; import com.bestvike.collections.generic.IEqualityComparer; +import com.bestvike.collections.generic.IList; import com.bestvike.collections.generic.StringComparer; import com.bestvike.function.Action0; import com.bestvike.function.Action1; @@ -20,6 +21,7 @@ import com.bestvike.linq.entity.Department; import com.bestvike.linq.entity.Employee; import com.bestvike.linq.enumerable.AbstractEnumerator; +import com.bestvike.linq.enumerable.AbstractIterator; import com.bestvike.linq.enumerable.Values; import com.bestvike.linq.exception.ExceptionArgument; import com.bestvike.linq.exception.InvalidOperationException; @@ -444,6 +446,22 @@ protected static IEnumerable FlipIsCollection(IEnumerable source) { return source instanceof ICollection ? ForceNotCollection(source) : Linq.of(source.toList()); } + protected static List Repeat(Func1 factory, int count) { + List results = new ArrayList<>(count); + for (int index = 0; index < count; index++) + results.add(factory.apply(index)); + return results; + } + + protected static IEnumerable ListPartitionOrEmpty(IEnumerable source) {// Or Empty + assertIsAssignableFrom(IList.class, source); + return source.skip(0); + } + + protected static IEnumerable EnumerablePartitionOrEmpty(IEnumerable source) {// Or Empty + return ForceNotCollection(source).skip(0); + } + private static boolean equal(Object expected, Object actual) { if (expected == actual) return true; @@ -1059,6 +1077,29 @@ public Long next() { } } + public static class InfiniteRepeatEnumerator extends AbstractIterator { + private final T item; + private final Action0 callback; + + public InfiniteRepeatEnumerator(T item, Action0 callback) { + this.item = item; + this.callback = callback; + } + + @Override + public AbstractIterator clone() { + return new InfiniteRepeatEnumerator<>(this.item, this.callback); + } + + @Override + public boolean moveNext() { + if (this.callback != null) + this.callback.apply(); + this.current = this.item; + return true; + } + } + @SuppressWarnings("unused") public static class SkipTakeData { public static IEnumerable EnumerableData() { diff --git a/src/test/java/com/bestvike/collections/generic/ComparerTest.java b/src/test/java/com/bestvike/collections/generic/ComparerTest.java index 47ffaf89..9256a27c 100644 --- a/src/test/java/com/bestvike/collections/generic/ComparerTest.java +++ b/src/test/java/com/bestvike/collections/generic/ComparerTest.java @@ -54,4 +54,36 @@ void testCreateWithIComparison() { void testNotImplementComparable() { assertThrows(ArgumentException.class, () -> Comparer.Default().compare(depts[0], depts[1])); } + + @Test + void testFloat() { + Comparator comparator = Comparer.Float(); + assertEquals(0, comparator.compare(null, null)); + assertEquals(1, comparator.compare(Float.NaN, null)); + assertEquals(-1, comparator.compare(null, Float.NaN)); + // + assertEquals(1, comparator.compare(1f, 0f)); + assertEquals(0, comparator.compare(1f, 1f)); + assertEquals(-1, comparator.compare(0f, 1f)); + // + assertEquals(1, comparator.compare(0f, Float.NaN)); + assertEquals(0, comparator.compare(Float.NaN, Float.NaN)); + assertEquals(-1, comparator.compare(Float.NaN, 0f)); + } + + @Test + void testDouble() { + Comparator comparator = Comparer.Double(); + assertEquals(0, comparator.compare(null, null)); + assertEquals(1, comparator.compare(Double.NaN, null)); + assertEquals(-1, comparator.compare(null, Double.NaN)); + // + assertEquals(1, comparator.compare(1d, 0d)); + assertEquals(0, comparator.compare(1d, 1d)); + assertEquals(-1, comparator.compare(0d, 1d)); + // + assertEquals(1, comparator.compare(0d, Double.NaN)); + assertEquals(0, comparator.compare(Double.NaN, Double.NaN)); + assertEquals(-1, comparator.compare(Double.NaN, 0d)); + } } diff --git a/src/test/java/com/bestvike/collections/generic/QueueTest.java b/src/test/java/com/bestvike/collections/generic/QueueTest.java new file mode 100644 index 00000000..a91fb722 --- /dev/null +++ b/src/test/java/com/bestvike/collections/generic/QueueTest.java @@ -0,0 +1,116 @@ +package com.bestvike.collections.generic; + +import com.bestvike.TestCase; +import com.bestvike.linq.exception.InvalidOperationException; +import com.bestvike.out; +import org.junit.jupiter.api.Test; + +/** + * Created by 许崇雷 on 2023-10-10. + */ +class QueueTest extends TestCase { + @Test + void size() { + Queue queue = new Queue<>(); + assertEquals(0, queue.size()); + queue.enqueue(null); + assertEquals(1, queue.size()); + queue.enqueue(5); + assertEquals(2, queue.size()); + } + + @Test + void isEmpty() { + Queue queue = new Queue<>(5); + assertTrue(queue.isEmpty()); + queue.enqueue(null); + assertFalse(queue.isEmpty()); + } + + @Test + void clear() { + Queue queue = new Queue<>(); + queue.enqueue(null); + assertFalse(queue.isEmpty()); + queue.clear(); + assertTrue(queue.isEmpty()); + } + + @Test + void enqueue() { + Queue queue = new Queue<>(); + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(null); + assertEquals(3, queue.size()); + } + + @Test + void dequeue() { + Queue queue = new Queue<>(); + queue.enqueue(1); + queue.enqueue(null); + assertEquals(1, queue.dequeue()); + assertNull(queue.dequeue()); + assertThrows(InvalidOperationException.class, queue::dequeue); + } + + @Test + void tryDequeue() { + Queue queue = new Queue<>(); + queue.enqueue(1); + queue.enqueue(null); + // + out result = out.init(); + assertTrue(queue.tryDequeue(result)); + assertEquals(1, result.value); + assertTrue(queue.tryDequeue(result)); + assertNull(result.value); + assertFalse(queue.tryDequeue(result)); + assertNull(result.value); + } + + @Test + void peek() { + Queue queue = new Queue<>(); + assertThrows(InvalidOperationException.class, queue::peek); + queue.enqueue(1); + queue.enqueue(null); + assertEquals(1, queue.peek()); + assertEquals(2, queue.size()); + queue.clear(); + assertTrue(queue.isEmpty()); + queue.enqueue(null); + queue.enqueue(1); + assertEquals(null, queue.peek()); + assertEquals(2, queue.size()); + } + + @Test + void tryPeek() { + Queue queue = new Queue<>(); + out result = out.init(); + assertFalse(queue.tryPeek(result)); + assertNull(result.value); + queue.enqueue(1); + queue.enqueue(null); + assertTrue(queue.tryPeek(result)); + assertEquals(1, result.value); + assertEquals(2, queue.size()); + queue.clear(); + assertTrue(queue.isEmpty()); + queue.enqueue(null); + queue.enqueue(1); + assertTrue(queue.tryPeek(result)); + assertNull(result.value); + assertEquals(2, queue.size()); + } + + @Test + void contains() { + Queue queue = new Queue<>(); + assertFalse(queue.contains(null)); + queue.enqueue(null); + assertTrue(queue.contains(null)); + } +} \ No newline at end of file diff --git a/src/test/java/com/bestvike/linq/enumerable/ChunkTest.java b/src/test/java/com/bestvike/linq/enumerable/ChunkTest.java new file mode 100644 index 00000000..18b17521 --- /dev/null +++ b/src/test/java/com/bestvike/linq/enumerable/ChunkTest.java @@ -0,0 +1,187 @@ +package com.bestvike.linq.enumerable; + +import com.bestvike.TestCase; +import com.bestvike.linq.IEnumerable; +import com.bestvike.linq.IEnumerator; +import com.bestvike.linq.Linq; +import com.bestvike.linq.exception.ArgumentOutOfRangeException; +import com.bestvike.linq.util.ArgsList; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Created by 许崇雷 on 2023-06-02. + */ +class ChunkTest extends TestCase { + private static IEnumerable ThrowsWhenSizeIsNonPositive_TestData() { + ArgsList argsList = new ArgsList(); + argsList.add(0); + argsList.add(-1); + return argsList; + } + + private static IEnumerable ConvertToType(T[] array, Class type) { + if (type == TestReadOnlyCollection.class) + return new TestReadOnlyCollection<>(array); + if (type == TestCollection.class) + return new TestCollection<>(array); + if (type == TestEnumerable.class) + return new TestEnumerable<>(array); + throw new RuntimeException(); + } + + private static IEnumerable ChunkSourceRepeatCalls_TestData() { + ArgsList argsList = new ArgsList(); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}, TestReadOnlyCollection.class); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}, TestCollection.class); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}, TestEnumerable.class); + return argsList; + } + + private static IEnumerable ChunkSourceEvenly_TestData() { + ArgsList argsList = new ArgsList(); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}, TestReadOnlyCollection.class); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}, TestCollection.class); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}, TestEnumerable.class); + return argsList; + } + + private static IEnumerable ChunkSourceUnevenly_TestData() { + ArgsList argsList = new ArgsList(); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2}, TestReadOnlyCollection.class); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2}, TestCollection.class); + argsList.add(new Integer[]{9999, 0, 888, -1, 66, -777, 1, 2}, TestEnumerable.class); + return argsList; + } + + private static IEnumerable ChunkSourceSmallerThanMaxSize_TestData() { + ArgsList argsList = new ArgsList(); + argsList.add(new Integer[]{9999, 0}, TestReadOnlyCollection.class); + argsList.add(new Integer[]{9999, 0}, TestCollection.class); + argsList.add(new Integer[]{9999, 0}, TestEnumerable.class); + return argsList; + } + + private static IEnumerable EmptySourceYieldsNoChunks_TestData() { + ArgsList argsList = new ArgsList(); + argsList.add(new Integer[]{}, TestReadOnlyCollection.class); + argsList.add(new Integer[]{}, TestCollection.class); + argsList.add(new Integer[]{}, TestEnumerable.class); + return argsList; + } + + @Test + void ThrowsOnNullSource() { + IEnumerable source = null; + assertThrows(NullPointerException.class, () -> source.chunk(5, Integer.class)); + } + + @ParameterizedTest + @MethodSource("ThrowsWhenSizeIsNonPositive_TestData") + void ThrowsWhenSizeIsNonPositive(int size) { + IEnumerable source = Linq.singleton(1); + assertThrows(ArgumentOutOfRangeException.class, () -> source.chunk(size, Integer.class)); + } + + @Test + void ChunkSourceLazily() { + try (IEnumerator chunks = new FastInfiniteEnumerator().chunk(5, Integer.class).enumerator()) { + chunks.moveNext(); + assertEquals(Linq.of(null, null, null, null, null), Linq.of(chunks.current())); + assertTrue(chunks.moveNext()); + } + } + + @ParameterizedTest + @MethodSource("ChunkSourceRepeatCalls_TestData") + void ChunkSourceRepeatCalls(Integer[] array, Class type) { + IEnumerable source = ConvertToType(array, type); + assertEquals(source.chunk(3, Integer.class), source.chunk(3, Integer.class)); + } + + @ParameterizedTest + @MethodSource("ChunkSourceEvenly_TestData") + void ChunkSourceEvenly(Integer[] array, Class type) { + IEnumerable source = ConvertToType(array, type); + + try (IEnumerator chunks = source.chunk(3, Integer.class).enumerator()) { + chunks.moveNext(); + assertEquals(Linq.of(9999, 0, 888), Linq.of(chunks.current())); + + chunks.moveNext(); + assertEquals(Linq.of(-1, 66, -777), Linq.of(chunks.current())); + + chunks.moveNext(); + assertEquals(Linq.of(1, 2, -12345), Linq.of(chunks.current())); + + assertFalse(chunks.moveNext()); + } + } + + @ParameterizedTest + @MethodSource("ChunkSourceUnevenly_TestData") + void ChunkSourceUnevenly(Integer[] array, Class type) { + IEnumerable source = ConvertToType(array, type); + + try (IEnumerator chunks = source.chunk(3, Integer.class).enumerator()) { + chunks.moveNext(); + assertEquals(Linq.of(9999, 0, 888), Linq.of(chunks.current())); + + chunks.moveNext(); + assertEquals(Linq.of(-1, 66, -777), Linq.of(chunks.current())); + + chunks.moveNext(); + assertEquals(Linq.of(1, 2), Linq.of(chunks.current())); + + assertFalse(chunks.moveNext()); + } + } + + @ParameterizedTest + @MethodSource("ChunkSourceSmallerThanMaxSize_TestData") + void ChunkSourceSmallerThanMaxSize(Integer[] array, Class type) { + IEnumerable source = ConvertToType(array, type); + try (IEnumerator chunks = source.chunk(3, Integer.class).enumerator()) { + chunks.moveNext(); + assertEquals(Linq.of(9999, 0), Linq.of(chunks.current())); + + assertFalse(chunks.moveNext()); + } + } + + @ParameterizedTest + @MethodSource("EmptySourceYieldsNoChunks_TestData") + void EmptySourceYieldsNoChunks(Integer[] array, Class type) { + IEnumerable source = ConvertToType(array, type); + try (IEnumerator chunks = source.chunk(3, Integer.class).enumerator()) { + assertFalse(chunks.moveNext()); + } + } + + @Test + void RemovingFromSourceBeforeIterating() { + List list = new ArrayList<>(Arrays.asList(9999, 0, 888, -1, 66, -777, 1, 2, -12345)); + + IEnumerable chunks = Linq.of(list).chunk(3, Integer.class); + list.remove((Object) 66); + + IEnumerable> expect = Linq.of(Linq.of(9999, 0, 888), Linq.of(-1, -777, 1), Linq.of(2, -12345)); + assertEquals(expect, chunks); + } + + @Test + void AddingToSourceBeforeIterating() { + List list = new ArrayList<>(Arrays.asList(9999, 0, 888, -1, 66, -777, 1, 2, -12345)); + + IEnumerable chunks = Linq.of(list).chunk(3, Integer.class); + list.add(10); + + IEnumerable> expect = Linq.of(Linq.of(9999, 0, 888), Linq.of(-1, 66, -777), Linq.of(1, 2, -12345), Linq.singleton(10)); + assertEquals(expect, chunks); + } +} diff --git a/src/test/java/com/bestvike/linq/enumerable/CountTest.java b/src/test/java/com/bestvike/linq/enumerable/CountTest.java index 61992346..cc4b0522 100644 --- a/src/test/java/com/bestvike/linq/enumerable/CountTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/CountTest.java @@ -6,10 +6,13 @@ import com.bestvike.linq.Linq; import com.bestvike.linq.exception.ArgumentNullException; import com.bestvike.linq.util.ArgsList; +import com.bestvike.out; +import com.bestvike.ref; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import java.util.Arrays; import java.util.Stack; /** @@ -61,6 +64,32 @@ private static IEnumerable CountsAndTallies() { return argsList; } + private static IEnumerable NonEnumeratedCount_SupportedEnumerables() { + ArgsList argsList = new ArgsList(); + argsList.add(4, Linq.of(new int[]{1, 2, 3, 4})); + argsList.add(4, Linq.of(Arrays.asList(1, 2, 3, 4))); + argsList.add(4, new TestCollection<>(new Integer[]{1, 2, 3, 4})); + argsList.add(0, Linq.empty()); + argsList.add(100, Linq.range(1, 100)); + argsList.add(80, Linq.repeat(1, 80)); + argsList.add(50, Linq.range(1, 50).select(x -> x + 1)); + argsList.add(4, Linq.of(new int[]{1, 2, 3, 4}).select(x -> x + 1)); + argsList.add(50, Linq.range(1, 50).select(x -> x + 1).select(x -> x - 1)); + argsList.add(20, Linq.range(1, 20).reverse()); + argsList.add(20, Linq.range(1, 20).orderBy(x -> -x)); + argsList.add(20, Linq.range(1, 10).concat(Linq.range(11, 10))); + return argsList; + } + + private static IEnumerable NonEnumeratedCount_UnsupportedEnumerables() { + ArgsList argsList = new ArgsList(); + argsList.add(Linq.range(1, 100).where(x -> x % 2 == 0)); + argsList.add(Linq.range(1, 100).groupBy(x -> x % 2 == 0)); + argsList.add(new TestCollection<>(new Integer[]{1, 2, 3, 4}).select(x -> x + 1)); + argsList.add(Linq.range(1, 100).distinct()); + return argsList; + } + @Test void SameResultsRepeatCallsIntQuery() { IEnumerable q = Linq.of(9999, 0, 888, -1, 66, -777, 1, 2, -12345) @@ -129,6 +158,41 @@ void NullPredicate_ThrowsArgumentNullException() { assertThrows(ArgumentNullException.class, () -> Linq.range(0, 3).count(predicate)); } + @Test + void NonEnumeratedCount_NullSource_ThrowsArgumentNullException() { + assertThrows(ArgumentNullException.class, () -> { + out countRef = out.init(); + Count.tryGetNonEnumeratedCount(null, countRef); + }); + } + + @ParameterizedTest + @MethodSource("NonEnumeratedCount_SupportedEnumerables") + void NonEnumeratedCount_SupportedEnumerables_ShouldReturnExpectedCount(int expectedCount, IEnumerable source) { + out actualCountRef = out.init(); + assertTrue(Count.tryGetNonEnumeratedCount(source, actualCountRef)); + assertEquals(expectedCount, actualCountRef.value); + } + + @ParameterizedTest + @MethodSource("NonEnumeratedCount_UnsupportedEnumerables") + void NonEnumeratedCount_UnsupportedEnumerables_ShouldReturnFalse(IEnumerable source) { + out actualCountRef = out.init(); + assertFalse(Count.tryGetNonEnumeratedCount(source, actualCountRef)); + assertEquals(0, actualCountRef.value); + } + + @Test + void NonEnumeratedCount_ShouldNotEnumerateSource() { + ref isEnumerated = ref.init(false); + + out countRef = out.init(); + IEnumerable source = new InfiniteRepeatEnumerator<>(42, () -> isEnumerated.value = true); + assertFalse(Count.tryGetNonEnumeratedCount(source, countRef)); + assertEquals(0, countRef.value); + assertFalse(isEnumerated.value); + } + @Test void testCount() { int count = Linq.of(depts).count(); diff --git a/src/test/java/com/bestvike/linq/enumerable/DistinctByTest.java b/src/test/java/com/bestvike/linq/enumerable/DistinctByTest.java index c1dbe707..46ae91c7 100644 --- a/src/test/java/com/bestvike/linq/enumerable/DistinctByTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/DistinctByTest.java @@ -4,12 +4,16 @@ import com.bestvike.collections.generic.EqualityComparer; import com.bestvike.collections.generic.IEqualityComparer; import com.bestvike.collections.generic.StringComparer; +import com.bestvike.function.Func1; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.IEnumerator; import com.bestvike.linq.Linq; import com.bestvike.linq.entity.Employee; import com.bestvike.linq.exception.ArgumentNullException; import com.bestvike.linq.util.ArgsList; +import com.bestvike.out; +import com.bestvike.tuple.Tuple; +import com.bestvike.tuple.Tuple2; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -21,6 +25,76 @@ * Created by 许崇雷 on 2018-05-10. */ class DistinctByTest extends TestCase { + private static Object[] WrapArgs(IEnumerable source, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + return new Object[]{source, keySelector, comparer, expected}; + } + + private static IEnumerable TestData() { + ArgsList argsList = new ArgsList(); + + argsList.add(WrapArgs( + Linq.range(0, 10), + x -> x, + null, + Linq.range(0, 10))); + + argsList.add(WrapArgs( + Linq.range(5, 10), + x -> true, + null, + Linq.singleton(5))); + + argsList.add(WrapArgs( + Linq.range(0, 20), + x -> x % 5, + null, + Linq.range(0, 5))); + + argsList.add(WrapArgs( + Linq.repeat(5, 20), + x -> x, + null, + Linq.repeat(5, 1))); + + argsList.add(WrapArgs( + Linq.of("Bob", "bob", "tim", "Bob", "Tim"), + x -> x, + null, + Linq.of("Bob", "bob", "tim", "Tim"))); + + argsList.add(WrapArgs( + Linq.of("Bob", "bob", "tim", "Bob", "Tim"), + x -> x, + StringComparer.OrdinalIgnoreCase, + Linq.of("Bob", "tim"))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)), + Tuple2::getItem2, + null, + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 20), Tuple.create("Harry", 40)), + Tuple2::getItem2, + null, + Linq.of(Tuple.create("Tom", 20), Tuple.create("Harry", 40)))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Bob", 20), Tuple.create("bob", 30), Tuple.create("Harry", 40)), + Tuple2::getItem1, + null, + Linq.of(Tuple.create("Bob", 20), Tuple.create("bob", 30), Tuple.create("Harry", 40)))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Bob", 20), Tuple.create("bob", 30), Tuple.create("Harry", 40)), + Tuple2::getItem1, + StringComparer.OrdinalIgnoreCase, + Linq.of(Tuple.create("Bob", 20), Tuple.create("Harry", 40)))); + + return argsList; + } + private static IEnumerable SequencesWithDuplicates() { ArgsList argsList = new ArgsList(); // Validate an array of different numeric data types. @@ -48,6 +122,34 @@ private static IEnumerable SequencesWithDuplicates() { return argsList; } + @Test + public void SourceNull_ThrowsArgumentNullException() { + IEnumerable first = null; + + assertThrows(NullPointerException.class, () -> first.distinctBy(x -> x)); + assertThrows(NullPointerException.class, () -> first.distinctBy(x -> x, new AnagramEqualityComparer())); + } + + @Test + public void KeySelectorNull_ThrowsArgumentNullException() { + IEnumerable source = Linq.of("Bob", "Tim", "Robert", "Chris"); + Func1 keySelector = null; + assertThrows(ArgumentNullException.class, () -> source.distinctBy(keySelector)); + assertThrows(ArgumentNullException.class, () -> source.distinctBy(keySelector, new AnagramEqualityComparer())); + } + + @ParameterizedTest + @MethodSource("TestData") + void HasExpectedOutput(IEnumerable source, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + assertEquals(expected, source.distinctBy(keySelector, comparer)); + } + + @ParameterizedTest + @MethodSource("TestData") + void RunOnce_HasExpectedOutput(IEnumerable source, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + assertEquals(expected, source.runOnce().distinctBy(keySelector, comparer)); + } + @Test void SameResultsRepeatCallsIntQuery() { IEnumerable q = Linq.of(new int[]{0, 9999, 0, 888, -1, 66, -1, -777, 1, 2, -12345, 66, 66, -1, -1}) @@ -59,7 +161,7 @@ void SameResultsRepeatCallsIntQuery() { @Test void SameResultsRepeatCallsStringQuery() { IEnumerable q = Linq.of(new String[]{"!@#$%^", "C", "AAA", "Calling Twice", "SoS"}) - .where(x -> IsNullOrEmpty(x)); + .where(TestCase::IsNullOrEmpty); assertEquals(q.distinctBy(x -> x), q.distinctBy(x -> x)); } @@ -164,20 +266,6 @@ void NullComparer() { assertEquals(Linq.of(expected), Linq.of(source).distinctBy(x -> x)); } - @Test - void NullSource() { - IEnumerable source = null; - - assertThrows(NullPointerException.class, () -> source.distinctBy(x -> x)); - } - - @Test - void NullSourceCustomComparer() { - IEnumerable source = null; - - assertThrows(NullPointerException.class, () -> source.distinctBy(x -> x, StringComparer.Ordinal)); - } - @Test void CustomEqualityComparer() { String[] source = {"Bob", "Tim", "bBo", "miT", "Robert", "iTm"}; @@ -261,11 +349,11 @@ void testDistinctBy() { }; IEnumerable enumerable = Linq.of(emps2).distinctBy(emp -> emp.deptno); DistinctByIterator distinctIterator = (DistinctByIterator) enumerable; - assertEquals(1, distinctIterator._toArray(Employee.class).length); - assertEquals(1, distinctIterator._toArray().length); - assertEquals(1, distinctIterator._toList().size()); - assertEquals(-1, distinctIterator._getCount(true)); - assertEquals(1, distinctIterator._getCount(false)); + assertEquals(1, distinctIterator.toArray(Employee.class).length); + assertEquals(1, distinctIterator.toArray().size()); + assertEquals(1, distinctIterator.toList().size()); + assertFalse(Count.tryGetNonEnumeratedCount(distinctIterator, out.init())); + assertEquals(1, distinctIterator.count()); assertEquals(1, enumerable.count()); assertThrows(NullPointerException.class, () -> ((IEnumerable) null).distinctBy(x -> x)); @@ -300,11 +388,11 @@ public int hashCode(Integer obj) { IEnumerable enumerable = Linq.of(emps2).distinctBy(emp -> emp.empno, comparer); DistinctByIterator distinctIterator = (DistinctByIterator) enumerable; - assertEquals(1, distinctIterator._toArray(Employee.class).length); - assertEquals(1, distinctIterator._toArray().length); - assertEquals(1, distinctIterator._toList().size()); - assertEquals(-1, distinctIterator._getCount(true)); - assertEquals(1, distinctIterator._getCount(false)); + assertEquals(1, distinctIterator.toArray(Employee.class).length); + assertEquals(1, distinctIterator.toArray().size()); + assertEquals(1, distinctIterator.toList().size()); + assertFalse(Count.tryGetNonEnumeratedCount(distinctIterator, out.init())); + assertEquals(1, distinctIterator.count()); assertEquals(1, enumerable.count()); } } diff --git a/src/test/java/com/bestvike/linq/enumerable/ElementAtOrDefaultTest.java b/src/test/java/com/bestvike/linq/enumerable/ElementAtOrDefaultTest.java index 27711c46..b6ceb30e 100644 --- a/src/test/java/com/bestvike/linq/enumerable/ElementAtOrDefaultTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/ElementAtOrDefaultTest.java @@ -1,14 +1,19 @@ package com.bestvike.linq.enumerable; +import com.bestvike.Index; import com.bestvike.TestCase; +import com.bestvike.function.Func0; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.Linq; import com.bestvike.linq.util.ArgsList; +import com.bestvike.ref; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * Created by 许崇雷 on 2019-05-07. @@ -16,53 +21,71 @@ class ElementAtOrDefaultTest extends TestCase { private static IEnumerable TestData() { ArgsList argsList = new ArgsList(); - argsList.add(NumberRangeGuaranteedNotCollectionType(9, 1), 0, 9); - argsList.add(NumberRangeGuaranteedNotCollectionType(9, 10), 9, 18); - argsList.add(NumberRangeGuaranteedNotCollectionType(-4, 10), 3, -1); - - argsList.add(Linq.of(new int[]{1, 2, 3, 4}), 4, null); - argsList.add(Linq.of(new int[0]), 0, null); - argsList.add(Linq.of(new int[]{-4}), 0, -4); - argsList.add(Linq.of(new int[]{9, 8, 0, -5, 10}), 4, 10); - - argsList.add(NumberRangeGuaranteedNotCollectionType(-4, 5), -1, null); - argsList.add(NumberRangeGuaranteedNotCollectionType(5, 5), 5, null); - argsList.add(NumberRangeGuaranteedNotCollectionType(0, 0), 0, null); + argsList.add(NumberRangeGuaranteedNotCollectionType(9, 1), 0, 1, 9); + argsList.add(NumberRangeGuaranteedNotCollectionType(9, 10), 9, 1, 18); + argsList.add(NumberRangeGuaranteedNotCollectionType(-4, 10), 3, 7, -1); + + argsList.add(Linq.of(new int[]{1, 2, 3, 4}), 4, 0, null); + argsList.add(Linq.of(new int[0]), 0, 0, null); + argsList.add(Linq.of(new int[]{-4}), 0, 1, -4); + argsList.add(Linq.of(new int[]{9, 8, 0, -5, 10}), 4, 1, 10); + + argsList.add(NumberRangeGuaranteedNotCollectionType(-4, 5), -1, 6, null); + argsList.add(NumberRangeGuaranteedNotCollectionType(5, 5), 5, 0, null); + argsList.add(NumberRangeGuaranteedNotCollectionType(0, 0), 0, 0, null); return argsList; } @Test void SameResultsRepeatCallsIntQuery() { - IEnumerable q = Linq.of(new int[]{0, 9999, 0, 888, -1, 66, -1, -777, 1, 2, -12345}) - .where(x -> x > Integer.MIN_VALUE); + List> q = Repeat(ii -> Linq.of(new int[]{0, 9999, 0, 888, -1, 66, -1, -777, 1, 2, -12345}) + .where(x -> x > Integer.MIN_VALUE), 3); - assertEquals(q.elementAtOrDefault(3), q.elementAtOrDefault(3)); + assertEquals(q.get(0).elementAtOrDefault(3), q.get(0).elementAtOrDefault(3)); + assertEquals(q.get(1).elementAtOrDefault(Index.fromStart(3)), q.get(1).elementAtOrDefault(Index.fromStart(3))); + assertEquals(q.get(2).elementAtOrDefault(Index.fromEnd(6)), q.get(2).elementAtOrDefault(Index.fromEnd(6))); } @Test void SameResultsRepeatCallsStringQuery() { - IEnumerable q = Linq.of(new String[]{"!@#$%^", "C", "AAA", "", "Calling Twice", "SoS", Empty}) - .where(x -> !IsNullOrEmpty(x)); + List> q = Repeat(ii -> Linq.of(new String[]{"!@#$%^", "C", "AAA", "", "Calling Twice", "SoS", Empty}) + .where(x -> !IsNullOrEmpty(x)), 3); - assertEquals(q.elementAtOrDefault(4), q.elementAtOrDefault(4)); + assertEquals(q.get(0).elementAtOrDefault(4), q.get(0).elementAtOrDefault(4)); + assertEquals(q.get(0).elementAtOrDefault(Index.fromStart(4)), q.get(0).elementAtOrDefault(Index.fromStart(4))); + assertEquals(q.get(0).elementAtOrDefault(Index.fromEnd(2)), q.get(0).elementAtOrDefault(Index.fromEnd(2))); } @ParameterizedTest @MethodSource("TestData") - void ElementAtOrDefault(IEnumerable source, int index, Integer expected) { + void ElementAtOrDefault(IEnumerable source, int index, int indexFromEnd, Integer expected) { assertEquals(expected, source.elementAtOrDefault(index)); + if (index >= 0) + assertEquals(expected, source.elementAtOrDefault(Index.fromStart(index))); + assertEquals(expected, source.elementAtOrDefault(Index.fromEnd(indexFromEnd))); } @ParameterizedTest @MethodSource("TestData") - void ElementAtOrDefaultRunOnce(IEnumerable source, int index, Integer expected) { + void ElementAtOrDefaultRunOnce(IEnumerable source, int index, int indexFromEnd, Integer expected) { assertEquals(expected, source.runOnce().elementAtOrDefault(index)); + if (index >= 0) + assertEquals(expected, source.runOnce().elementAtOrDefault(Index.fromStart(index))); + assertEquals(expected, source.runOnce().elementAtOrDefault(Index.fromEnd(indexFromEnd))); } @Test - void NullableArray_NegativeIndex_ReturnsNull() { + void NullableArray_InvalidIndex_ReturnsNull() { Integer[] source = {9, 8}; assertNull(Linq.of(source).elementAtOrDefault(-1)); + assertNull(Linq.of(source).elementAtOrDefault(3)); + assertNull(Linq.of(source).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(Linq.of(source).elementAtOrDefault(Integer.MIN_VALUE)); + + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(3))); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromStart(3))); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); } @Test @@ -71,11 +94,313 @@ void NullableArray_ValidIndex_ReturnsCorrectObject() { assertNull(Linq.of(source).elementAtOrDefault(2)); assertEquals(-5, Linq.of(source).elementAtOrDefault(3)); + + assertNull(Linq.of(source).elementAtOrDefault(Index.fromStart(2))); + assertEquals(-5, Linq.of(source).elementAtOrDefault(Index.fromStart(3))); + + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(3))); + assertEquals(-5, Linq.of(source).elementAtOrDefault(Index.fromEnd(2))); } @Test void NullSource_ThrowsArgumentNullException() { assertThrows(NullPointerException.class, () -> ((IEnumerable) null).elementAtOrDefault(2)); + assertThrows(NullPointerException.class, () -> ((IEnumerable) null).elementAtOrDefault(Index.fromStart(2))); + assertThrows(NullPointerException.class, () -> ((IEnumerable) null).elementAtOrDefault(Index.fromEnd(2))); + } + + @Test + void MutableSource() { + List source = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + assertEquals(2, Linq.of(source).elementAtOrDefault(2)); + assertEquals(2, Linq.of(source).elementAtOrDefault(Index.fromStart(2))); + assertEquals(2, Linq.of(source).elementAtOrDefault(Index.fromEnd(3))); + + source.addAll(3, Arrays.asList(-1, -2)); + source.remove(0); + assertEquals(-1, Linq.of(source).elementAtOrDefault(2)); + assertEquals(-1, Linq.of(source).elementAtOrDefault(Index.fromStart(2))); + assertEquals(-1, Linq.of(source).elementAtOrDefault(Index.fromEnd(4))); + } + + @Test + void MutableSourceNotList() { + List source = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + List> query1 = Repeat(ii -> ForceNotCollection(Linq.of(source)).select(i -> i), 3); + assertEquals(2, query1.get(0).elementAtOrDefault(2)); + assertEquals(2, query1.get(1).elementAtOrDefault(Index.fromStart(2))); + assertEquals(2, query1.get(2).elementAtOrDefault(Index.fromEnd(3))); + + List> query2 = Repeat(ii -> ForceNotCollection(Linq.of(source)).select(i -> i), 3); + source.addAll(3, Arrays.asList(-1, -2)); + source.remove(0); + assertEquals(-1, query2.get(0).elementAtOrDefault(2)); + assertEquals(-1, query2.get(1).elementAtOrDefault(Index.fromStart(2))); + assertEquals(-1, query2.get(2).elementAtOrDefault(Index.fromEnd(4))); + } + + @Test + void EnumerateElements() { + final int ElementCount = 10; + ref state = ref.init(-1); + ref moveNextCallCount = ref.init(0); + Func0> source = () -> + { + state.value = -1; + moveNextCallCount.value = 0; + return new DelegateIterator<>( + () -> { + moveNextCallCount.value++; + return ++state.value < ElementCount; + }, + () -> state.value, + () -> state.value = -1); + }; + + assertEquals(0, source.apply().elementAtOrDefault(0)); + assertEquals(1, moveNextCallCount.value); + assertEquals(0, source.apply().elementAtOrDefault(Index.fromStart(0))); + assertEquals(1, moveNextCallCount.value); + + assertEquals(5, source.apply().elementAtOrDefault(5)); + assertEquals(6, moveNextCallCount.value); + assertEquals(5, source.apply().elementAtOrDefault(Index.fromStart(5))); + assertEquals(6, moveNextCallCount.value); + + assertEquals(0, source.apply().elementAtOrDefault(Index.fromEnd(ElementCount))); + assertEquals(ElementCount + 1, moveNextCallCount.value); + assertEquals(5, source.apply().elementAtOrDefault(Index.fromEnd(5))); + assertEquals(ElementCount + 1, moveNextCallCount.value); + + assertNull(source.apply().elementAtOrDefault(ElementCount)); + assertEquals(ElementCount + 1, moveNextCallCount.value); + assertNull(source.apply().elementAtOrDefault(Index.fromStart(ElementCount))); + assertEquals(ElementCount + 1, moveNextCallCount.value); + assertNull(source.apply().elementAtOrDefault(Index.fromEnd(0))); + assertEquals(0, moveNextCallCount.value); + } + + @Test + void NonEmptySource_Consistency() { + Integer[] source = new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, Linq.of(source).elementAtOrDefault(5)); + assertEquals(5, Linq.of(source).elementAtOrDefault(Index.fromStart(5))); + assertEquals(5, Linq.of(source).elementAtOrDefault(Index.fromEnd(5))); + + assertEquals(0, Linq.of(source).elementAtOrDefault(0)); + assertEquals(0, Linq.of(source).elementAtOrDefault(Index.fromStart(0))); + assertEquals(0, Linq.of(source).elementAtOrDefault(Index.fromEnd(10))); + + assertEquals(9, Linq.of(source).elementAtOrDefault(9)); + assertEquals(9, Linq.of(source).elementAtOrDefault(Index.fromStart(9))); + assertEquals(9, Linq.of(source).elementAtOrDefault(Index.fromEnd(1))); + + assertNull(Linq.of(source).elementAtOrDefault(-1)); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(11))); + + assertNull(Linq.of(source).elementAtOrDefault(10)); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromStart(10))); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(0))); + + assertNull(Linq.of(source).elementAtOrDefault(Integer.MIN_VALUE)); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); + + assertNull(Linq.of(source).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void NonEmptySource_Consistency_NotList() { + Integer[] source = new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, ForceNotCollection(Linq.of(source)).elementAtOrDefault(5)); + assertEquals(5, ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromStart(5))); + assertEquals(5, ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(5))); + + assertEquals(0, ForceNotCollection(Linq.of(source)).elementAtOrDefault(0)); + assertEquals(0, ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromStart(0))); + assertEquals(0, ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(10))); + + assertEquals(9, ForceNotCollection(Linq.of(source)).elementAtOrDefault(9)); + assertEquals(9, ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromStart(9))); + assertEquals(9, ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(1))); + + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(-1)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(11))); + + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(10)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromStart(10))); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(0))); + + final int ElementCount = 10; + ref state = ref.init(-1); + ref moveNextCallCount = ref.init(0); + Func0> getSource = () -> + { + state.value = -1; + moveNextCallCount.value = 0; + return new DelegateIterator<>( + () -> { + moveNextCallCount.value++; + return ++state.value < ElementCount; + }, + () -> state.value, + () -> state.value = -1); + }; + + assertNull(getSource.apply().elementAtOrDefault(10)); + assertEquals(ElementCount + 1, moveNextCallCount.value); + assertNull(getSource.apply().elementAtOrDefault(Index.fromStart(10))); + assertEquals(ElementCount + 1, moveNextCallCount.value); + assertNull(getSource.apply().elementAtOrDefault(Index.fromEnd(0))); + assertEquals(0, moveNextCallCount.value); + + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Integer.MIN_VALUE)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); + + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void NonEmptySource_Consistency_ListPartition() { + Integer[] source = new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(5)); + assertEquals(5, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(5))); + assertEquals(5, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(5))); + + assertEquals(0, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(0)); + assertEquals(0, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(0))); + assertEquals(0, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(10))); + + assertEquals(9, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(9)); + assertEquals(9, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(9))); + assertEquals(9, ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(1))); + + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(-1)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(11))); + + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(10)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(10))); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(0))); + + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Integer.MIN_VALUE)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); + + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void NonEmptySource_Consistency_EnumerablePartition() { + Integer[] source = new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(5)); + assertEquals(5, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(5))); + assertEquals(5, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(5))); + + assertEquals(0, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(0)); + assertEquals(0, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(0))); + assertEquals(0, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(10))); + + assertEquals(9, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(9)); + assertEquals(9, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(9))); + assertEquals(9, EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(1))); + + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(-1)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(11))); + + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(10)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(10))); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(0))); + + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Integer.MIN_VALUE)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); + + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency() { + Integer[] source = new Integer[]{}; + + assertNull(Linq.of(source).elementAtOrDefault(1)); + assertNull(Linq.of(source).elementAtOrDefault(-1)); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromStart(1))); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(1))); + + assertNull(Linq.of(source).elementAtOrDefault(0)); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromStart(0))); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(0))); + + assertNull(Linq.of(source).elementAtOrDefault(Integer.MIN_VALUE)); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); + + assertNull(Linq.of(source).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(Linq.of(source).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency_NotList() { + Integer[] source = new Integer[]{}; + + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(1)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(-1)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromStart(1))); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(1))); + + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(0)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromStart(0))); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(0))); + + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Integer.MIN_VALUE)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); + + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(ForceNotCollection(Linq.of(source)).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency_ListPartition() { + Integer[] source = new Integer[]{}; + + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(1)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(-1)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(1))); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(1))); + + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(0)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(0))); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(0))); + + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Integer.MIN_VALUE)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); + + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(ListPartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency_EnumerablePartition() { + Integer[] source = new Integer[]{}; + + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(1)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(-1)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(1))); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(1))); + + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(0)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(0))); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(0))); + + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Integer.MIN_VALUE)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromEnd(Integer.MAX_VALUE))); + + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Integer.MAX_VALUE)); + assertNull(EnumerablePartitionOrEmpty(Linq.of(source)).elementAtOrDefault(Index.fromStart(Integer.MAX_VALUE))); } @Test diff --git a/src/test/java/com/bestvike/linq/enumerable/ElementAtTest.java b/src/test/java/com/bestvike/linq/enumerable/ElementAtTest.java index 137d44f1..c5157c69 100644 --- a/src/test/java/com/bestvike/linq/enumerable/ElementAtTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/ElementAtTest.java @@ -1,15 +1,20 @@ package com.bestvike.linq.enumerable; +import com.bestvike.Index; import com.bestvike.TestCase; +import com.bestvike.function.Func0; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.Linq; import com.bestvike.linq.exception.ArgumentOutOfRangeException; import com.bestvike.linq.util.ArgsList; +import com.bestvike.ref; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * Created by 许崇雷 on 2018-05-10. @@ -17,55 +22,85 @@ class ElementAtTest extends TestCase { private static IEnumerable TestData() { ArgsList argsList = new ArgsList(); - argsList.add(NumberRangeGuaranteedNotCollectionType(9, 1), 0, 9); - argsList.add(NumberRangeGuaranteedNotCollectionType(9, 10), 9, 18); - argsList.add(NumberRangeGuaranteedNotCollectionType(-4, 10), 3, -1); + argsList.add(NumberRangeGuaranteedNotCollectionType(9, 1), 0, 1, 9); + argsList.add(NumberRangeGuaranteedNotCollectionType(9, 10), 9, 1, 18); + argsList.add(NumberRangeGuaranteedNotCollectionType(-4, 10), 3, 7, -1); - argsList.add(Linq.of(new int[]{-4}), 0, -4); - argsList.add(Linq.of(new int[]{9, 8, 0, -5, 10}), 4, 10); + argsList.add(Linq.of(new int[]{-4}), 0, 1, -4); + argsList.add(Linq.of(new int[]{9, 8, 0, -5, 10}), 4, 1, 10); return argsList; } @Test void SameResultsRepeatCallsIntQuery() { - IEnumerable q = Linq.of(new int[]{0, 9999, 0, 888, -1, 66, -1, -777, 1, 2, -12345}) - .where(x -> x > Integer.MIN_VALUE); + List> q = Repeat(ii -> Linq.of(new int[]{0, 9999, 0, 888, -1, 66, -1, -777, 1, 2, -12345}) + .where(x -> x > Integer.MIN_VALUE), 3); - assertEquals(q.elementAt(3), q.elementAt(3)); + assertEquals(q.get(0).elementAt(3), q.get(1).elementAt(3)); + assertEquals(q.get(1).elementAt(Index.fromStart(3)), q.get(1).elementAt(Index.fromStart(3))); + assertEquals(q.get(2).elementAt(Index.fromEnd(6)), q.get(2).elementAt(Index.fromEnd(6))); } @Test void SameResultsRepeatCallsStringQuery() { - IEnumerable q = Linq.of(new String[]{"!@#$%^", "C", "AAA", "", "Calling Twice", "SoS", Empty}) - .where(x -> !IsNullOrEmpty(x)); + List> q = Repeat(ii -> Linq.of(new String[]{"!@#$%^", "C", "AAA", "", "Calling Twice", "SoS", Empty}) + .where(x -> !IsNullOrEmpty(x)), 3); - assertEquals(q.elementAt(4), q.elementAt(4)); + assertEquals(q.get(0).elementAt(4), q.get(1).elementAt(4)); + assertEquals(q.get(1).elementAt(Index.fromStart(4)), q.get(1).elementAt(Index.fromStart(4))); + assertEquals(q.get(2).elementAt(Index.fromEnd(2)), q.get(2).elementAt(Index.fromEnd(2))); } @ParameterizedTest @MethodSource("TestData") - void ElementAt(IEnumerable source, int index, Integer expected) { + void ElementAt(IEnumerable source, int index, int indexFromEnd, Integer expected) { assertEquals(expected, source.elementAt(index)); + assertEquals(expected, source.elementAt(Index.fromStart(index))); + assertEquals(expected, source.elementAt(Index.fromEnd(indexFromEnd))); } @ParameterizedTest @MethodSource("TestData") - void ElementAtRunOnce(IEnumerable source, int index, Integer expected) { + void ElementAtRunOnce(IEnumerable source, int index, int indexFromEnd, Integer expected) { assertEquals(expected, source.runOnce().elementAt(index)); + assertEquals(expected, source.runOnce().elementAt(Index.fromStart(index))); + assertEquals(expected, source.runOnce().elementAt(Index.fromEnd(indexFromEnd))); } @Test void InvalidIndex_ThrowsArgumentOutOfRangeException() { assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new Integer[]{9, 8}).elementAt(-1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new Integer[]{9, 8}).elementAt(Index.fromEnd(3))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new Integer[]{9, 8}).elementAt(Integer.MAX_VALUE)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new Integer[]{9, 8}).elementAt(Integer.MIN_VALUE)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new Integer[]{9, 8}).elementAt(Index.fromStart(Integer.MAX_VALUE))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new Integer[]{9, 8}).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new int[]{1, 2, 3, 4}).elementAt(4)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new int[]{1, 2, 3, 4}).elementAt(Index.fromStart(4))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new int[]{1, 2, 3, 4}).elementAt(Index.fromEnd(0))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new int[]{1, 2, 3, 4}).elementAt(Index.fromEnd(5))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new int[0]).elementAt(0)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new int[0]).elementAt(Index.fromStart(0))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new int[0]).elementAt(Index.fromEnd(0))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(new int[0]).elementAt(Index.fromEnd(1))); assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(-4, 5).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(-4, 5).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(-4, 5).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(-4, 5).elementAt(Index.fromEnd(6))); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(-4, 5).elementAt(Index.fromStart(Integer.MAX_VALUE))); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(-4, 5).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(5, 5).elementAt(5)); - assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(0, 0).elementAt(0)); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(5, 5).elementAt(Index.fromStart(5))); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(5, 5).elementAt(Index.fromEnd(0))); - assertThrows(ArgumentOutOfRangeException.class, () -> Linq.singleton(true).elementAt(1)); - assertThrows(ArgumentOutOfRangeException.class, () -> Linq.singleton(true).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(0, 0).elementAt(0)); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(0, 0).elementAt(Index.fromStart(0))); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(0, 0).elementAt(Index.fromEnd(0))); + assertThrows(ArgumentOutOfRangeException.class, () -> NumberRangeGuaranteedNotCollectionType(0, 0).elementAt(Index.fromEnd(1))); } @Test @@ -74,11 +109,427 @@ void NullableArray_ValidIndex_ReturnsCorrectObject() { assertNull(Linq.of(source).elementAt(2)); assertEquals(-5, Linq.of(source).elementAt(3)); + + assertNull(Linq.of(source).elementAt(Index.fromStart(2))); + assertEquals(-5, Linq.of(source).elementAt(Index.fromStart(3))); + + assertNull(Linq.of(source).elementAt(Index.fromEnd(3))); + assertEquals(-5, Linq.of(source).elementAt(Index.fromEnd(2))); } @Test void NullSource_ThrowsArgumentNullException() { assertThrows(NullPointerException.class, () -> ((IEnumerable) null).elementAt(2)); + assertThrows(NullPointerException.class, () -> ((IEnumerable) null).elementAt(Index.fromStart(2))); + assertThrows(NullPointerException.class, () -> ((IEnumerable) null).elementAt(Index.fromEnd(2))); + } + + @Test + void MutableSource() { + List source = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + assertEquals(2, Linq.of(source).elementAt(2)); + assertEquals(2, Linq.of(source).elementAt(Index.fromStart(2))); + assertEquals(2, Linq.of(source).elementAt(Index.fromEnd(3))); + + source.addAll(3, Arrays.asList(-1, -2)); + source.remove(0); + assertEquals(-1, Linq.of(source).elementAt(2)); + assertEquals(-1, Linq.of(source).elementAt(Index.fromStart(2))); + assertEquals(-1, Linq.of(source).elementAt(Index.fromEnd(4))); + } + + @Test + void MutableSourceNotList() { + List source = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + List> query1 = Repeat(ii -> ForceNotCollection(Linq.of(source)).select(i -> i), 3); + assertEquals(2, query1.get(0).elementAt(2)); + assertEquals(2, query1.get(1).elementAt(Index.fromStart(2))); + assertEquals(2, query1.get(2).elementAt(Index.fromEnd(3))); + + List> query2 = Repeat(ii -> ForceNotCollection(Linq.of(source)).select(i -> i), 3); + source.addAll(3, Arrays.asList(-1, -2)); + source.remove(0); + assertEquals(-1, query2.get(0).elementAt(2)); + assertEquals(-1, query2.get(1).elementAt(Index.fromStart(2))); + assertEquals(-1, query2.get(2).elementAt(Index.fromEnd(4))); + } + + @Test + void EnumerateElements() { + final int ElementCount = 10; + ref state = ref.init(-1); + ref moveNextCallCount = ref.init(0); + Func0> source = () -> + { + state.value = -1; + moveNextCallCount.value = 0; + return new DelegateIterator<>( + () -> { + moveNextCallCount.value++; + return ++state.value < ElementCount; + }, + () -> state.value, + () -> state.value = -1); + }; + + assertEquals(0, source.apply().elementAt(0)); + assertEquals(1, moveNextCallCount.value); + assertEquals(0, source.apply().elementAt(Index.fromStart(0))); + assertEquals(1, moveNextCallCount.value); + + assertEquals(5, source.apply().elementAt(5)); + assertEquals(6, moveNextCallCount.value); + assertEquals(5, source.apply().elementAt(Index.fromStart(5))); + assertEquals(6, moveNextCallCount.value); + + assertEquals(0, source.apply().elementAt(Index.fromEnd(ElementCount))); + assertEquals(ElementCount + 1, moveNextCallCount.value); + assertEquals(5, source.apply().elementAt(Index.fromEnd(5))); + assertEquals(ElementCount + 1, moveNextCallCount.value); + + assertThrows(ArgumentOutOfRangeException.class, () -> source.apply().elementAt(ElementCount)); + assertEquals(ElementCount + 1, moveNextCallCount.value); + assertThrows(ArgumentOutOfRangeException.class, () -> source.apply().elementAt(Index.fromStart(ElementCount))); + assertEquals(ElementCount + 1, moveNextCallCount.value); + assertThrows(ArgumentOutOfRangeException.class, () -> source.apply().elementAt(Index.fromEnd(0))); + assertEquals(0, moveNextCallCount.value); + } + + @Test + void NonEmptySource_Consistency() { + int[] source = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, Linq.of(source).elementAt(5)); + assertEquals(5, Linq.of(source).elementAt(Index.fromStart(5))); + assertEquals(5, Linq.of(source).elementAt(Index.fromEnd(5))); + + assertEquals(0, Linq.of(source).elementAt(0)); + assertEquals(0, Linq.of(source).elementAt(Index.fromStart(0))); + assertEquals(0, Linq.of(source).elementAt(Index.fromEnd(10))); + + assertEquals(9, Linq.of(source).elementAt(9)); + assertEquals(9, Linq.of(source).elementAt(Index.fromStart(9))); + assertEquals(9, Linq.of(source).elementAt(Index.fromEnd(1))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(-1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(11))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(10)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromStart(10))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(0))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Integer.MIN_VALUE)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Integer.MAX_VALUE)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void NonEmptySource_Consistency_ThrowsIListIndexerException() { + Integer[] source = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(-1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(11))); + // ImmutableArray implements IList. ElementAt calls ImmutableArray's indexer, which throws IndexOutOfRangeException instead of ArgumentOutOfRangeException. + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Linq.of(Arrays.asList(source))).elementAt(-1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(Index.fromEnd(11))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(10)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromStart(10))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(0))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(10)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(Index.fromStart(10))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(Index.fromEnd(0))); + } + + @Test + void NonEmptySource_Consistency_NotList() { + int[] source = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, ForceNotCollection(Linq.of(source)).elementAt(5)); + assertEquals(5, ForceNotCollection(Linq.of(source)).elementAt(Index.fromStart(5))); + assertEquals(5, ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(5))); + + assertEquals(0, ForceNotCollection(Linq.of(source)).elementAt(0)); + assertEquals(0, ForceNotCollection(Linq.of(source)).elementAt(Index.fromStart(0))); + assertEquals(0, ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(10))); + + assertEquals(9, ForceNotCollection(Linq.of(source)).elementAt(9)); + assertEquals(9, ForceNotCollection(Linq.of(source)).elementAt(Index.fromStart(9))); + assertEquals(9, ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(11))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(10)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromStart(10))); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void NonEmptySource_Consistency_ListPartition() { + int[] source = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, ListPartitionOrEmpty(Linq.of(source)).elementAt(5)); + assertEquals(5, ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(5))); + assertEquals(5, ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(5))); + + assertEquals(0, ListPartitionOrEmpty(Linq.of(source)).elementAt(0)); + assertEquals(0, ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(0))); + assertEquals(0, ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(10))); + + assertEquals(9, ListPartitionOrEmpty(Linq.of(source)).elementAt(9)); + assertEquals(9, ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(9))); + assertEquals(9, ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(11))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(10)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(10))); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void NonEmptySource_Consistency_EnumerablePartition() { + int[] source = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(5)); + assertEquals(5, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(5))); + assertEquals(5, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(5))); + + assertEquals(0, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(0)); + assertEquals(0, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(0))); + assertEquals(0, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(10))); + + assertEquals(9, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(9)); + assertEquals(9, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(9))); + assertEquals(9, EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(11))); + + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(10)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(10))); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void NonEmptySource_Consistency_Collection() { + Integer[] source = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, new TestCollection<>(source).elementAt(5)); + assertEquals(5, new TestCollection<>(source).elementAt(Index.fromStart(5))); + assertEquals(5, new TestCollection<>(source).elementAt(Index.fromEnd(5))); + + assertEquals(0, new TestCollection<>(source).elementAt(0)); + assertEquals(0, new TestCollection<>(source).elementAt(Index.fromStart(0))); + assertEquals(0, new TestCollection<>(source).elementAt(Index.fromEnd(10))); + + assertEquals(9, new TestCollection<>(source).elementAt(9)); + assertEquals(9, new TestCollection<>(source).elementAt(Index.fromStart(9))); + assertEquals(9, new TestCollection<>(source).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromEnd(11))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(10)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromStart(10))); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void NonEmptySource_Consistency_NonGenericCollection() { + int[] source = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assertEquals(5, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(5)); + assertEquals(5, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromStart(5))); + assertEquals(5, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(5))); + + assertEquals(0, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(0)); + assertEquals(0, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromStart(0))); + assertEquals(0, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(10))); + + assertEquals(9, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(9)); + assertEquals(9, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromStart(9))); + assertEquals(9, new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(11))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(10)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromStart(10))); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency() { + int[] source = {}; + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(-1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromStart(1))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(1))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(0)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromStart(0))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(0))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Integer.MIN_VALUE)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Integer.MAX_VALUE)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency_ThrowsIListIndexerException() { + Integer[] source = {}; + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(-1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(1))); + // ImmutableArray implements IList. ElementAt calls ImmutableArray's indexer, which throws IndexOutOfRangeException instead of ArgumentOutOfRangeException. + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(-1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(Index.fromEnd(1))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(0)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromEnd(0))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(0)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(Index.fromEnd(0))); + + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(source).elementAt(Index.fromStart(1))); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> Linq.of(Arrays.asList(source)).elementAt(Index.fromStart(1))); + } + + @Test + void EmptySource_Consistency_NotList() { + int[] source = {}; + + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(1)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromStart(1))); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(0)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromStart(0))); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> ForceNotCollection(Linq.of(source)).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency_ListPartition() { + int[] source = {}; + + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(1)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(1))); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(0)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(0))); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> ListPartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency_EnumerablePartition() { + int[] source = {}; + + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(1)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(1))); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(0)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(0))); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> EnumerablePartitionOrEmpty(Linq.of(source)).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency_Collection() { + Integer[] source = {}; + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(1)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromStart(1))); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(0)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromStart(0))); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(source).elementAt(Index.fromStart(Integer.MAX_VALUE))); + } + + @Test + void EmptySource_Consistency_NonGenericCollection() { + int[] source = {}; + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(1)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromStart(1))); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(1))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(0)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromStart(0))); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(0))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Integer.MIN_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromEnd(Integer.MAX_VALUE))); + + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Integer.MAX_VALUE)); + assertThrows(ArgumentOutOfRangeException.class, () -> new TestCollection<>(Linq.of(source).toArray(Integer.class)).elementAt(Index.fromStart(Integer.MAX_VALUE))); } @Test diff --git a/src/test/java/com/bestvike/linq/enumerable/ExceptByTest.java b/src/test/java/com/bestvike/linq/enumerable/ExceptByTest.java index 2c9ed7e7..55318a13 100644 --- a/src/test/java/com/bestvike/linq/enumerable/ExceptByTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/ExceptByTest.java @@ -4,12 +4,15 @@ import com.bestvike.collections.generic.EqualityComparer; import com.bestvike.collections.generic.IEqualityComparer; import com.bestvike.collections.generic.StringComparer; +import com.bestvike.function.Func1; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.IEnumerator; import com.bestvike.linq.Linq; import com.bestvike.linq.entity.Employee; import com.bestvike.linq.exception.ArgumentNullException; import com.bestvike.linq.util.ArgsList; +import com.bestvike.tuple.Tuple; +import com.bestvike.tuple.Tuple2; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -18,6 +21,73 @@ * Created by 许崇雷 on 2018-05-10. */ class ExceptByTest extends TestCase { + private static Object[] WrapArgs(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + return new Object[]{first, second, keySelector, comparer, expected}; + } + + private static IEnumerable TestData() { + ArgsList argsList = new ArgsList(); + + argsList.add(WrapArgs( + Linq.range(0, 10), + Linq.range(0, 5), + x -> x, + null, + Linq.range(5, 5))); + + argsList.add(WrapArgs( + Linq.repeat(5, 20), + Linq.empty(), + x -> x, + null, + Linq.repeat(5, 1))); + + argsList.add(WrapArgs( + Linq.repeat(5, 20), + Linq.repeat(5, 3), + x -> x, + null, + Linq.empty())); + + argsList.add(WrapArgs( + Linq.of("Bob", "Tim", "Robert", "Chris"), + Linq.of("bBo", "shriC"), + x -> x, + null, + Linq.of("Bob", "Tim", "Robert", "Chris"))); + + argsList.add(WrapArgs( + Linq.of("Bob", "Tim", "Robert", "Chris"), + Linq.of("bBo", "shriC"), + x -> x, + new AnagramEqualityComparer(), + Linq.of("Tim", "Robert"))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)), + Linq.of(new int[]{15, 20, 40}), + Tuple2::getItem2, + null, + Linq.of(Tuple.create("Dick", 30)))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)), + Linq.of("moT"), + Tuple2::getItem1, + null, + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)), + Linq.of("moT"), + Tuple2::getItem1, + new AnagramEqualityComparer(), + Linq.of(Tuple.create("Dick", 30), Tuple.create("Harry", 40)))); + + return argsList; + + } + private static IEnumerable Int_TestData() { ArgsList argsList = new ArgsList(); argsList.add(Linq.of(new int[0]), Linq.of(new int[0]), null, Linq.of(new int[0])); @@ -46,6 +116,46 @@ private static IEnumerable NullableInt_TestData() { return argsList; } + @Test + void FirstNull_ThrowsArgumentNullException() { + IEnumerable first = null; + IEnumerable second = Linq.of("bBo", "shriC"); + + assertThrows(NullPointerException.class, () -> first.exceptBy(second, x -> x)); + assertThrows(NullPointerException.class, () -> first.exceptBy(second, x -> x, new AnagramEqualityComparer())); + } + + @Test + void SecondNull_ThrowsArgumentNullException() { + IEnumerable first = Linq.of("Bob", "Tim", "Robert", "Chris"); + IEnumerable second = null; + + assertThrows(ArgumentNullException.class, () -> first.exceptBy(second, x -> x)); + assertThrows(ArgumentNullException.class, () -> first.exceptBy(second, x -> x, new AnagramEqualityComparer())); + } + + @Test + void KeySelectorNull_ThrowsArgumentNullException() { + IEnumerable first = Linq.of("Bob", "Tim", "Robert", "Chris"); + IEnumerable second = Linq.of("bBo", "shriC"); + Func1 keySelector = null; + + assertThrows(ArgumentNullException.class, () -> first.exceptBy(second, keySelector)); + assertThrows(ArgumentNullException.class, () -> first.exceptBy(second, keySelector, new AnagramEqualityComparer())); + } + + @ParameterizedTest + @MethodSource("TestData") + void HasExpectedOutput(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + assertEquals(expected, first.exceptBy(second, keySelector, comparer)); + } + + @ParameterizedTest + @MethodSource("TestData") + void RunOnce_HasExpectedOutput(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + assertEquals(expected, first.runOnce().exceptBy(second.runOnce(), keySelector, comparer)); + } + @Test void SameResultsRepeatCallsIntQuery() { IEnumerable q1 = Linq.of(2, 3, null, 2, null, 4, 5); @@ -59,9 +169,6 @@ void SameResultsRepeatCallsStringQuery() { IEnumerable q1 = Linq.of("AAA", Empty, "q", "C", "#", "!@#$%^", "0987654321", "Calling Twice"); IEnumerable q2 = Linq.of("!@#$%^", "C", "AAA", "", "Calling Twice", "SoS"); - q1.exceptBy(q2, x -> x); - q1.exceptBy(q2, x -> x); - assertEquals(q1.exceptBy(q2, x -> x), q1.exceptBy(q2, x -> x)); } @@ -95,24 +202,6 @@ void NullableIntRunOnce(IEnumerable first, IEnumerable second, assertEquals(expected, first.runOnce().exceptBy(second.runOnce(), x -> x)); } - @Test - void FirstNull_ThrowsArgumentNullException() { - IEnumerable first = null; - IEnumerable second = Linq.of("bBo", "shriC"); - - assertThrows(NullPointerException.class, () -> first.exceptBy(second, x -> x)); - assertThrows(NullPointerException.class, () -> first.exceptBy(second, x -> x, new AnagramEqualityComparer())); - } - - @Test - void SecondNull_ThrowsArgumentNullException() { - IEnumerable first = Linq.of("Bob", "Tim", "Robert", "Chris"); - IEnumerable second = null; - - assertThrows(ArgumentNullException.class, () -> first.exceptBy(second, x -> x)); - assertThrows(ArgumentNullException.class, () -> first.exceptBy(second, x -> x, new AnagramEqualityComparer())); - } - @Test void ForcedToEnumeratorDoesntEnumerate() { IEnumerable iterator = NumberRangeGuaranteedNotCollectionType(0, 3).exceptBy(Linq.range(0, 3), x -> x); @@ -144,7 +233,7 @@ void testExceptBy() { emps[3], }; assertEquals(1, Linq.of(emps) - .exceptBy(Linq.of(emps2), emp -> emp.deptno) + .exceptBy(Linq.of(emps2).select(e -> e.deptno), emp -> emp.deptno) .count()); IEnumerable oneToHundred = Linq.range(1, 100); @@ -180,7 +269,7 @@ public int hashCode(Integer obj) { Employee[] emps2 = {new Employee(150, "Theodore", 10)}; assertEquals(0, Linq.of(emps) - .exceptBy(Linq.of(emps2), emp -> emp.deptno, comparer) + .exceptBy(Linq.of(emps2).select(e -> e.deptno), emp -> emp.deptno, comparer) .count()); } } diff --git a/src/test/java/com/bestvike/linq/enumerable/ExceptTest.java b/src/test/java/com/bestvike/linq/enumerable/ExceptTest.java index e0560bd2..2c0ee41d 100644 --- a/src/test/java/com/bestvike/linq/enumerable/ExceptTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/ExceptTest.java @@ -61,9 +61,6 @@ void SameResultsRepeatCallsStringQuery() { IEnumerable q1 = Linq.of("AAA", Empty, "q", "C", "#", "!@#$%^", "0987654321", "Calling Twice"); IEnumerable q2 = Linq.of("!@#$%^", "C", "AAA", "", "Calling Twice", "SoS"); - q1.except(q2); - q1.except(q2); - assertEquals(q1.except(q2), q1.except(q2)); } diff --git a/src/test/java/com/bestvike/linq/enumerable/FirstOrDefaultTest.java b/src/test/java/com/bestvike/linq/enumerable/FirstOrDefaultTest.java index 257268f1..36ebf6c7 100644 --- a/src/test/java/com/bestvike/linq/enumerable/FirstOrDefaultTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/FirstOrDefaultTest.java @@ -1,6 +1,7 @@ package com.bestvike.linq.enumerable; import com.bestvike.TestCase; +import com.bestvike.collections.generic.Array; import com.bestvike.collections.generic.IList; import com.bestvike.function.Predicate1; import com.bestvike.linq.IEnumerable; @@ -35,6 +36,18 @@ private static void TestEmptyNotIList() { assertEquals(expected, source.runOnce().firstOrDefault()); } + private static void TestEmptyIListDefault(T defaultValue) { + Array source = Array.empty(); + assertIsAssignableFrom(IList.class, source); + assertEquals(defaultValue, source.runOnce().firstOrDefault(defaultValue)); + } + + private static void TestEmptyNotIListDefault(T defaultValue) { + IEnumerable source = Linq.empty(); + assertNull(as(source, IList.class)); + assertEquals(defaultValue, source.runOnce().firstOrDefault(defaultValue)); + } + @Test void SameResultsRepeatCallsIntQuery() { IEnumerable ieInt = Linq.range(0, 0); @@ -59,6 +72,16 @@ void EmptyIListT() { TestEmptyIList(FirstOrDefaultTest.class); } + @Test + void EmptyIListDefault() { + TestEmptyIListDefault(5); // int + TestEmptyIListDefault("Hello"); // string + TestEmptyIListDefault(new Date()); //DateTime + TestEmptyNotIListDefault(5); // int + TestEmptyNotIListDefault("Hello"); // string + TestEmptyNotIListDefault(new Date()); //DateTime + } + @Test void IListTOneElement() { int[] source = new int[]{5}; @@ -68,6 +91,15 @@ void IListTOneElement() { assertEquals(expected, Linq.of(source).firstOrDefault()); } + @Test + void IListOneElementDefault() { + int[] source = new int[]{5}; + int expected = 5; + + assertIsAssignableFrom(IList.class, Linq.of(source)); + assertEquals(expected, Linq.of(source).firstOrDefault(3)); + } + @Test void IListTManyElementsFirstIsDefault() { Integer[] source = {null, -10, 2, 4, 3, 0, 2}; @@ -129,6 +161,15 @@ void OneElementTruePredicate() { assertEquals(expected, Linq.of(source).firstOrDefault(predicate)); } + @Test + void OneElementTruePredicateDefault() { + int[] source = {4}; + Predicate1 predicate = TestCase::IsEven; + int expected = 4; + + assertEquals(expected, Linq.of(source).firstOrDefault(predicate, 5)); + } + @Test void ManyElementsPredicateFalseForAll() { int[] source = {9, 5, 1, 3, 17, 21}; @@ -138,6 +179,15 @@ void ManyElementsPredicateFalseForAll() { assertEquals(expected, Linq.of(source).firstOrDefault(predicate)); } + @Test + void ManyElementsPredicateFalseForAllDefault() { + int[] source = {9, 5, 1, 3, 17, 21}; + Predicate1 predicate = TestCase::IsEven; + int expected = 5; + + assertEquals(expected, Linq.of(source).firstOrDefault(predicate, 5)); + } + @Test void PredicateTrueOnlyForLast() { int[] source = {9, 5, 1, 3, 17, 21, 50}; @@ -147,6 +197,15 @@ void PredicateTrueOnlyForLast() { assertEquals(expected, Linq.of(source).firstOrDefault(predicate)); } + @Test + void PredicateTrueOnlyForLastDefault() { + int[] source = {9, 5, 1, 3, 17, 21, 50}; + Predicate1 predicate = TestCase::IsEven; + int expected = 50; + + assertEquals(expected, Linq.of(source).firstOrDefault(predicate, 5)); + } + @Test void PredicateTrueForSome() { int[] source = {3, 7, 10, 7, 9, 2, 11, 17, 13, 8}; @@ -156,6 +215,15 @@ void PredicateTrueForSome() { assertEquals(expected, Linq.of(source).firstOrDefault(predicate)); } + @Test + void PredicateTrueForSomeDefault() { + int[] source = {3, 7, 10, 7, 9, 2, 11, 17, 13, 8}; + Predicate1 predicate = TestCase::IsEven; + int expected = 10; + + assertEquals(expected, Linq.of(source).firstOrDefault(predicate, 5)); + } + @Test void PredicateTrueForSomeRunOnce() { int[] source = {3, 7, 10, 7, 9, 2, 11, 17, 13, 8}; @@ -168,17 +236,20 @@ void PredicateTrueForSomeRunOnce() { @Test void NullSource() { assertThrows(NullPointerException.class, () -> ((IEnumerable) null).firstOrDefault()); + assertThrows(NullPointerException.class, () -> ((IEnumerable) null).firstOrDefault(5)); } @Test void NullSourcePredicateUsed() { assertThrows(NullPointerException.class, () -> ((IEnumerable) null).firstOrDefault(i -> i != 2)); + assertThrows(NullPointerException.class, () -> ((IEnumerable) null).firstOrDefault(i -> i != 2, 5)); } @Test void NullPredicate() { Predicate1 predicate = null; assertThrows(ArgumentNullException.class, () -> Linq.range(0, 3).firstOrDefault(predicate)); + assertThrows(ArgumentNullException.class, () -> Linq.range(0, 3).firstOrDefault(predicate, 5)); } @Test diff --git a/src/test/java/com/bestvike/linq/enumerable/IntersectByTest.java b/src/test/java/com/bestvike/linq/enumerable/IntersectByTest.java index 8f15d89d..1a6b734b 100644 --- a/src/test/java/com/bestvike/linq/enumerable/IntersectByTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/IntersectByTest.java @@ -4,6 +4,7 @@ import com.bestvike.collections.generic.EqualityComparer; import com.bestvike.collections.generic.IEqualityComparer; import com.bestvike.collections.generic.StringComparer; +import com.bestvike.function.Func1; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.IEnumerator; import com.bestvike.linq.Linq; @@ -11,6 +12,8 @@ import com.bestvike.linq.exception.ArgumentNullException; import com.bestvike.linq.util.ArgsList; import com.bestvike.linq.util.HashSet; +import com.bestvike.tuple.Tuple; +import com.bestvike.tuple.Tuple2; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -19,6 +22,78 @@ * Created by 许崇雷 on 2018-05-10. */ class IntersectByTest extends TestCase { + private static Object[] WrapArgs(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + return new Object[]{first, second, keySelector, comparer, expected}; + } + + private static IEnumerable TestData() { + ArgsList argsList = new ArgsList(); + argsList.add(WrapArgs( + Linq.range(0, 10), + Linq.range(0, 5), + x -> x, + null, + Linq.range(0, 5))); + + argsList.add(WrapArgs( + Linq.range(0, 10), + Linq.range(10, 10), + x -> x, + null, + Linq.empty())); + + argsList.add(WrapArgs( + Linq.repeat(5, 20), + Linq.empty(), + x -> x, + null, + Linq.empty())); + + argsList.add(WrapArgs( + Linq.repeat(5, 20), + Linq.repeat(5, 3), + x -> x, + null, + Linq.repeat(5, 1))); + + argsList.add(WrapArgs( + Linq.of("Bob", "Tim", "Robert", "Chris"), + Linq.of("bBo", "shriC"), + x -> x, + null, + Linq.empty())); + + argsList.add(WrapArgs( + Linq.of("Bob", "Tim", "Robert", "Chris"), + Linq.of("bBo", "shriC"), + x -> x, + new AnagramEqualityComparer(), + Linq.of("Bob", "Chris"))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)), + Linq.of(new int[]{15, 20, 40}), + Tuple2::getItem2, + null, + Linq.of(Tuple.create("Tom", 20), Tuple.create("Harry", 40)))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)), + Linq.of(new String[]{"moT"}), + Tuple2::getItem2, + null, + Linq.empty())); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40)), + Linq.of("moT"), + Tuple2::getItem1, + new AnagramEqualityComparer(), + Linq.of(Tuple.create("Tom", 20)))); + + return argsList; + } + private static IEnumerable Int_TestData() { ArgsList argsList = new ArgsList(); argsList.add(Linq.of(new int[0]), Linq.of(new int[0]), new int[0]); @@ -48,6 +123,46 @@ private static IEnumerable NullableInt_TestData() { return argsList; } + @Test + void FirstNull_ThrowsArgumentNullException() { + IEnumerable first = null; + IEnumerable second = Linq.of("bBo", "shriC"); + + assertThrows(NullPointerException.class, () -> first.intersectBy(second, x -> x)); + assertThrows(NullPointerException.class, () -> first.intersectBy(second, x -> x, new AnagramEqualityComparer())); + } + + @Test + void SecondNull_ThrowsArgumentNullException() { + IEnumerable first = Linq.of("Bob", "Tim", "Robert", "Chris"); + IEnumerable second = null; + + assertThrows(ArgumentNullException.class, () -> first.intersectBy(second, x -> x)); + assertThrows(ArgumentNullException.class, () -> first.intersectBy(second, x -> x, new AnagramEqualityComparer())); + } + + @Test + void KeySelectorNull_ThrowsArgumentNullException() { + IEnumerable first = Linq.of("Bob", "Tim", "Robert", "Chris"); + IEnumerable second = Linq.of("bBo", "shriC"); + Func1 keySelector = null; + + assertThrows(ArgumentNullException.class, () -> first.intersectBy(second, keySelector)); + assertThrows(ArgumentNullException.class, () -> first.intersectBy(second, keySelector, new AnagramEqualityComparer())); + } + + @ParameterizedTest + @MethodSource("TestData") + void HasExpectedOutput(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + assertEquals(expected, first.intersectBy(second, keySelector, comparer)); + } + + @ParameterizedTest + @MethodSource("TestData") + void RunOnce_HasExpectedOutput(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + assertEquals(expected, first.runOnce().intersectBy(second.runOnce(), keySelector, comparer)); + } + @Test void SameResultsRepeatCallsIntQuery() { IEnumerable first = Linq.of(2, 3, null, 2, null, 4, 5); @@ -94,24 +209,6 @@ void NullableIntRunOnce(IEnumerable first, IEnumerable second, assertEquals(Linq.of(expected), first.runOnce().intersectBy(second.runOnce(), x -> x, null)); } - @Test - void FirstNull_ThrowsArgumentNullException() { - IEnumerable first = null; - IEnumerable second = Linq.of("ekiM", "bBo"); - - assertThrows(NullPointerException.class, () -> first.intersectBy(second, x -> x)); - assertThrows(NullPointerException.class, () -> first.intersectBy(second, x -> x, new AnagramEqualityComparer())); - } - - @Test - void SecondNull_ThrowsArgumentNullException() { - IEnumerable first = Linq.of("Tim", "Bob", "Mike", "Robert"); - IEnumerable second = null; - - assertThrows(ArgumentNullException.class, () -> first.intersectBy(second, x -> x)); - assertThrows(ArgumentNullException.class, () -> first.intersectBy(second, x -> x, new AnagramEqualityComparer())); - } - @Test void ForcedToEnumeratorDoesntEnumerate() { IEnumerable iterator = NumberRangeGuaranteedNotCollectionType(0, 3).intersectBy(Linq.range(0, 3), x -> x); @@ -146,7 +243,7 @@ void testIntersectBy() { emps[3], }; assertEquals(2, Linq.of(emps) - .intersectBy(Linq.of(emps2), emp -> emp.deptno) + .intersectBy(Linq.of(emps2).select(e -> e.deptno), emp -> emp.deptno) .count()); assertThrows(NullPointerException.class, () -> ((IEnumerable) null).intersectBy(Linq.empty(), x -> x)); @@ -179,7 +276,7 @@ public int hashCode(Integer obj) { new Employee(150, "Theodore", 10) }; assertEquals(1, Linq.of(emps) - .intersectBy(Linq.of(emps2), emp -> emp.deptno, comparer) + .intersectBy(Linq.of(emps2).select(e -> e.deptno), emp -> emp.deptno, comparer) .count()); } } diff --git a/src/test/java/com/bestvike/linq/enumerable/LastOrDefaultTest.java b/src/test/java/com/bestvike/linq/enumerable/LastOrDefaultTest.java index 56c9f36d..ac9fe669 100644 --- a/src/test/java/com/bestvike/linq/enumerable/LastOrDefaultTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/LastOrDefaultTest.java @@ -1,6 +1,7 @@ package com.bestvike.linq.enumerable; import com.bestvike.TestCase; +import com.bestvike.collections.generic.Array; import com.bestvike.collections.generic.IList; import com.bestvike.function.Predicate1; import com.bestvike.linq.IEnumerable; @@ -36,6 +37,12 @@ private static void TestEmptyNotIList() { assertEquals(expected, source.runOnce().lastOrDefault()); } + private static void TestEmptyIListDefault(T defaultValue) { + Array source = Array.empty(); + assertIsAssignableFrom(IList.class, source); + assertEquals(defaultValue, source.runOnce().lastOrDefault(defaultValue)); + } + @Test void SameResultsrepeatCallsIntQuery() { IEnumerable q = Linq.of(new int[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}) @@ -60,6 +67,13 @@ void EmptyIListT() { LastOrDefaultTest.TestEmptyIList(); } + @Test + void EmptyIList() { + TestEmptyIListDefault(5); // int + TestEmptyIListDefault("Hello"); // string + TestEmptyIListDefault(new Date()); + } + @Test void IListTOneElement() { IEnumerable source = Linq.of(new int[]{5}); @@ -69,6 +83,15 @@ void IListTOneElement() { assertEquals(expected, source.lastOrDefault()); } + @Test + void IListTOneElementDefault() { + int[] source = new int[]{5}; + int expected = 5; + + assertIsAssignableFrom(IList.class, Linq.of(source)); + assertEquals(expected, Linq.of(source).lastOrDefault(4)); + } + @Test void IListTManyElementsLastIsDefault() { IEnumerable source = Linq.of(-10, 2, 4, 3, 0, 2, null); @@ -87,6 +110,24 @@ void IListTManyElementsLastIsNotDefault() { assertEquals(expected, source.lastOrDefault()); } + @Test + void IListTManyElementsLastHasDefault() { + Integer[] source = {-10, 2, 4, 3, 0, 2, null}; + Integer expected = null; + + assertIsAssignableFrom(IList.class, Linq.of(source)); + assertEquals(expected, Linq.of(source).lastOrDefault(5)); + } + + @Test + void IListTManyElementsLastIsHasDefault() { + Integer[] source = {-10, 2, 4, 3, 0, 2, null, 19}; + Integer expected = 19; + + assertIsAssignableFrom(IList.class, Linq.of(source)); + assertEquals(expected, Linq.of(source).lastOrDefault(5)); + } + @Test void EmptyNotIListT() { LastOrDefaultTest.TestEmptyNotIList(); @@ -130,6 +171,15 @@ void OneElementIListTruePredicate() { assertEquals(expected, source.lastOrDefault(predicate)); } + @Test + void OneElementIListTruePredicateDefault() { + int[] source = {4}; + Predicate1 predicate = TestCase::IsEven; + int expected = 4; + + assertEquals(expected, Linq.of(source).lastOrDefault(predicate, 5)); + } + @Test void ManyElementsIListPredicateFalseForAll() { IEnumerable source = Linq.of(new int[]{9, 5, 1, 3, 17, 21}); @@ -139,6 +189,15 @@ void ManyElementsIListPredicateFalseForAll() { assertEquals(expected, source.lastOrDefault(predicate)); } + @Test + void ManyElementsIListPredicateFalseForAllDefault() { + int[] source = {9, 5, 1, 3, 17, 21}; + Predicate1 predicate = TestCase::IsEven; + int expected = 5; + + assertEquals(expected, Linq.of(source).lastOrDefault(predicate, 5)); + } + @Test void IListPredicateTrueOnlyForLast() { IEnumerable source = Linq.of(new int[]{9, 5, 1, 3, 17, 21, 50}); diff --git a/src/test/java/com/bestvike/linq/enumerable/MaxByTest.java b/src/test/java/com/bestvike/linq/enumerable/MaxByTest.java index d34b513b..26f47f23 100644 --- a/src/test/java/com/bestvike/linq/enumerable/MaxByTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/MaxByTest.java @@ -1,20 +1,206 @@ package com.bestvike.linq.enumerable; import com.bestvike.TestCase; +import com.bestvike.function.Func1; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.Linq; import com.bestvike.linq.exception.ArgumentNullException; import com.bestvike.linq.exception.InvalidOperationException; +import com.bestvike.linq.util.ArgsList; import com.bestvike.tuple.Tuple; import com.bestvike.tuple.Tuple1; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.math.BigDecimal; +import java.util.Comparator; /** * Created by 许崇雷 on 2018-05-10. */ class MaxByTest extends TestCase { + private static Object[] WrapArgs(IEnumerable source, Func1 keySelector, Comparator comparer, TSource expected) { + return new Object[]{source, keySelector, comparer, expected}; + } + + private static IEnumerable MaxBy_Generic_TestData() { + ArgsList argsList = new ArgsList(); + + argsList.add(WrapArgs( + Linq.empty(), + x -> x, + null, + null)); + + argsList.add(WrapArgs( + Linq.empty(), + x -> x, + (x, y) -> 0, + null)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + x -> x, + null, + 9)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + x -> x, + (x, y) -> -x.compareTo(y), + 0)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + x -> x, + (x, y) -> 0, + 0)); + + argsList.add(WrapArgs( + Linq.of("Aardvark", "Zyzzyva", "Zebra", "Antelope"), + x -> x, + null, + "Zyzzyva")); + + argsList.add(WrapArgs( + Linq.of("Aardvark", "Zyzzyva", "Zebra", "Antelope"), + x -> x, + (x, y) -> -x.compareTo(y), + "Aardvark")); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem2(), + null, + Tuple.create("Dick", 55))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem2(), + (x, y) -> -x.compareTo(y), + Tuple.create("Harry", 20))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem1(), + null, + Tuple.create("Tom", 43))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem1(), + (x, y) -> -x.compareTo(y), + Tuple.create("Dick", 55))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create((String) null, 55), Tuple.create("Harry", 20)), + x -> x.getItem1(), + (x, y) -> -x.compareTo(y), + Tuple.create("Harry", 20))); + + return argsList; + } + + @Test + void MaxBy_Generic_NullSource_ThrowsArgumentNullException() { + IEnumerable source = null; + + assertThrows(NullPointerException.class, () -> source.maxBy(x -> x)); + assertThrows(NullPointerException.class, () -> source.maxBy(x -> x, null)); + assertThrows(NullPointerException.class, () -> source.maxBy(x -> x, (x, y) -> 0)); + } + + @Test + void MaxBy_Generic_NullKeySelector_ThrowsArgumentNullException() { + IEnumerable source = Linq.empty(); + Func1 keySelector = null; + + assertThrows(ArgumentNullException.class, () -> source.maxBy(keySelector)); + assertThrows(ArgumentNullException.class, () -> source.maxBy(keySelector, null)); + assertThrows(ArgumentNullException.class, () -> source.maxBy(keySelector, (x, y) -> 0)); + } + + @Test + void MaxBy_Generic_EmptyStructSource_ThrowsInvalidOperationException() { + assertThrows(InvalidOperationException.class, () -> Linq.empty().maxBy(x -> x.toString())); + assertThrows(InvalidOperationException.class, () -> Linq.empty().maxBy(x -> x.toString(), null)); + assertThrows(InvalidOperationException.class, () -> Linq.empty().maxBy(x -> x.toString(), (x, y) -> 0)); + } + + @Test + void MaxBy_Generic_EmptyNullableSource_ReturnsNull() { + assertNull(Linq.empty().maxByNull(x -> x.hashCode())); + assertNull(Linq.empty().maxByNull(x -> x.hashCode(), null)); + assertNull(Linq.empty().maxByNull(x -> x.hashCode(), (x, y) -> 0)); + } + + @Test + void MaxBy_Generic_EmptyReferenceSource_ReturnsNull() { + assertNull(Linq.empty().maxByNull(x -> x.hashCode())); + assertNull(Linq.empty().maxByNull(x -> x.hashCode(), null)); + assertNull(Linq.empty().maxByNull(x -> x.hashCode(), (x, y) -> 0)); + } + + @Test + void MaxBy_Generic_StructSourceAllKeysAreNull_ReturnsLastElement() { + assertEquals(4, Linq.range(0, 5).maxByNull(x -> null)); + assertEquals(4, Linq.range(0, 5).maxByNull(x -> null, null)); + assertEquals(4, Linq.range(0, 5).maxByNull(x -> null, (x, y) -> { + throw new InvalidOperationException("comparer should not be called."); + })); + } + + @Test + void MaxBy_Generic_NullableSourceAllKeysAreNull_ReturnsLastElement() { + assertEquals(4, Linq.range(0, 5).cast(Integer.class).maxByNull(x -> null)); + assertEquals(4, Linq.range(0, 5).cast(Integer.class).maxByNull(x -> null, null)); + assertEquals(4, Linq.range(0, 5).cast(Integer.class).maxByNull(x -> null, (x, y) -> { + throw new InvalidOperationException("comparer should not be called."); + })); + // + assertEquals(4, Linq.range(0, 5).maxByIntNull(x -> null)); + assertEquals(0, Linq.range(0, 5).maxByIntNull(x -> 0)); + assertEquals(1, Linq.of(Tuple.create((Integer) null, 0), Tuple.create(0, 1), Tuple.create((Integer) null, 2), Tuple.create(0, 3), Tuple.create((Integer) null, 4)).maxByIntNull(x -> x.getItem1()).getItem2()); + // + assertEquals(4, Linq.range(0, 5).maxByLongNull(x -> null)); + assertEquals(0, Linq.range(0, 5).maxByLongNull(x -> 0L)); + assertEquals(1, Linq.of(Tuple.create((Long) null, 0), Tuple.create(0L, 1), Tuple.create((Long) null, 2), Tuple.create(0L, 3), Tuple.create((Long) null, 4)).maxByLongNull(x -> x.getItem1()).getItem2()); + // + assertEquals(4, Linq.range(0, 5).maxByFloatNull(x -> null)); + assertEquals(0, Linq.range(0, 5).maxByFloatNull(x -> Float.NaN)); + assertEquals(1, Linq.of(Tuple.create((Float) null, 0), Tuple.create(Float.NaN, 1), Tuple.create((Float) null, 2), Tuple.create(Float.NaN, 3), Tuple.create((Float) null, 4)).maxByFloatNull(x -> x.getItem1()).getItem2()); + // + assertEquals(4, Linq.range(0, 5).maxByDoubleNull(x -> null)); + assertEquals(0, Linq.range(0, 5).maxByDoubleNull(x -> Double.NaN)); + assertEquals(1, Linq.of(Tuple.create((Double) null, 0), Tuple.create(Double.NaN, 1), Tuple.create((Double) null, 2), Tuple.create(Double.NaN, 3), Tuple.create((Double) null, 4)).maxByDoubleNull(x -> x.getItem1()).getItem2()); + // + assertEquals(4, Linq.range(0, 5).maxByDecimalNull(x -> null)); + assertEquals(0, Linq.range(0, 5).maxByDecimalNull(x -> m(0))); + assertEquals(1, Linq.of(Tuple.create((BigDecimal) null, 0), Tuple.create(m(0), 1), Tuple.create((BigDecimal) null, 2), Tuple.create(m(0), 3), Tuple.create((BigDecimal) null, 4)).maxByDecimalNull(x -> x.getItem1()).getItem2()); + } + + @Test + void MaxBy_Generic_ReferenceSourceAllKeysAreNull_ReturnsLastElement() { + assertEquals("4", Linq.range(0, 5).select(x -> x.toString()).maxByNull(x -> null)); + assertEquals("4", Linq.range(0, 5).select(x -> x.toString()).maxByNull(x -> null, null)); + assertEquals("4", Linq.range(0, 5).select(x -> x.toString()).maxByNull(x -> null, (x, y) -> { + throw new InvalidOperationException("comparer should not be called."); + })); + } + + @ParameterizedTest + @MethodSource("MaxBy_Generic_TestData") + void MaxBy_Generic_HasExpectedOutput(IEnumerable source, Func1 keySelector, Comparator comparer, TSource expected) { + assertEquals(expected, source.maxByNull(keySelector, comparer)); + } + + @ParameterizedTest + @MethodSource("MaxBy_Generic_TestData") + void MaxBy_Generic_RunOnce_HasExpectedOutput(IEnumerable source, Func1 keySelector, Comparator comparer, TSource expected) { + assertEquals(expected, source.runOnce().maxByNull(keySelector, comparer)); + } + @Test void testMaxByInt() { assertThrows(NullPointerException.class, () -> ((IEnumerable) null).maxByInt(x -> x)); @@ -38,7 +224,7 @@ void testMaxByIntNull() { assertEquals(Tuple.create(3), Linq.of(tuple1s).maxByIntNull(tuple -> (Integer) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).maxByIntNull(tuple -> (Integer) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).maxByIntNull(tuple -> (Integer) tuple.getItem1())); } @Test @@ -64,7 +250,7 @@ void testMaxByLongNull() { assertEquals(Tuple.create(3L), Linq.of(tuple1s).maxByLongNull(tuple -> (Long) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).maxByLongNull(tuple -> (Long) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).maxByLongNull(tuple -> (Long) tuple.getItem1())); } @Test @@ -96,7 +282,7 @@ void testMaxByFloatNull() { assertEquals(Tuple.create(2f), Linq.of(tuple1s).maxByFloatNull(tuple -> (Float) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).maxByFloatNull(tuple -> (Float) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).maxByFloatNull(tuple -> (Float) tuple.getItem1())); Tuple1[] tuple1s3 = {Tuple.create(null), Tuple.create(Float.NaN), Tuple.create(Float.NaN)}; assertSame(tuple1s3[1], Linq.of(tuple1s3).maxByFloatNull(tuple -> (Float) tuple.getItem1())); @@ -134,7 +320,7 @@ void testMaxByDoubleNull() { assertEquals(Tuple.create(2d), Linq.of(tuple1s).maxByDoubleNull(tuple -> (Double) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).maxByDoubleNull(tuple -> (Double) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).maxByDoubleNull(tuple -> (Double) tuple.getItem1())); Tuple1[] tuple1s3 = {Tuple.create(null), Tuple.create(Double.NaN), Tuple.create(Double.NaN)}; assertSame(tuple1s3[1], Linq.of(tuple1s3).maxByDoubleNull(tuple -> (Double) tuple.getItem1())); @@ -166,7 +352,7 @@ void testMaxByDecimalNull() { assertEquals(Tuple.create(m("3")), Linq.of(tuple1s).maxByDecimalNull(tuple -> (BigDecimal) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).maxByDecimalNull(tuple -> (BigDecimal) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).maxByDecimalNull(tuple -> (BigDecimal) tuple.getItem1())); } @Test @@ -195,7 +381,7 @@ void testMaxByNull() { assertEquals(Tuple.create(Float.NaN), Linq.of(tuple1s).maxByNull(tuple -> (Float) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).maxByNull(tuple -> (Float) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).maxByNull(tuple -> (Float) tuple.getItem1())); Tuple1[] tuple1s3 = {Tuple.create(1f), Tuple.create(null)}; assertEquals(tuple1s3[0], Linq.of(tuple1s3).maxByNull(tuple -> (Float) tuple.getItem1())); diff --git a/src/test/java/com/bestvike/linq/enumerable/MaxTest.java b/src/test/java/com/bestvike/linq/enumerable/MaxTest.java index e0ba0c4b..b1b17f13 100644 --- a/src/test/java/com/bestvike/linq/enumerable/MaxTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/MaxTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.params.provider.MethodSource; import java.math.BigDecimal; +import java.util.Comparator; import java.util.Date; /** @@ -278,6 +279,51 @@ private static IEnumerable Max_String_TestData() { return argsList; } + private static Object[] WrapArgs(IEnumerable source, Comparator comparer, TSource expected) { + return new Object[]{source, comparer, expected}; + } + + private static IEnumerable Max_Generic_TestData() { + ArgsList argsList = new ArgsList(); + + argsList.add(WrapArgs( + Linq.empty(), + null, + null)); + + argsList.add(WrapArgs( + Linq.empty(), + (x, y) -> 0, + null)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + null, + 9)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + (x, y) -> -x.compareTo(y), + 0)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + (x, y) -> 0, + 0)); + + argsList.add(WrapArgs( + Linq.of("Aardvark", "Zyzzyva", "Zebra", "Antelope"), + null, + "Zyzzyva")); + + argsList.add(WrapArgs( + Linq.of("Aardvark", "Zyzzyva", "Zebra", "Antelope"), + (x, y) -> -x.compareTo(y), + "Aardvark")); + + return argsList; + } + @Test void SameResultsRepeatCallsIntQuery() { IEnumerable q = Linq.of(new int[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}) @@ -729,6 +775,34 @@ void Max_Boolean_EmptySource_ThrowsInvalidOperationException() { assertThrows(InvalidOperationException.class, () -> Linq.empty().max()); } + @Test + void Max_Generic_NullSource_ThrowsArgumentNullException() { + IEnumerable source = null; + + assertThrows(NullPointerException.class, () -> source.max()); + assertThrows(NullPointerException.class, () -> source.max((Comparator) null)); + assertThrows(NullPointerException.class, () -> source.max((x, y) -> 0)); + } + + @Test + void Max_Generic_EmptyStructSource_ThrowsInvalidOperationException() { + assertThrows(InvalidOperationException.class, () -> Linq.empty().max()); + assertThrows(InvalidOperationException.class, () -> Linq.empty().max((Comparator) null)); + assertThrows(InvalidOperationException.class, () -> Linq.empty().max((x, y) -> 0)); + } + + @ParameterizedTest + @MethodSource("Max_Generic_TestData") + void Max_Generic_HasExpectedOutput(IEnumerable source, Comparator comparer, TSource expected) { + assertEquals(expected, source.maxNull(comparer)); + } + + @ParameterizedTest + @MethodSource("Max_Generic_TestData") + void Max_Generic_RunOnce_HasExpectedOutput(IEnumerable source, Comparator comparer, TSource expected) { + assertEquals(expected, source.runOnce().maxNull(comparer)); + } + @Test void testMaxInt() { Integer[] numbers = {0, 2, 3}; diff --git a/src/test/java/com/bestvike/linq/enumerable/MinByTest.java b/src/test/java/com/bestvike/linq/enumerable/MinByTest.java index 9102f44a..18b132de 100644 --- a/src/test/java/com/bestvike/linq/enumerable/MinByTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/MinByTest.java @@ -1,20 +1,206 @@ package com.bestvike.linq.enumerable; import com.bestvike.TestCase; +import com.bestvike.function.Func1; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.Linq; import com.bestvike.linq.exception.ArgumentNullException; import com.bestvike.linq.exception.InvalidOperationException; +import com.bestvike.linq.util.ArgsList; import com.bestvike.tuple.Tuple; import com.bestvike.tuple.Tuple1; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.math.BigDecimal; +import java.util.Comparator; /** * Created by 许崇雷 on 2018-05-10. */ class MinByTest extends TestCase { + private static Object[] WrapArgs(IEnumerable source, Func1 keySelector, Comparator comparer, TSource expected) { + return new Object[]{source, keySelector, comparer, expected}; + } + + private static IEnumerable MinBy_Generic_TestData() { + ArgsList argsList = new ArgsList(); + + argsList.add(WrapArgs( + Linq.empty(), + x -> x, + null, + null)); + + argsList.add(WrapArgs( + Linq.empty(), + x -> x, + (x, y) -> 0, + null)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + x -> x, + null, + 0)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + x -> x, + (x, y) -> -x.compareTo(y), + 9)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + x -> x, + (x, y) -> 0, + 0)); + + argsList.add(WrapArgs( + Linq.of("Aardvark", "Zyzzyva", "Zebra", "Antelope"), + x -> x, + null, + "Aardvark")); + + argsList.add(WrapArgs( + Linq.of("Aardvark", "Zyzzyva", "Zebra", "Antelope"), + x -> x, + (x, y) -> -x.compareTo(y), + "Zyzzyva")); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem2(), + null, + Tuple.create("Harry", 20))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem2(), + (x, y) -> -x.compareTo(y), + Tuple.create("Dick", 55))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem1(), + null, + Tuple.create("Dick", 55))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem1(), + (x, y) -> -x.compareTo(y), + Tuple.create("Tom", 43))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create((String) null, 43), Tuple.create("Dick", 55), Tuple.create("Harry", 20)), + x -> x.getItem1(), + (x, y) -> -x.compareTo(y), + Tuple.create("Harry", 20))); + + return argsList; + } + + @Test + void MinBy_Generic_NullSource_ThrowsArgumentNullException() { + IEnumerable source = null; + + assertThrows(NullPointerException.class, () -> source.minBy(x -> x)); + assertThrows(NullPointerException.class, () -> source.minBy(x -> x, null)); + assertThrows(NullPointerException.class, () -> source.minBy(x -> x, (x, y) -> 0)); + } + + @Test + void MinBy_Generic_NullKeySelector_ThrowsArgumentNullException() { + IEnumerable source = Linq.empty(); + Func1 keySelector = null; + + assertThrows(ArgumentNullException.class, () -> source.minBy(keySelector)); + assertThrows(ArgumentNullException.class, () -> source.minBy(keySelector, null)); + assertThrows(ArgumentNullException.class, () -> source.minBy(keySelector, (x, y) -> 0)); + } + + @Test + void MinBy_Generic_EmptyStructSource_ThrowsInvalidOperationException() { + assertThrows(InvalidOperationException.class, () -> Linq.empty().minBy(x -> x.toString())); + assertThrows(InvalidOperationException.class, () -> Linq.empty().minBy(x -> x.toString(), null)); + assertThrows(InvalidOperationException.class, () -> Linq.empty().minBy(x -> x.toString(), (x, y) -> 0)); + } + + @Test + void MinBy_Generic_EmptyNullableSource_ReturnsNull() { + assertNull(Linq.empty().minByNull(x -> x.hashCode())); + assertNull(Linq.empty().minByNull(x -> x.hashCode(), null)); + assertNull(Linq.empty().minByNull(x -> x.hashCode(), (x, y) -> 0)); + } + + @Test + void MinBy_Generic_EmptyReferenceSource_ReturnsNull() { + assertNull(Linq.empty().minByNull(x -> x.hashCode())); + assertNull(Linq.empty().minByNull(x -> x.hashCode(), null)); + assertNull(Linq.empty().minByNull(x -> x.hashCode(), (x, y) -> 0)); + } + + @Test + void MinBy_Generic_StructSourceAllKeysAreNull_ReturnsLastElement() { + assertEquals(4, Linq.range(0, 5).minByNull(x -> null)); + assertEquals(4, Linq.range(0, 5).minByNull(x -> null, null)); + assertEquals(4, Linq.range(0, 5).minByNull(x -> null, (x, y) -> { + throw new InvalidOperationException("comparer should not be called."); + })); + } + + @Test + void MinBy_Generic_NullableSourceAllKeysAreNull_ReturnsLastElement() { + assertEquals(4, Linq.range(0, 5).cast(Integer.class).minByNull(x -> null)); + assertEquals(4, Linq.range(0, 5).cast(Integer.class).minByNull(x -> null, null)); + assertEquals(4, Linq.range(0, 5).cast(Integer.class).minByNull(x -> null, (x, y) -> { + throw new InvalidOperationException("comparer should not be called."); + })); + // + assertEquals(4, Linq.range(0, 5).minByIntNull(x -> null)); + assertEquals(0, Linq.range(0, 5).minByIntNull(x -> 0)); + assertEquals(1, Linq.of(Tuple.create((Integer) null, 0), Tuple.create(0, 1), Tuple.create((Integer) null, 2), Tuple.create(0, 3), Tuple.create((Integer) null, 4)).minByIntNull(x -> x.getItem1()).getItem2()); + // + assertEquals(4, Linq.range(0, 5).minByLongNull(x -> null)); + assertEquals(0, Linq.range(0, 5).minByLongNull(x -> 0L)); + assertEquals(1, Linq.of(Tuple.create((Long) null, 0), Tuple.create(0L, 1), Tuple.create((Long) null, 2), Tuple.create(0L, 3), Tuple.create((Long) null, 4)).minByLongNull(x -> x.getItem1()).getItem2()); + // + assertEquals(4, Linq.range(0, 5).minByFloatNull(x -> null)); + assertEquals(0, Linq.range(0, 5).minByFloatNull(x -> Float.NaN)); + assertEquals(1, Linq.of(Tuple.create((Float) null, 0), Tuple.create(Float.NaN, 1), Tuple.create((Float) null, 2), Tuple.create(Float.NaN, 3), Tuple.create((Float) null, 4)).minByFloatNull(x -> x.getItem1()).getItem2()); + // + assertEquals(4, Linq.range(0, 5).minByDoubleNull(x -> null)); + assertEquals(0, Linq.range(0, 5).minByDoubleNull(x -> Double.NaN)); + assertEquals(1, Linq.of(Tuple.create((Double) null, 0), Tuple.create(Double.NaN, 1), Tuple.create((Double) null, 2), Tuple.create(Double.NaN, 3), Tuple.create((Double) null, 4)).minByDoubleNull(x -> x.getItem1()).getItem2()); + // + assertEquals(4, Linq.range(0, 5).minByDecimalNull(x -> null)); + assertEquals(0, Linq.range(0, 5).minByDecimalNull(x -> m(0))); + assertEquals(1, Linq.of(Tuple.create((BigDecimal) null, 0), Tuple.create(m(0), 1), Tuple.create((BigDecimal) null, 2), Tuple.create(m(0), 3), Tuple.create((BigDecimal) null, 4)).minByDecimalNull(x -> x.getItem1()).getItem2()); + } + + @Test + void MinBy_Generic_ReferenceSourceAllKeysAreNull_ReturnsLastElement() { + assertEquals("4", Linq.range(0, 5).select(x -> x.toString()).minByNull(x -> null)); + assertEquals("4", Linq.range(0, 5).select(x -> x.toString()).minByNull(x -> null, null)); + assertEquals("4", Linq.range(0, 5).select(x -> x.toString()).minByNull(x -> null, (x, y) -> { + throw new InvalidOperationException("comparer should not be called."); + })); + } + + @ParameterizedTest + @MethodSource("MinBy_Generic_TestData") + void MinBy_Generic_HasExpectedOutput(IEnumerable source, Func1 keySelector, Comparator comparer, TSource expected) { + assertEquals(expected, source.minByNull(keySelector, comparer)); + } + + @ParameterizedTest + @MethodSource("MinBy_Generic_TestData") + void MinBy_Generic_RunOnce_HasExpectedOutput(IEnumerable source, Func1 keySelector, Comparator comparer, TSource expected) { + assertEquals(expected, source.runOnce().minByNull(keySelector, comparer)); + } + @Test void testMinByInt() { assertThrows(NullPointerException.class, () -> ((IEnumerable) null).minByInt(x -> x)); @@ -38,7 +224,7 @@ void testMinByIntNull() { assertEquals(Tuple.create(0), Linq.of(tuple1s).minByIntNull(tuple -> (Integer) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).minByIntNull(tuple -> (Integer) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).minByIntNull(tuple -> (Integer) tuple.getItem1())); } @Test @@ -64,7 +250,7 @@ void testMinByLongNull() { assertEquals(Tuple.create(0L), Linq.of(tuple1s).minByLongNull(tuple -> (Long) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).minByLongNull(tuple -> (Long) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).minByLongNull(tuple -> (Long) tuple.getItem1())); } @Test @@ -96,7 +282,7 @@ void testMinByFloatNull() { assertEquals(Tuple.create(0f), Linq.of(tuple1s).minByFloatNull(tuple -> (Float) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).minByFloatNull(tuple -> (Float) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).minByFloatNull(tuple -> (Float) tuple.getItem1())); Tuple1[] tuple1s3 = {Tuple.create(null), Tuple.create(Float.NaN), Tuple.create(Float.NaN)}; assertSame(tuple1s3[1], Linq.of(tuple1s3).minByFloatNull(tuple -> (Float) tuple.getItem1())); @@ -134,7 +320,7 @@ void testMinByDoubleNull() { assertEquals(Tuple.create(0d), Linq.of(tuple1s).minByDoubleNull(tuple -> (Double) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).minByDoubleNull(tuple -> (Double) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).minByDoubleNull(tuple -> (Double) tuple.getItem1())); Tuple1[] tuple1s3 = {Tuple.create(null), Tuple.create(Double.NaN), Tuple.create(Double.NaN)}; assertSame(tuple1s3[1], Linq.of(tuple1s3).minByDoubleNull(tuple -> (Double) tuple.getItem1())); @@ -166,7 +352,7 @@ void testMinByDecimalNull() { assertEquals(Tuple.create(m("0")), Linq.of(tuple1s).minByDecimalNull(tuple -> (BigDecimal) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).minByDecimalNull(tuple -> (BigDecimal) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).minByDecimalNull(tuple -> (BigDecimal) tuple.getItem1())); } @Test @@ -195,7 +381,7 @@ void testMinByNull() { assertEquals(Tuple.create(0f), Linq.of(tuple1s).minByNull(tuple -> (Float) tuple.getItem1())); Tuple1[] tuple1s2 = {Tuple.create(null)}; - assertEquals(null, Linq.of(tuple1s2).minByNull(tuple -> (Float) tuple.getItem1())); + assertSame(tuple1s2[0], Linq.of(tuple1s2).minByNull(tuple -> (Float) tuple.getItem1())); Tuple1[] tuple1s3 = {Tuple.create(1f), Tuple.create(null)}; assertEquals(tuple1s3[0], Linq.of(tuple1s3).minByNull(tuple -> (Float) tuple.getItem1())); diff --git a/src/test/java/com/bestvike/linq/enumerable/MinTest.java b/src/test/java/com/bestvike/linq/enumerable/MinTest.java index 51bb7b4b..f18a5bfa 100644 --- a/src/test/java/com/bestvike/linq/enumerable/MinTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/MinTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.params.provider.MethodSource; import java.math.BigDecimal; +import java.util.Comparator; import java.util.Date; /** @@ -305,6 +306,51 @@ private static IEnumerable Min_String_TestData() { return argsList; } + private static Object[] WrapArgs(IEnumerable source, Comparator comparer, TSource expected) { + return new Object[]{source, comparer, expected}; + } + + private static IEnumerable Min_Generic_TestData() { + ArgsList argsList = new ArgsList(); + + argsList.add(WrapArgs( + Linq.empty(), + null, + null)); + + argsList.add(WrapArgs( + Linq.empty(), + (x, y) -> 0, + null)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + null, + 0)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + (x, y) -> -x.compareTo(y), + 9)); + + argsList.add(WrapArgs( + Linq.range(0, 10), + (x, y) -> 0, + 0)); + + argsList.add(WrapArgs( + Linq.of("Aardvark", "Zyzzyva", "Zebra", "Antelope"), + null, + "Aardvark")); + + argsList.add(WrapArgs( + Linq.of("Aardvark", "Zyzzyva", "Zebra", "Antelope"), + (x, y) -> -x.compareTo(y), + "Zyzzyva")); + + return argsList; + } + @Test void SameResultsRepeatCallsIntQuery() { IEnumerable q = Linq.of(new int[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}) @@ -712,6 +758,34 @@ void Min_Bool_EmptySource_ThrowsInvalodOperationException() { assertThrows(InvalidOperationException.class, () -> Linq.empty().min()); } + @Test + void Min_Generic_NullSource_ThrowsArgumentNullException() { + IEnumerable source = null; + + assertThrows(NullPointerException.class, () -> source.min()); + assertThrows(NullPointerException.class, () -> source.min((Comparator) null)); + assertThrows(NullPointerException.class, () -> source.min((x, y) -> 0)); + } + + @Test + void Min_Generic_EmptyStructSource_ThrowsInvalidOperationException() { + assertThrows(InvalidOperationException.class, () -> Linq.empty().min()); + assertThrows(InvalidOperationException.class, () -> Linq.empty().min((Comparator) null)); + assertThrows(InvalidOperationException.class, () -> Linq.empty().min((x, y) -> 0)); + } + + @ParameterizedTest + @MethodSource("Min_Generic_TestData") + void Min_Generic_HasExpectedOutput(IEnumerable source, Comparator comparer, TSource expected) { + assertEquals(expected, source.minNull(comparer)); + } + + @ParameterizedTest + @MethodSource("Min_Generic_TestData") + void Min_Generic_RunOnce_HasExpectedOutput(IEnumerable source, Comparator comparer, TSource expected) { + assertEquals(expected, source.runOnce().minNull(comparer)); + } + @Test void testMinInt() { Integer[] numbers = {0, 2, 3}; diff --git a/src/test/java/com/bestvike/linq/enumerable/SequenceEqualTest.java b/src/test/java/com/bestvike/linq/enumerable/SequenceEqualTest.java index c200b528..ce1ab351 100644 --- a/src/test/java/com/bestvike/linq/enumerable/SequenceEqualTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/SequenceEqualTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test; import java.util.List; +import java.util.Random; /** * Created by 许崇雷 on 2018-05-10. @@ -198,6 +199,35 @@ void SecondSourceNull() { assertThrows(ArgumentNullException.class, () -> first.sequenceEqual(second)); } + @Test + void ByteArrays_SpecialCasedButExpectedBehavior() { + assertThrows(NullPointerException.class, () -> ((IEnumerable) null).sequenceEqual(Linq.empty())); + assertThrows(ArgumentNullException.class, () -> Linq.empty().sequenceEqual(null)); + + assertFalse(Linq.of(new byte[1]).sequenceEqual(Linq.of(new byte[0]))); + assertFalse(Linq.of(new byte[0]).sequenceEqual(Linq.of(new byte[1]))); + + Random r = new Random(); + for (int i = 0; i < 32; i++) { + byte[] arr = new byte[i]; + r.nextBytes(arr); + + byte[] same = arr.clone(); + assertTrue(Linq.of(arr).sequenceEqual(Linq.of(same))); + assertTrue(Linq.of(same).sequenceEqual(Linq.of(arr))); + assertTrue(Linq.of(same).sequenceEqual(Linq.of(Linq.of(arr).toList()))); + assertTrue(Linq.of(Linq.of(same).toList()).sequenceEqual(Linq.of(arr))); + assertTrue(Linq.of(Linq.of(same).toList()).sequenceEqual(Linq.of(Linq.of(arr).toList()))); + + if (i > 0) { + byte[] diff = arr.clone(); + diff[diff.length - 1]++; + assertFalse(Linq.of(arr).sequenceEqual(Linq.of(diff))); + assertFalse(Linq.of(diff).sequenceEqual(Linq.of(arr))); + } + } + } + @Test void testSequenceEqual() { List list = Linq.of(emps).toList(); diff --git a/src/test/java/com/bestvike/linq/enumerable/SingleOrDefaultTest.java b/src/test/java/com/bestvike/linq/enumerable/SingleOrDefaultTest.java index f88521e3..ee14deb0 100644 --- a/src/test/java/com/bestvike/linq/enumerable/SingleOrDefaultTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/SingleOrDefaultTest.java @@ -44,6 +44,14 @@ void EmptyIList() { assertEquals(expected, Linq.of(source).singleOrDefault()); } + @Test + void EmptyIListDefault() { + Integer[] source = {}; + int expected = 5; + + assertEquals(expected, Linq.of(source).singleOrDefault(5)); + } + @Test void SingleElementIList() { int[] source = {4}; @@ -52,6 +60,14 @@ void SingleElementIList() { assertEquals(expected, Linq.of(source).singleOrDefault()); } + @Test + void SingleElementIListDefault() { + int[] source = {4}; + int expected = 4; + + assertEquals(expected, Linq.of(source).singleOrDefault(5)); + } + @Test void ManyElementIList() { int[] source = {4, 4, 4, 4, 4}; @@ -59,6 +75,13 @@ void ManyElementIList() { assertThrows(InvalidOperationException.class, () -> Linq.of(source).singleOrDefault()); } + @Test + void ManyElementIListDefault() { + int[] source = {4, 4, 4, 4, 4}; + + assertThrows(InvalidOperationException.class, () -> Linq.of(source).singleOrDefault(5)); + } + @Test void EmptyNotIList() { IEnumerable source = RepeatedNumberGuaranteedNotCollectionType(0, 0); @@ -90,6 +113,14 @@ void EmptySourceWithPredicate() { assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0)); } + @Test + void EmptySourceWithPredicateDefault() { + int[] source = {}; + int expected = 5; + + assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0, 5)); + } + @Test void SingleElementPredicateTrue() { int[] source = {4}; @@ -98,6 +129,14 @@ void SingleElementPredicateTrue() { assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0)); } + @Test + void SingleElementPredicateTrueDefault() { + int[] source = {4}; + int expected = 4; + + assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0, 5)); + } + @Test void SingleElementPredicateFalse() { int[] source = {3}; @@ -106,6 +145,14 @@ void SingleElementPredicateFalse() { assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0)); } + @Test + void SingleElementPredicateFalseDefault() { + int[] source = {3}; + int expected = 5; + + assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0, 5)); + } + @Test void ManyElementsPredicateFalseForAll() { int[] source = {3, 1, 7, 9, 13, 19}; @@ -114,6 +161,14 @@ void ManyElementsPredicateFalseForAll() { assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0)); } + @Test + void ManyElementsPredicateFalseForAllDefault() { + int[] source = {3, 1, 7, 9, 13, 19}; + int expected = 5; + + assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0, 5)); + } + @Test void ManyElementsPredicateTrueForLast() { int[] source = {3, 1, 7, 9, 13, 19, 20}; @@ -122,6 +177,14 @@ void ManyElementsPredicateTrueForLast() { assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0)); } + @Test + void ManyElementsPredicateTrueForLastDefault() { + int[] source = {3, 1, 7, 9, 13, 19, 20}; + int expected = 20; + + assertEquals(expected, Linq.of(source).singleOrDefault(i -> i % 2 == 0, 5)); + } + @Test void ManyElementsPredicateTrueForFirstAndFifth() { int[] source = {2, 3, 1, 7, 10, 13, 19, 9}; @@ -129,6 +192,13 @@ void ManyElementsPredicateTrueForFirstAndFifth() { assertThrows(InvalidOperationException.class, () -> Linq.of(source).singleOrDefault(i -> i % 2 == 0)); } + @Test + void ManyElementsPredicateTrueForFirstAndFifthDefault() { + int[] source = {2, 3, 1, 7, 10, 13, 19, 9}; + + assertThrows(InvalidOperationException.class, () -> Linq.of(source).singleOrDefault(i -> i % 2 == 0, 5)); + } + @ParameterizedTest @MethodSource("FindSingleMatch_TestData") void FindSingleMatch(int target, int range) { @@ -148,6 +218,13 @@ void ThrowsOnNullSource() { assertThrows(NullPointerException.class, () -> source.singleOrDefault(i -> i % 2 == 0)); } + @Test + void ThrowsOnNullSourceDefault() { + IEnumerable source = null; + assertThrows(NullPointerException.class, () -> source.singleOrDefault(5)); + assertThrows(NullPointerException.class, () -> source.singleOrDefault(i -> i % 2 == 0, 5)); + } + @Test void ThrowsOnNullPredicate() { IEnumerable source = Linq.empty(); @@ -155,6 +232,13 @@ void ThrowsOnNullPredicate() { assertThrows(ArgumentNullException.class, () -> source.singleOrDefault(nullPredicate)); } + @Test + void ThrowsOnNullPredicateDefault() { + int[] source = {}; + Predicate1 nullPredicate = null; + assertThrows(ArgumentNullException.class, () -> Linq.of(source).singleOrDefault(nullPredicate, 5)); + } + @Test void testSingleOrDefault() { String[] person = {"Smith"}; diff --git a/src/test/java/com/bestvike/linq/enumerable/SkipLastTest.java b/src/test/java/com/bestvike/linq/enumerable/SkipLastTest.java index d3e11862..fc0b6331 100644 --- a/src/test/java/com/bestvike/linq/enumerable/SkipLastTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/SkipLastTest.java @@ -85,7 +85,7 @@ void List_ChangesAfterSkipLast_ChangesReflectedInResults() { IEnumerable e = Linq.of(list).skipLast(2); list.remove(4); list.remove(3); - assertEquals(Linq.of(1, 2, 3), Linq.of(e.toArray()));//Make sure the source is immutable. see https://github.com/dotnet/runtime/pull/42506 + assertEquals(Linq.of(1), Linq.of(e.toArray())); } @Test @@ -93,7 +93,7 @@ void List_Skip_ChangesAfterSkipLast_ChangesReflectedInResults() { List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); IEnumerable e = Linq.of(list).skip(1).skipLast(2); list.remove(4); - assertEquals(Linq.of(2, 3), Linq.of(e.toArray()));//Make sure the source is immutable. see https://github.com/dotnet/runtime/pull/42506 + assertEquals(Linq.of(2), Linq.of(e.toArray())); } @Test diff --git a/src/test/java/com/bestvike/linq/enumerable/SkipTest.java b/src/test/java/com/bestvike/linq/enumerable/SkipTest.java index 18371ddb..5b0d0ee6 100644 --- a/src/test/java/com/bestvike/linq/enumerable/SkipTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/SkipTest.java @@ -564,7 +564,7 @@ void testIPartition_SkipLast() { assertEquals(0, emptySource2.count()); assertThrows(InvalidOperationException.class, () -> emptySource2.first()); assertThrows(InvalidOperationException.class, () -> emptySource2.last()); - assertIsType(Linq.empty().getClass(), emptySource2.skip(2)); + assertIsType(EnumerablePartition.class, emptySource2.skip(2)); try (IEnumerator e = emptySource2.enumerator()) { assertFalse(e.moveNext()); assertFalse(e.moveNext()); @@ -601,7 +601,7 @@ void testIArrayList_SkipLast() { assertEquals(0, emptySource2.count()); assertThrows(InvalidOperationException.class, () -> emptySource2.first()); assertThrows(InvalidOperationException.class, () -> emptySource2.last()); - assertIsType(Linq.empty().getClass(), emptySource2.skip(2)); + assertIsType(EnumerablePartition.class, emptySource2.skip(2)); try (IEnumerator e = emptySource2.enumerator()) { assertFalse(e.moveNext()); assertFalse(e.moveNext()); @@ -638,7 +638,7 @@ void testIList_SkipLast() { assertEquals(0, emptySource2.count()); assertThrows(InvalidOperationException.class, () -> emptySource2.first()); assertThrows(InvalidOperationException.class, () -> emptySource2.last()); - assertIsType(Linq.empty().getClass(), emptySource2.skip(2)); + assertIsType(EnumerablePartition.class, emptySource2.skip(2)); try (IEnumerator e = emptySource2.enumerator()) { assertFalse(e.moveNext()); assertFalse(e.moveNext()); diff --git a/src/test/java/com/bestvike/linq/enumerable/TakeLastTest.java b/src/test/java/com/bestvike/linq/enumerable/TakeLastTest.java index 6bea52e6..1f08a4b4 100644 --- a/src/test/java/com/bestvike/linq/enumerable/TakeLastTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/TakeLastTest.java @@ -91,7 +91,7 @@ void List_ChangesAfterTakeLast_ChangesReflectedInResults() { IEnumerable e = Linq.of(list).takeLast(3); list.remove(0); list.remove(0); - assertEquals(Linq.of(5), Linq.of(e.toArray()));//Make sure the source is immutable. see https://github.com/dotnet/runtime/pull/42506 + assertEquals(Linq.of(3, 4, 5), Linq.of(e.toArray())); } @Test @@ -99,7 +99,7 @@ void List_Skip_ChangesAfterTakeLast_ChangesReflectedInResults() { List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); IEnumerable e = Linq.of(list).skip(1).takeLast(3); list.remove(0); - assertEquals(Linq.of(4, 5), Linq.of(e.toArray()));//Make sure the source is immutable. see https://github.com/dotnet/runtime/pull/42506 + assertEquals(Linq.of(3, 4, 5), Linq.of(e.toArray())); } @Test diff --git a/src/test/java/com/bestvike/linq/enumerable/TakeTest.java b/src/test/java/com/bestvike/linq/enumerable/TakeTest.java index fd8c9387..465792b4 100644 --- a/src/test/java/com/bestvike/linq/enumerable/TakeTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/TakeTest.java @@ -1,6 +1,9 @@ package com.bestvike.linq.enumerable; +import com.bestvike.Index; +import com.bestvike.Range; import com.bestvike.TestCase; +import com.bestvike.function.Func0; import com.bestvike.linq.IEnumerable; import com.bestvike.linq.IEnumerator; import com.bestvike.linq.Linq; @@ -14,6 +17,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; @@ -23,10 +27,6 @@ * Created by 许崇雷 on 2018-05-10. */ class TakeTest extends TestCase { - private static IEnumerable GuaranteeNotIList(IEnumerable source) { - return source.select(x -> x); - } - private static IEnumerable LazySkipAllTakenForLargeNumbers_TestData() { ArgsList argsList = new ArgsList(); argsList.add(1000); @@ -63,10 +63,10 @@ private static IEnumerable FirstAndLastOfLazySkipTakeChain_TestData() private static IEnumerable ElementAtOfLazySkipTakeChain_TestData() { ArgsList argsList = new ArgsList(); - argsList.add(Linq.of(new int[]{1, 2, 3, 4, 5}), 1, 3, new int[]{-1, 0, 1, 2}, new Integer[]{null, 2, 3, 4}); - argsList.add(Linq.of(new int[]{0xfefe, 7000, 123}), 0, 3, new int[]{-1, 0, 1, 2}, new Integer[]{null, 0xfefe, 7000, 123}); - argsList.add(Linq.of(new int[]{0xfefe}), 100, 100, new int[]{-1, 0, 1, 2}, new Integer[]{null, null, null, null}); - argsList.add(Linq.of(new int[]{0xfefe, 123, 456, 7890, 5555, 55}), 1, 10, new int[]{-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, new Integer[]{null, 123, 456, 7890, 5555, 55, null, null, null, null, null, null, null}); + argsList.add(new int[]{1, 2, 3, 4, 5}, 1, 3, new int[]{-1, 0, 1, 2}, new Integer[]{null, 2, 3, 4}); + argsList.add(new int[]{0xfefe, 7000, 123}, 0, 3, new int[]{-1, 0, 1, 2}, new Integer[]{null, 0xfefe, 7000, 123}); + argsList.add(new int[]{0xfefe}, 100, 100, new int[]{-1, 0, 1, 2}, new Integer[]{null, null, null, null}); + argsList.add(new int[]{0xfefe, 123, 456, 7890, 5555, 55}, 1, 10, new int[]{-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, new Integer[]{null, 123, 456, 7890, 5555, 55, null, null, null, null, null, null, null}); return argsList; } @@ -86,6 +86,11 @@ void SameResultsRepeatCallsIntQuery() { IEnumerable q = Linq.of(new int[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}).where(x -> x > Integer.MIN_VALUE); assertEquals(q.take(9), q.take(9)); + + assertEquals(q.take(new Range(Index.Start, Index.fromStart(9))), q.take(new Range(Index.Start, Index.fromStart(9)))); + assertEquals(q.take(new Range(Index.fromEnd(9), Index.fromStart(9))), q.take(new Range(Index.fromEnd(9), Index.fromStart(9)))); + assertEquals(q.take(new Range(Index.Start, Index.End)), q.take(new Range(Index.Start, Index.End))); + assertEquals(q.take(new Range(Index.fromEnd(9), Index.End)), q.take(new Range(Index.fromEnd(9), Index.End))); } @Test @@ -93,6 +98,11 @@ void SameResultsRepeatCallsIntQueryIList() { List q = Linq.of(new int[]{9999, 0, 888, -1, 66, -777, 1, 2, -12345}).where(x -> x > Integer.MIN_VALUE).toList(); assertEquals(Linq.of(q).take(9), Linq.of(q).take(9)); + + assertEquals(Linq.of(q).take(new Range(Index.Start, Index.fromStart(9))), Linq.of(q).take(new Range(Index.Start, Index.fromStart(9)))); + assertEquals(Linq.of(q).take(new Range(Index.fromEnd(9), Index.fromStart(9))), Linq.of(q).take(new Range(Index.fromEnd(9), Index.fromStart(9)))); + assertEquals(Linq.of(q).take(new Range(Index.Start, Index.End)), Linq.of(q).take(new Range(Index.Start, Index.End))); + assertEquals(Linq.of(q).take(new Range(Index.fromEnd(9), Index.End)), Linq.of(q).take(new Range(Index.fromEnd(9), Index.End))); } @Test @@ -100,6 +110,11 @@ void SameResultsRepeatCallsStringQuery() { IEnumerable q = Linq.of(new String[]{"!@#$%^", "C", "AAA", "", "Calling Twice", "SoS", Empty}).where(x -> !IsNullOrEmpty(x)); assertEquals(q.take(7), q.take(7)); + + assertEquals(q.take(new Range(Index.Start, Index.fromStart(7))), q.take(new Range(Index.Start, Index.fromStart(7)))); + assertEquals(q.take(new Range(Index.fromEnd(7), Index.fromStart(7))), q.take(new Range(Index.fromEnd(7), Index.fromStart(7)))); + assertEquals(q.take(new Range(Index.Start, Index.End)), q.take(new Range(Index.Start, Index.End))); + assertEquals(q.take(new Range(Index.fromEnd(7), Index.End)), q.take(new Range(Index.fromEnd(7), Index.End))); } @Test @@ -107,42 +122,71 @@ void SameResultsRepeatCallsStringQueryIList() { List q = Linq.of(new String[]{"!@#$%^", "C", "AAA", "", "Calling Twice", "SoS", Empty}).where(x -> !IsNullOrEmpty(x)).toList(); assertEquals(Linq.of(q).take(7), Linq.of(q).take(7)); + + assertEquals(Linq.of(q).take(new Range(Index.Start, Index.fromStart(7))), Linq.of(q).take(new Range(Index.Start, Index.fromStart(7)))); + assertEquals(Linq.of(q).take(new Range(Index.fromEnd(7), Index.fromStart(7))), Linq.of(q).take(new Range(Index.fromEnd(7), Index.fromStart(7)))); + assertEquals(Linq.of(q).take(new Range(Index.Start, Index.End)), Linq.of(q).take(new Range(Index.Start, Index.End))); + assertEquals(Linq.of(q).take(new Range(Index.fromEnd(7), Index.End)), Linq.of(q).take(new Range(Index.fromEnd(7), Index.End))); } @Test void SourceEmptyCountPositive() { int[] source = {}; assertEmpty(Linq.of(source).take(5)); + + assertEmpty(Linq.of(source).take(new Range(Index.Start, Index.fromStart(5)))); + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(5)))); + assertEmpty(Linq.of(source).take(new Range(Index.Start, Index.End))); + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(5), Index.End))); } @Test void SourceEmptyCountPositiveNotIList() { IEnumerable source = NumberRangeGuaranteedNotCollectionType(0, 0); assertEmpty(source.take(5)); + + assertEmpty(source.take(new Range(Index.Start, Index.fromStart(5)))); + assertEmpty(source.take(new Range(Index.fromEnd(5), Index.fromStart(5)))); + assertEmpty(source.take(new Range(Index.Start, Index.End))); + assertEmpty(source.take(new Range(Index.fromEnd(5), Index.End))); } @Test void SourceNonEmptyCountNegative() { int[] source = {2, 5, 9, 1}; assertEmpty(Linq.of(source).take(-5)); + + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(9), Index.Start))); } @Test void SourceNonEmptyCountNegativeNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{2, 5, 9, 1})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{2, 5, 9, 1})); assertEmpty(source.take(-5)); + + assertEmpty(source.take(new Range(Index.fromEnd(9), Index.Start))); } @Test void SourceNonEmptyCountZero() { int[] source = {2, 5, 9, 1}; assertEmpty(Linq.of(source).take(0)); + + assertEmpty(Linq.of(source).take(new Range(Index.Start, Index.Start))); + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(4), Index.Start))); + assertEmpty(Linq.of(source).take(new Range(Index.Start, Index.fromEnd(4)))); + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(4), Index.fromEnd(4)))); } @Test void SourceNonEmptyCountZeroNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{2, 5, 9, 1})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{2, 5, 9, 1})); assertEmpty(source.take(0)); + + assertEmpty(source.take(new Range(Index.Start, Index.Start))); + assertEmpty(source.take(new Range(Index.fromEnd(4), Index.Start))); + assertEmpty(source.take(new Range(Index.Start, Index.fromEnd(4)))); + assertEmpty(source.take(new Range(Index.fromEnd(4), Index.fromEnd(4)))); } @Test @@ -151,14 +195,24 @@ void SourceNonEmptyCountOne() { int[] expected = {2}; assertEquals(Linq.of(expected), Linq.of(source).take(1)); + + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.fromEnd(4), Index.fromStart(1)))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.Start, Index.fromEnd(3)))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.fromEnd(4), Index.fromEnd(3)))); } @Test void SourceNonEmptyCountOneNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{2, 5, 9, 1})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{2, 5, 9, 1})); int[] expected = {2}; assertEquals(Linq.of(expected), source.take(1)); + + assertEquals(Linq.of(expected), source.take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(expected), source.take(new Range(Index.fromEnd(4), Index.fromStart(1)))); + assertEquals(Linq.of(expected), source.take(new Range(Index.Start, Index.fromEnd(3)))); + assertEquals(Linq.of(expected), source.take(new Range(Index.fromEnd(4), Index.fromEnd(3)))); } @Test @@ -166,13 +220,23 @@ void SourceNonEmptyTakeAllExactly() { int[] source = {2, 5, 9, 1}; assertEquals(Linq.of(source), Linq.of(source).take(source.length)); + + assertEquals(Linq.of(source), Linq.of(source).take(new Range(Index.Start, Index.fromStart(source.length)))); + assertEquals(Linq.of(source), Linq.of(source).take(new Range(Index.fromEnd(source.length), Index.fromStart(source.length)))); + assertEquals(Linq.of(source), Linq.of(source).take(new Range(Index.Start, Index.End))); + assertEquals(Linq.of(source), Linq.of(source).take(new Range(Index.fromEnd(source.length), Index.End))); } @Test void SourceNonEmptyTakeAllExactlyNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{2, 5, 9, 1})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{2, 5, 9, 1})); assertEquals(source, source.take(source.count())); + + assertEquals(source, source.take(new Range(Index.Start, Index.fromStart(source.count())))); + assertEquals(source, source.take(new Range(Index.fromEnd(source.count()), Index.fromStart(source.count())))); + assertEquals(source, source.take(new Range(Index.Start, Index.End))); + assertEquals(source, source.take(new Range(Index.fromEnd(source.count()), Index.End))); } @Test @@ -181,6 +245,11 @@ void SourceNonEmptyTakeAllButOne() { int[] expected = {2, 5, 9}; assertEquals(Linq.of(expected), Linq.of(source).take(3)); + + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.Start, Index.fromStart(3)))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.fromEnd(4), Index.fromStart(3)))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.Start, Index.fromEnd(1)))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.fromEnd(4), Index.fromEnd(1)))); } @Test @@ -189,14 +258,24 @@ void RunOnce() { int[] expected = {2, 5, 9}; assertEquals(Linq.of(expected), Linq.of(source).runOnce().take(3)); + + assertEquals(Linq.of(expected), Linq.of(source).runOnce().take(new Range(Index.Start, Index.fromStart(3)))); + assertEquals(Linq.of(expected), Linq.of(source).runOnce().take(new Range(Index.fromEnd(4), Index.fromStart(3)))); + assertEquals(Linq.of(expected), Linq.of(source).runOnce().take(new Range(Index.Start, Index.fromEnd(1)))); + assertEquals(Linq.of(expected), Linq.of(source).runOnce().take(new Range(Index.fromEnd(4), Index.fromEnd(1)))); } @Test void SourceNonEmptyTakeAllButOneNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{2, 5, 9, 1})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{2, 5, 9, 1})); int[] expected = {2, 5, 9}; - assertEquals(Linq.of(expected), source.take(3)); + assertEquals(Linq.of(expected), source.runOnce().take(3)); + + assertEquals(Linq.of(expected), source.runOnce().take(new Range(Index.Start, Index.fromStart(3)))); + assertEquals(Linq.of(expected), source.runOnce().take(new Range(Index.fromEnd(4), Index.fromStart(3)))); + assertEquals(Linq.of(expected), source.runOnce().take(new Range(Index.Start, Index.fromEnd(1)))); + assertEquals(Linq.of(expected), source.runOnce().take(new Range(Index.fromEnd(4), Index.fromEnd(1)))); } @Test @@ -204,27 +283,54 @@ void SourceNonEmptyTakeExcessive() { Integer[] source = {2, 5, null, 9, 1}; assertEquals(Linq.of(source), Linq.of(source).take(source.length + 1)); + + assertEquals(Linq.of(source), Linq.of(source).take(new Range(Index.Start, Index.fromStart(source.length + 1)))); + assertEquals(Linq.of(source), Linq.of(source).take(new Range(Index.fromEnd(source.length + 1), Index.fromStart(source.length + 1)))); } @Test void SourceNonEmptyTakeExcessiveNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(2, 5, null, 9, 1)); + IEnumerable source = ForceNotCollection(Linq.of(2, 5, null, 9, 1)); assertEquals(source, source.take(source.count() + 1)); + + assertEquals(source, source.take(new Range(Index.Start, Index.fromStart(source.count() + 1)))); + assertEquals(source, source.take(new Range(Index.fromEnd(source.count() + 1), Index.fromStart(source.count() + 1)))); } @Test void ThrowsOnNullSource() { IEnumerable source = null; assertThrows(NullPointerException.class, () -> source.take(5)); + + assertThrows(NullPointerException.class, () -> source.take(new Range(Index.Start, Index.fromStart(5)))); + assertThrows(NullPointerException.class, () -> source.take(new Range(Index.fromEnd(5), Index.fromStart(5)))); + assertThrows(NullPointerException.class, () -> source.take(new Range(Index.Start, Index.End))); + assertThrows(NullPointerException.class, () -> source.take(new Range(Index.fromEnd(5), Index.End))); } @Test - void ForcedToEnumeratorDoesntEnumerate() { - IEnumerable iterator = NumberRangeGuaranteedNotCollectionType(0, 3).take(2); + void ForcedToEnumeratorDoesNotEnumerate() { + IEnumerable iterator1 = NumberRangeGuaranteedNotCollectionType(0, 3).take(2); // Don't insist on this behaviour, but check it's correct if it happens - IEnumerator en = (IEnumerator) iterator; - assertFalse(en != null && en.moveNext()); + IEnumerator en1 = (IEnumerator) iterator1; + assertFalse(en1 != null && en1.moveNext()); + + IEnumerable iterator2 = NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.Start, Index.fromStart(2))); + IEnumerator en2 = (IEnumerator) iterator2; + assertFalse(en2 != null && en2.moveNext()); + + IEnumerable iterator3 = NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.fromEnd(3), Index.fromStart(2))); + IEnumerator en3 = (IEnumerator) iterator3; + assertFalse(en3 != null && en3.moveNext()); + + IEnumerable iterator4 = NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.Start, Index.fromEnd(1))); + IEnumerator en4 = (IEnumerator) iterator4; + assertFalse(en4 != null && en4.moveNext()); + + IEnumerable iterator5 = NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.fromEnd(3), Index.fromEnd(1))); + IEnumerator en5 = (IEnumerator) iterator5; + assertFalse(en5 != null && en5.moveNext()); } @Test @@ -232,14 +338,46 @@ void Count() { assertEquals(2, NumberRangeGuaranteedNotCollectionType(0, 3).take(2).count()); assertEquals(2, Linq.of(new int[]{1, 2, 3}).take(2).count()); assertEquals(0, NumberRangeGuaranteedNotCollectionType(0, 3).take(0).count()); + + assertEquals(2, NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.Start, Index.fromStart(2))).count()); + assertEquals(2, Linq.of(new int[]{1, 2, 3}).take(new Range(Index.Start, Index.fromStart(2))).count()); + assertEquals(0, NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.Start, Index.Start)).count()); + + assertEquals(2, NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.fromEnd(3), Index.fromStart(2))).count()); + assertEquals(2, Linq.of(new int[]{1, 2, 3}).take(new Range(Index.fromEnd(3), Index.fromStart(2))).count()); + assertEquals(0, NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.fromEnd(3), Index.Start)).count()); + + assertEquals(2, NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.Start, Index.fromEnd(1))).count()); + assertEquals(2, Linq.of(new int[]{1, 2, 3}).take(new Range(Index.Start, Index.fromEnd(1))).count()); + assertEquals(0, NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.Start, Index.fromEnd(3))).count()); + + assertEquals(2, NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.fromEnd(3), Index.fromEnd(1))).count()); + assertEquals(2, Linq.of(new int[]{1, 2, 3}).take(new Range(Index.fromEnd(3), Index.fromEnd(1))).count()); + assertEquals(0, NumberRangeGuaranteedNotCollectionType(0, 3).take(new Range(Index.fromEnd(3), Index.fromEnd(3))).count()); } @Test void ForcedToEnumeratorDoesntEnumerateIList() { - IEnumerable iterator = Linq.of(NumberRangeGuaranteedNotCollectionType(0, 3).toList()).take(2); + IEnumerable iterator1 = Linq.of(NumberRangeGuaranteedNotCollectionType(0, 3).toList()).take(2); // Don't insist on this behaviour, but check it's correct if it happens - IEnumerator en = (IEnumerator) iterator; - assertFalse(en != null && en.moveNext()); + IEnumerator en1 = (IEnumerator) iterator1; + assertFalse(en1 != null && en1.moveNext()); + + IEnumerable iterator2 = Linq.of(NumberRangeGuaranteedNotCollectionType(0, 3).toList()).take(new Range(Index.Start, Index.fromStart(2))); + IEnumerator en2 = (IEnumerator) iterator2; + assertFalse(en2 != null && en2.moveNext()); + + IEnumerable iterator3 = Linq.of(NumberRangeGuaranteedNotCollectionType(0, 3).toList()).take(new Range(Index.fromEnd(3), Index.fromStart(2))); + IEnumerator en3 = (IEnumerator) iterator3; + assertFalse(en3 != null && en3.moveNext()); + + IEnumerable iterator4 = Linq.of(NumberRangeGuaranteedNotCollectionType(0, 3).toList()).take(new Range(Index.Start, Index.fromEnd(1))); + IEnumerator en4 = (IEnumerator) iterator4; + assertFalse(en4 != null && en4.moveNext()); + + IEnumerable iterator5 = Linq.of(NumberRangeGuaranteedNotCollectionType(0, 3).toList()).take(new Range(Index.fromEnd(3), Index.fromEnd(1))); + IEnumerator en5 = (IEnumerator) iterator5; + assertFalse(en5 != null && en5.moveNext()); } @Test @@ -247,6 +385,11 @@ void FollowWithTake() { int[] source = new int[]{5, 6, 7, 8}; int[] expected = new int[]{5, 6}; assertEquals(Linq.of(expected), Linq.of(source).take(5).take(3).take(2).take(40)); + + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.Start, Index.fromStart(5))).take(new Range(Index.Start, Index.fromStart(3))).take(new Range(Index.Start, Index.fromStart(2))).take(new Range(Index.Start, Index.fromStart(40)))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.fromEnd(4), Index.fromStart(5))).take(new Range(Index.fromEnd(4), Index.fromStart(3))).take(new Range(Index.fromEnd(3), Index.fromStart(2))).take(new Range(Index.fromEnd(2), Index.fromStart(40)))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.Start, Index.End)).take(new Range(Index.Start, Index.fromEnd(1))).take(new Range(Index.Start, Index.fromEnd(1))).take(new Range(Index.Start, Index.End))); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.fromEnd(4), Index.End)).take(new Range(Index.fromEnd(4), Index.fromEnd(1))).take(new Range(Index.fromEnd(3), Index.fromEnd(1))).take(new Range(Index.fromEnd(2), Index.End))); } @Test @@ -254,6 +397,11 @@ void FollowWithTakeNotIList() { IEnumerable source = NumberRangeGuaranteedNotCollectionType(5, 4); int[] expected = new int[]{5, 6}; assertEquals(Linq.of(expected), source.take(5).take(3).take(2)); + + assertEquals(Linq.of(expected), source.take(new Range(Index.Start, Index.fromStart(5))).take(new Range(Index.Start, Index.fromStart(3))).take(new Range(Index.Start, Index.fromStart(2)))); + assertEquals(Linq.of(expected), source.take(new Range(Index.fromEnd(4), Index.fromStart(5))).take(new Range(Index.fromEnd(4), Index.fromStart(3))).take(new Range(Index.fromEnd(3), Index.fromStart(2)))); + assertEquals(Linq.of(expected), source.take(new Range(Index.Start, Index.End)).take(new Range(Index.Start, Index.fromEnd(1))).take(new Range(Index.Start, Index.fromEnd(1)))); + assertEquals(Linq.of(expected), source.take(new Range(Index.fromEnd(4), Index.End)).take(new Range(Index.fromEnd(4), Index.fromEnd(1))).take(new Range(Index.fromEnd(3), Index.fromEnd(1)))); } @Test @@ -261,6 +409,11 @@ void FollowWithSkip() { int[] source = new int[]{1, 2, 3, 4, 5, 6}; int[] expected = new int[]{3, 4, 5}; assertEquals(Linq.of(expected), Linq.of(source).take(5).skip(2).skip(-4)); + + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.Start, Index.fromStart(5))).skip(2).skip(-4)); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.fromEnd(6), Index.fromStart(5))).skip(2).skip(-4)); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.Start, Index.fromEnd(1))).skip(2).skip(-4)); + assertEquals(Linq.of(expected), Linq.of(source).take(new Range(Index.fromEnd(6), Index.fromEnd(1))).skip(2).skip(-4)); } @Test @@ -268,46 +421,147 @@ void FollowWithSkipNotIList() { IEnumerable source = NumberRangeGuaranteedNotCollectionType(1, 6); int[] expected = new int[]{3, 4, 5}; assertEquals(Linq.of(expected), source.take(5).skip(2).skip(-4)); + + assertEquals(Linq.of(expected), source.take(new Range(Index.Start, Index.fromStart(5))).skip(2).skip(-4)); + assertEquals(Linq.of(expected), source.take(new Range(Index.fromEnd(6), Index.fromStart(5))).skip(2).skip(-4)); + assertEquals(Linq.of(expected), source.take(new Range(Index.Start, Index.fromEnd(1))).skip(2).skip(-4)); + assertEquals(Linq.of(expected), source.take(new Range(Index.fromEnd(6), Index.fromEnd(1))).skip(2).skip(-4)); } @Test void ElementAt() { int[] source = new int[]{1, 2, 3, 4, 5, 6}; - IEnumerable taken = Linq.of(source).take(3); - assertEquals(1, taken.elementAt(0)); - assertEquals(3, taken.elementAt(2)); - assertThrows(ArgumentOutOfRangeException.class, () -> taken.elementAt(-1)); - assertThrows(ArgumentOutOfRangeException.class, () -> taken.elementAt(3)); + IEnumerable taken0 = Linq.of(source).take(3); + assertEquals(1, taken0.elementAt(0)); + assertEquals(3, taken0.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken0.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken0.elementAt(3)); + + IEnumerable taken1 = Linq.of(source).take(new Range(Index.Start, Index.fromStart(3))); + assertEquals(1, taken1.elementAt(0)); + assertEquals(3, taken1.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken1.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken1.elementAt(3)); + + IEnumerable taken2 = Linq.of(source).take(new Range(Index.fromEnd(6), Index.fromStart(3))); + assertEquals(1, taken2.elementAt(0)); + assertEquals(3, taken2.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken2.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken2.elementAt(3)); + + IEnumerable taken3 = Linq.of(source).take(new Range(Index.Start, Index.fromEnd(3))); + assertEquals(1, taken3.elementAt(0)); + assertEquals(3, taken3.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken3.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken3.elementAt(3)); + + IEnumerable taken4 = Linq.of(source).take(new Range(Index.fromEnd(6), Index.fromEnd(3))); + assertEquals(1, taken4.elementAt(0)); + assertEquals(3, taken4.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken4.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken4.elementAt(3)); } @Test void ElementAtNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5, 6})); - IEnumerable taken = source.take(3); - assertEquals(1, taken.elementAt(0)); - assertEquals(3, taken.elementAt(2)); - assertThrows(ArgumentOutOfRangeException.class, () -> taken.elementAt(-1)); - assertThrows(ArgumentOutOfRangeException.class, () -> taken.elementAt(3)); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5, 6})); + IEnumerable taken0 = source.take(3); + assertEquals(1, taken0.elementAt(0)); + assertEquals(3, taken0.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken0.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken0.elementAt(3)); + + IEnumerable taken1 = source.take(new Range(Index.Start, Index.fromStart(3))); + assertEquals(1, taken1.elementAt(0)); + assertEquals(3, taken1.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken1.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken1.elementAt(3)); + + IEnumerable taken2 = source.take(new Range(Index.fromEnd(6), Index.fromStart(3))); + assertEquals(1, taken2.elementAt(0)); + assertEquals(3, taken2.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken2.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken2.elementAt(3)); + + IEnumerable taken3 = source.take(new Range(Index.Start, Index.fromEnd(3))); + assertEquals(1, taken3.elementAt(0)); + assertEquals(3, taken3.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken3.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken3.elementAt(3)); + + IEnumerable taken4 = source.take(new Range(Index.fromEnd(6), Index.fromEnd(3))); + assertEquals(1, taken4.elementAt(0)); + assertEquals(3, taken4.elementAt(2)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken4.elementAt(-1)); + assertThrows(ArgumentOutOfRangeException.class, () -> taken4.elementAt(3)); } @Test void ElementAtOrDefault() { int[] source = new int[]{1, 2, 3, 4, 5, 6}; - IEnumerable taken = Linq.of(source).take(3); - assertEquals(1, taken.elementAtOrDefault(0)); - assertEquals(3, taken.elementAtOrDefault(2)); - assertEquals(null, taken.elementAtOrDefault(-1)); - assertEquals(null, taken.elementAtOrDefault(3)); + IEnumerable taken0 = Linq.of(source).take(3); + assertEquals(1, taken0.elementAtOrDefault(0)); + assertEquals(3, taken0.elementAtOrDefault(2)); + assertEquals(null, taken0.elementAtOrDefault(-1)); + assertEquals(null, taken0.elementAtOrDefault(3)); + + IEnumerable taken1 = Linq.of(source).take(new Range(Index.Start, Index.fromStart(3))); + assertEquals(1, taken1.elementAtOrDefault(0)); + assertEquals(3, taken1.elementAtOrDefault(2)); + assertEquals(null, taken1.elementAtOrDefault(-1)); + assertEquals(null, taken1.elementAtOrDefault(3)); + + IEnumerable taken2 = Linq.of(source).take(new Range(Index.fromEnd(6), Index.fromStart(3))); + assertEquals(1, taken2.elementAtOrDefault(0)); + assertEquals(3, taken2.elementAtOrDefault(2)); + assertEquals(null, taken2.elementAtOrDefault(-1)); + assertEquals(null, taken2.elementAtOrDefault(3)); + + IEnumerable taken3 = Linq.of(source).take(new Range(Index.Start, Index.fromEnd(3))); + assertEquals(1, taken3.elementAtOrDefault(0)); + assertEquals(3, taken3.elementAtOrDefault(2)); + assertEquals(null, taken3.elementAtOrDefault(-1)); + assertEquals(null, taken3.elementAtOrDefault(3)); + + IEnumerable taken4 = Linq.of(source).take(new Range(Index.fromEnd(6), Index.fromEnd(3))); + assertEquals(1, taken4.elementAtOrDefault(0)); + assertEquals(3, taken4.elementAtOrDefault(2)); + assertEquals(null, taken4.elementAtOrDefault(-1)); + assertEquals(null, taken4.elementAtOrDefault(3)); } @Test void ElementAtOrDefaultNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5, 6})); - IEnumerable taken = source.take(3); - assertEquals(1, taken.elementAtOrDefault(0)); - assertEquals(3, taken.elementAtOrDefault(2)); - assertEquals(null, taken.elementAtOrDefault(-1)); - assertEquals(null, taken.elementAtOrDefault(3)); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5, 6})); + IEnumerable taken0 = source.take(3); + assertEquals(1, taken0.elementAtOrDefault(0)); + assertEquals(3, taken0.elementAtOrDefault(2)); + assertEquals(null, taken0.elementAtOrDefault(-1)); + assertEquals(null, taken0.elementAtOrDefault(3)); + + IEnumerable taken1 = source.take(new Range(Index.Start, Index.fromStart(3))); + assertEquals(1, taken1.elementAtOrDefault(0)); + assertEquals(3, taken1.elementAtOrDefault(2)); + assertEquals(null, taken1.elementAtOrDefault(-1)); + assertEquals(null, taken1.elementAtOrDefault(3)); + + IEnumerable taken2 = source.take(new Range(Index.fromEnd(6), Index.fromStart(3))); + assertEquals(1, taken2.elementAtOrDefault(0)); + assertEquals(3, taken2.elementAtOrDefault(2)); + assertEquals(null, taken2.elementAtOrDefault(-1)); + assertEquals(null, taken2.elementAtOrDefault(3)); + + IEnumerable taken3 = source.take(new Range(Index.Start, Index.fromEnd(3))); + assertEquals(1, taken3.elementAtOrDefault(0)); + assertEquals(3, taken3.elementAtOrDefault(2)); + assertEquals(null, taken3.elementAtOrDefault(-1)); + assertEquals(null, taken3.elementAtOrDefault(3)); + + IEnumerable taken4 = source.take(new Range(Index.fromEnd(6), Index.fromEnd(3))); + assertEquals(1, taken4.elementAtOrDefault(0)); + assertEquals(3, taken4.elementAtOrDefault(2)); + assertEquals(null, taken4.elementAtOrDefault(-1)); + assertEquals(null, taken4.elementAtOrDefault(3)); } @Test @@ -318,16 +572,64 @@ void First() { assertEquals(1, Linq.of(source).take(40).first()); assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(0).first()); assertThrows(InvalidOperationException.class, () -> Linq.of(source).skip(5).take(10).first()); + + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(1))).first()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(4))).first()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(40))).first()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(new Range(Index.Start, Index.Start)).first()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).skip(5).take(new Range(Index.Start, Index.fromStart(10))).first()); + + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(1))).first()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(4))).first()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(40))).first()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(new Range(Index.fromEnd(5), Index.Start)).first()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).skip(5).take(new Range(Index.fromEnd(5), Index.fromStart(10))).first()); + + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(4))).first()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(1))).first()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.End)).first()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(new Range(Index.Start, Index.fromEnd(5))).first()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).skip(5).take(new Range(Index.Start, Index.fromEnd(5))).first()); + + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4))).first()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(1))).first()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.End)).first()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(5))).first()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).skip(5).take(new Range(Index.fromEnd(10), Index.End)).first()); } @Test void FirstNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5})); assertEquals(1, source.take(1).first()); assertEquals(1, source.take(4).first()); assertEquals(1, source.take(40).first()); assertThrows(InvalidOperationException.class, () -> source.take(0).first()); assertThrows(InvalidOperationException.class, () -> source.skip(5).take(10).first()); + + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(1))).first()); + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(4))).first()); + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(40))).first()); + assertThrows(InvalidOperationException.class, () -> source.take(new Range(Index.Start, Index.Start)).first()); + assertThrows(InvalidOperationException.class, () -> source.skip(5).take(new Range(Index.Start, Index.fromStart(10))).first()); + + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(1))).first()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(4))).first()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(40))).first()); + assertThrows(InvalidOperationException.class, () -> source.take(new Range(Index.fromEnd(5), Index.Start)).first()); + assertThrows(InvalidOperationException.class, () -> source.skip(5).take(new Range(Index.fromEnd(5), Index.fromStart(10))).first()); + + assertEquals(1, source.take(new Range(Index.Start, Index.fromEnd(4))).first()); + assertEquals(1, source.take(new Range(Index.Start, Index.fromEnd(1))).first()); + assertEquals(1, source.take(new Range(Index.Start, Index.End)).first()); + assertThrows(InvalidOperationException.class, () -> source.take(new Range(Index.Start, Index.fromEnd(5))).first()); + assertThrows(InvalidOperationException.class, () -> source.skip(5).take(new Range(Index.Start, Index.fromEnd(5))).first()); + + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromEnd(4))).first()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromEnd(1))).first()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.End)).first()); + assertThrows(InvalidOperationException.class, () -> source.take(new Range(Index.fromEnd(5), Index.fromEnd(5))).first()); + assertThrows(InvalidOperationException.class, () -> source.skip(5).take(new Range(Index.fromEnd(10), Index.End)).first()); } @Test @@ -338,16 +640,64 @@ void FirstOrDefault() { assertEquals(1, Linq.of(source).take(40).firstOrDefault()); assertEquals(null, Linq.of(source).take(0).firstOrDefault()); assertEquals(null, Linq.of(source).skip(5).take(10).firstOrDefault()); + + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(1))).firstOrDefault()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(4))).firstOrDefault()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(40))).firstOrDefault()); + assertEquals(null, Linq.of(source).take(new Range(Index.Start, Index.Start)).firstOrDefault()); + assertEquals(null, Linq.of(source).skip(5).take(new Range(Index.Start, Index.fromStart(10))).firstOrDefault()); + + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(1))).firstOrDefault()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(4))).firstOrDefault()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(40))).firstOrDefault()); + assertEquals(null, Linq.of(source).take(new Range(Index.fromEnd(5), Index.Start)).firstOrDefault()); + assertEquals(null, Linq.of(source).skip(5).take(new Range(Index.fromEnd(10), Index.fromStart(10))).firstOrDefault()); + + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(4))).firstOrDefault()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(1))).firstOrDefault()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.End)).firstOrDefault()); + assertEquals(null, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(5))).firstOrDefault()); + assertEquals(null, Linq.of(source).skip(5).take(new Range(Index.Start, Index.fromEnd(10))).firstOrDefault()); + + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4))).firstOrDefault()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(1))).firstOrDefault()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.End)).firstOrDefault()); + assertEquals(null, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(5))).firstOrDefault()); + assertEquals(null, Linq.of(source).skip(5).take(new Range(Index.fromEnd(10), Index.End)).firstOrDefault()); } @Test void FirstOrDefaultNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5})); assertEquals(1, source.take(1).firstOrDefault()); assertEquals(1, source.take(4).firstOrDefault()); assertEquals(1, source.take(40).firstOrDefault()); assertEquals(null, source.take(0).firstOrDefault()); assertEquals(null, source.skip(5).take(10).firstOrDefault()); + + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(1))).firstOrDefault()); + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(4))).firstOrDefault()); + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(40))).firstOrDefault()); + assertEquals(null, source.take(new Range(Index.Start, Index.Start)).firstOrDefault()); + assertEquals(null, source.skip(5).take(new Range(Index.Start, Index.fromStart(10))).firstOrDefault()); + + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(1))).firstOrDefault()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(4))).firstOrDefault()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(40))).firstOrDefault()); + assertEquals(null, source.take(new Range(Index.fromEnd(5), Index.Start)).firstOrDefault()); + assertEquals(null, source.skip(5).take(new Range(Index.fromEnd(10), Index.fromStart(10))).firstOrDefault()); + + assertEquals(1, source.take(new Range(Index.Start, Index.fromEnd(4))).firstOrDefault()); + assertEquals(1, source.take(new Range(Index.Start, Index.fromEnd(1))).firstOrDefault()); + assertEquals(1, source.take(new Range(Index.Start, Index.End)).firstOrDefault()); + assertEquals(null, source.take(new Range(Index.Start, Index.fromEnd(5))).firstOrDefault()); + assertEquals(null, source.skip(5).take(new Range(Index.Start, Index.fromEnd(10))).firstOrDefault()); + + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromEnd(4))).firstOrDefault()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromEnd(1))).firstOrDefault()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.End)).firstOrDefault()); + assertEquals(null, source.take(new Range(Index.fromEnd(5), Index.fromEnd(5))).firstOrDefault()); + assertEquals(null, source.skip(5).take(new Range(Index.fromEnd(10), Index.End)).firstOrDefault()); } @Test @@ -358,16 +708,64 @@ void Last() { assertEquals(5, Linq.of(source).take(40).last()); assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(0).last()); assertThrows(InvalidOperationException.class, () -> Linq.empty().take(40).last()); + + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(1))).last()); + assertEquals(5, Linq.of(source).take(new Range(Index.Start, Index.fromStart(5))).last()); + assertEquals(5, Linq.of(source).take(new Range(Index.Start, Index.fromStart(40))).last()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(new Range(Index.Start, Index.Start)).last()); + assertThrows(InvalidOperationException.class, () -> Linq.empty().take(new Range(Index.Start, Index.fromStart(40))).last()); + + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(1))).last()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(5))).last()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(40))).last()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(new Range(Index.fromEnd(5), Index.Start)).last()); + assertThrows(InvalidOperationException.class, () -> Linq.empty().take(new Range(Index.fromEnd(5), Index.fromStart(40))).last()); + + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(4))).last()); + assertEquals(5, Linq.of(source).take(new Range(Index.Start, Index.End)).last()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromStart(3), Index.End)).last()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(new Range(Index.Start, Index.fromEnd(5))).last()); + assertThrows(InvalidOperationException.class, () -> Linq.empty().take(new Range(Index.Start, Index.End)).last()); + + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4))).last()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromEnd(5), Index.End)).last()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromEnd(5), Index.End)).last()); + assertThrows(InvalidOperationException.class, () -> Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(5))).last()); + assertThrows(InvalidOperationException.class, () -> Linq.empty().take(new Range(Index.fromEnd(40), Index.End)).last()); } @Test void LastNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5})); assertEquals(1, source.take(1).last()); assertEquals(5, source.take(5).last()); assertEquals(5, source.take(40).last()); assertThrows(InvalidOperationException.class, () -> source.take(0).last()); - assertThrows(InvalidOperationException.class, () -> GuaranteeNotIList(Linq.empty()).take(40).last()); + assertThrows(InvalidOperationException.class, () -> ForceNotCollection(Linq.empty()).take(40).last()); + + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(1))).last()); + assertEquals(5, source.take(new Range(Index.Start, Index.fromStart(5))).last()); + assertEquals(5, source.take(new Range(Index.Start, Index.fromStart(40))).last()); + assertThrows(InvalidOperationException.class, () -> source.take(new Range(Index.Start, Index.Start)).last()); + assertThrows(InvalidOperationException.class, () -> ForceNotCollection(Linq.empty()).take(new Range(Index.Start, Index.fromStart(40))).last()); + + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(1))).last()); + assertEquals(5, source.take(new Range(Index.fromEnd(5), Index.fromStart(5))).last()); + assertEquals(5, source.take(new Range(Index.fromEnd(5), Index.fromStart(40))).last()); + assertThrows(InvalidOperationException.class, () -> source.take(new Range(Index.fromEnd(5), Index.Start)).last()); + assertThrows(InvalidOperationException.class, () -> ForceNotCollection(Linq.empty()).take(new Range(Index.fromEnd(5), Index.fromStart(40))).last()); + + assertEquals(1, source.take(new Range(Index.Start, Index.fromEnd(4))).last()); + assertEquals(5, source.take(new Range(Index.Start, Index.End)).last()); + assertEquals(5, source.take(new Range(Index.fromStart(3), Index.End)).last()); + assertThrows(InvalidOperationException.class, () -> source.take(new Range(Index.Start, Index.fromEnd(5))).last()); + assertThrows(InvalidOperationException.class, () -> ForceNotCollection(Linq.empty()).take(new Range(Index.Start, Index.End)).last()); + + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromEnd(4))).last()); + assertEquals(5, source.take(new Range(Index.fromEnd(5), Index.End)).last()); + assertEquals(5, source.take(new Range(Index.fromEnd(5), Index.End)).last()); + assertThrows(InvalidOperationException.class, () -> source.take(new Range(Index.fromEnd(5), Index.fromEnd(5))).last()); + assertThrows(InvalidOperationException.class, () -> ForceNotCollection(Linq.empty()).take(new Range(Index.fromEnd(40), Index.End)).last()); } @Test @@ -378,16 +776,64 @@ void LastOrDefault() { assertEquals(5, Linq.of(source).take(40).lastOrDefault()); assertEquals(null, Linq.of(source).take(0).lastOrDefault()); assertEquals(null, Linq.empty().take(40).lastOrDefault()); + + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(1))).lastOrDefault()); + assertEquals(5, Linq.of(source).take(new Range(Index.Start, Index.fromStart(5))).lastOrDefault()); + assertEquals(5, Linq.of(source).take(new Range(Index.Start, Index.fromStart(40))).lastOrDefault()); + assertEquals(null, Linq.of(source).take(new Range(Index.Start, Index.Start)).lastOrDefault()); + assertEquals(null, Linq.empty().take(new Range(Index.Start, Index.fromStart(40))).lastOrDefault()); + + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(1))).lastOrDefault()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(5))).lastOrDefault()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(40))).lastOrDefault()); + assertEquals(null, Linq.of(source).take(new Range(Index.fromEnd(5), Index.Start)).lastOrDefault()); + assertEquals(null, Linq.empty().take(new Range(Index.fromEnd(5), Index.fromStart(40))).lastOrDefault()); + + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(4))).lastOrDefault()); + assertEquals(5, Linq.of(source).take(new Range(Index.Start, Index.End)).lastOrDefault()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromStart(3), Index.End)).lastOrDefault()); + assertEquals(null, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(5))).lastOrDefault()); + assertEquals(null, Linq.empty().take(new Range(Index.Start, Index.End)).lastOrDefault()); + + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4))).lastOrDefault()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromEnd(5), Index.End)).lastOrDefault()); + assertEquals(5, Linq.of(source).take(new Range(Index.fromEnd(40), Index.End)).lastOrDefault()); + assertEquals(null, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(5))).lastOrDefault()); + assertEquals(null, Linq.empty().take(new Range(Index.fromEnd(40), Index.End)).lastOrDefault()); } @Test void LastOrDefaultNotIList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5})); assertEquals(1, source.take(1).lastOrDefault()); assertEquals(5, source.take(5).lastOrDefault()); assertEquals(5, source.take(40).lastOrDefault()); assertEquals(null, source.take(0).lastOrDefault()); - assertEquals(null, GuaranteeNotIList(Linq.empty()).take(40).lastOrDefault()); + assertEquals(null, ForceNotCollection(Linq.empty()).take(40).lastOrDefault()); + + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(1))).lastOrDefault()); + assertEquals(5, source.take(new Range(Index.Start, Index.fromStart(5))).lastOrDefault()); + assertEquals(5, source.take(new Range(Index.Start, Index.fromStart(40))).lastOrDefault()); + assertEquals(null, source.take(new Range(Index.Start, Index.Start)).lastOrDefault()); + assertEquals(null, ForceNotCollection(Linq.empty()).take(new Range(Index.Start, Index.fromStart(40))).lastOrDefault()); + + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(1))).lastOrDefault()); + assertEquals(5, source.take(new Range(Index.fromEnd(5), Index.fromStart(5))).lastOrDefault()); + assertEquals(5, source.take(new Range(Index.fromEnd(5), Index.fromStart(40))).lastOrDefault()); + assertEquals(null, source.take(new Range(Index.fromEnd(5), Index.Start)).lastOrDefault()); + assertEquals(null, ForceNotCollection(Linq.empty()).take(new Range(Index.fromEnd(5), Index.fromStart(40))).lastOrDefault()); + + assertEquals(1, source.take(new Range(Index.Start, Index.fromEnd(4))).lastOrDefault()); + assertEquals(5, source.take(new Range(Index.Start, Index.End)).lastOrDefault()); + assertEquals(5, source.take(new Range(Index.fromStart(3), Index.End)).lastOrDefault()); + assertEquals(null, source.take(new Range(Index.Start, Index.fromEnd(5))).lastOrDefault()); + assertEquals(null, ForceNotCollection(Linq.empty()).take(new Range(Index.Start, Index.End)).lastOrDefault()); + + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromEnd(4))).lastOrDefault()); + assertEquals(5, source.take(new Range(Index.fromEnd(5), Index.End)).lastOrDefault()); + assertEquals(5, source.take(new Range(Index.fromEnd(40), Index.End)).lastOrDefault()); + assertEquals(null, source.take(new Range(Index.fromEnd(5), Index.fromEnd(5))).lastOrDefault()); + assertEquals(null, ForceNotCollection(Linq.empty()).take(new Range(Index.fromEnd(40), Index.End)).lastOrDefault()); } @Test @@ -400,11 +846,40 @@ void ToArray() { assertEquals(1, Linq.of(source).take(1).toArray().single()); assertEmpty(Linq.of(source).take(0).toArray()); assertEmpty(Linq.of(source).take(-10).toArray()); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.Start, Index.fromStart(5))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.Start, Index.fromStart(6))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.Start, Index.fromStart(40))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(source).take(new Range(Index.Start, Index.fromStart(4))).toArray()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromStart(1))).toArray().single()); + assertEmpty(Linq.of(source).take(new Range(Index.Start, Index.Start)).toArray()); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(5))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(6))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(40))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(4))).toArray()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(1))).toArray().single()); + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(5), Index.Start)).toArray()); + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(15), Index.Start)).toArray()); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.Start, Index.End)).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(source).take(new Range(Index.Start, Index.fromEnd(1))).toArray()); + assertEquals(1, Linq.of(source).take(new Range(Index.Start, Index.fromEnd(4))).toArray().single()); + assertEmpty(Linq.of(source).take(new Range(Index.Start, Index.fromEnd(5))).toArray()); + assertEmpty(Linq.of(source).take(new Range(Index.Start, Index.fromEnd(15))).toArray()); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.End)).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.fromEnd(6), Index.End)).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source).take(new Range(Index.fromEnd(45), Index.End)).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(1))).toArray()); + assertEquals(1, Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4))).toArray().single()); + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(5))).toArray()); + assertEmpty(Linq.of(source).take(new Range(Index.fromEnd(15), Index.fromEnd(5))).toArray()); } @Test void ToArrayNotList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5})); assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(5).toArray()); assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(6).toArray()); assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(40).toArray()); @@ -412,6 +887,35 @@ void ToArrayNotList() { assertEquals(1, source.take(1).toArray().single()); assertEmpty(source.take(0).toArray()); assertEmpty(source.take(-10).toArray()); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.Start, Index.fromStart(5))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.Start, Index.fromStart(6))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.Start, Index.fromStart(40))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), source.take(new Range(Index.Start, Index.fromStart(4))).toArray()); + assertEquals(1, source.take(new Range(Index.Start, Index.fromStart(1))).toArray().single()); + assertEmpty(source.take(new Range(Index.Start, Index.Start)).toArray()); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.fromEnd(5), Index.fromStart(5))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.fromEnd(5), Index.fromStart(6))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.fromEnd(5), Index.fromStart(40))).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), source.take(new Range(Index.fromEnd(5), Index.fromStart(4))).toArray()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromStart(1))).toArray().single()); + assertEmpty(source.take(new Range(Index.fromEnd(5), Index.Start)).toArray()); + assertEmpty(source.take(new Range(Index.fromEnd(15), Index.Start)).toArray()); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.Start, Index.End)).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), source.take(new Range(Index.Start, Index.fromEnd(1))).toArray()); + assertEquals(1, source.take(new Range(Index.Start, Index.fromEnd(4))).toArray().single()); + assertEmpty(source.take(new Range(Index.Start, Index.fromEnd(5))).toArray()); + assertEmpty(source.take(new Range(Index.Start, Index.fromEnd(15))).toArray()); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.fromEnd(5), Index.End)).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.fromEnd(6), Index.End)).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), source.take(new Range(Index.fromEnd(45), Index.End)).toArray()); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), source.take(new Range(Index.fromEnd(5), Index.fromEnd(1))).toArray()); + assertEquals(1, source.take(new Range(Index.fromEnd(5), Index.fromEnd(4))).toArray().single()); + assertEmpty(source.take(new Range(Index.fromEnd(5), Index.fromEnd(5))).toArray()); + assertEmpty(source.take(new Range(Index.fromEnd(15), Index.fromEnd(5))).toArray()); } @Test @@ -424,11 +928,40 @@ void ToList() { assertEquals(1, Linq.of(Linq.of(source).take(1).toList()).single()); assertEmpty(Linq.of(Linq.of(source).take(0).toList())); assertEmpty(Linq.of(Linq.of(source).take(-10).toList())); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromStart(5))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromStart(6))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromStart(40))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromStart(4))).toList())); + assertEquals(1, Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromStart(1))).toList()).single()); + assertEmpty(Linq.of(Linq.of(source).take(new Range(Index.Start, Index.Start)).toList())); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(5))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(6))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(40))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(4))).toList())); + assertEquals(1, Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(1))).toList()).single()); + assertEmpty(Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.Start)).toList())); + assertEmpty(Linq.of(Linq.of(source).take(new Range(Index.fromEnd(15), Index.Start)).toList())); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.Start, Index.End)).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromEnd(1))).toList())); + assertEquals(1, Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromEnd(4))).toList()).single()); + assertEmpty(Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromEnd(5))).toList())); + assertEmpty(Linq.of(Linq.of(source).take(new Range(Index.Start, Index.fromEnd(15))).toList())); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.End)).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.fromEnd(6), Index.End)).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(Linq.of(source).take(new Range(Index.fromEnd(45), Index.End)).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(1))).toList())); + assertEquals(1, Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4))).toList()).single()); + assertEmpty(Linq.of(Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(5))).toList())); + assertEmpty(Linq.of(Linq.of(source).take(new Range(Index.fromEnd(15), Index.fromEnd(5))).toList())); } @Test void ToListNotList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5})); assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(5).toList())); assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(6).toList())); assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(40).toList())); @@ -436,6 +969,35 @@ void ToListNotList() { assertEquals(1, Linq.of(source.take(1).toList()).single()); assertEmpty(Linq.of(source.take(0).toList())); assertEmpty(Linq.of(source.take(-10).toList())); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.Start, Index.fromStart(5))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.Start, Index.fromStart(6))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.Start, Index.fromStart(40))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(source.take(new Range(Index.Start, Index.fromStart(4))).toList())); + assertEquals(1, Linq.of(source.take(new Range(Index.Start, Index.fromStart(1))).toList()).single()); + assertEmpty(Linq.of(source.take(new Range(Index.Start, Index.Start)).toList())); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.fromEnd(5), Index.fromStart(5))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.fromEnd(5), Index.fromStart(6))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.fromEnd(5), Index.fromStart(40))).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(source.take(new Range(Index.fromEnd(5), Index.fromStart(4))).toList())); + assertEquals(1, Linq.of(source.take(new Range(Index.fromEnd(5), Index.fromStart(1))).toList()).single()); + assertEmpty(Linq.of(source.take(new Range(Index.fromEnd(5), Index.Start)).toList())); + assertEmpty(Linq.of(source.take(new Range(Index.fromEnd(15), Index.Start)).toList())); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.Start, Index.End)).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(source.take(new Range(Index.Start, Index.fromEnd(1))).toList())); + assertEquals(1, Linq.of(source.take(new Range(Index.Start, Index.fromEnd(4))).toList()).single()); + assertEmpty(Linq.of(source.take(new Range(Index.Start, Index.fromEnd(5))).toList())); + assertEmpty(Linq.of(source.take(new Range(Index.Start, Index.fromEnd(15))).toList())); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.fromEnd(5), Index.End)).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.fromEnd(6), Index.End)).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4, 5}), Linq.of(source.take(new Range(Index.fromEnd(45), Index.End)).toList())); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), Linq.of(source.take(new Range(Index.fromEnd(5), Index.fromEnd(1))).toList())); + assertEquals(1, Linq.of(source.take(new Range(Index.fromEnd(5), Index.fromEnd(4))).toList()).single()); + assertEmpty(Linq.of(source.take(new Range(Index.fromEnd(5), Index.fromEnd(5))).toList())); + assertEmpty(Linq.of(source.take(new Range(Index.fromEnd(15), Index.fromEnd(5))).toList())); } @Test @@ -445,29 +1007,93 @@ void TakeCanOnlyBeOneList() { assertEquals(Linq.of(new int[]{4}), Linq.of(source).skip(1).take(1)); assertEquals(Linq.of(new int[]{6}), Linq.of(source).take(3).skip(2)); assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(3).take(1)); + + assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(new int[]{4}), Linq.of(source).skip(1).take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(new int[]{6}), Linq.of(source).take(new Range(Index.Start, Index.fromStart(3))).skip(2)); + assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(new Range(Index.Start, Index.fromStart(3))).take(new Range(Index.Start, Index.fromStart(1)))); + + assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(1)))); + assertEquals(Linq.of(new int[]{4}), Linq.of(source).skip(1).take(new Range(Index.fromEnd(4), Index.fromStart(1)))); + assertEquals(Linq.of(new int[]{6}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(3))).skip(2)); + assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(3))).take(new Range(Index.fromEnd(4), Index.fromStart(1)))); + + assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(new Range(Index.Start, Index.fromEnd(4)))); + assertEquals(Linq.of(new int[]{4}), Linq.of(source).skip(1).take(new Range(Index.Start, Index.fromEnd(3)))); + assertEquals(Linq.of(new int[]{6}), Linq.of(source).take(new Range(Index.Start, Index.fromEnd(2))).skip(2)); + assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(new Range(Index.Start, Index.fromEnd(2))).take(new Range(Index.Start, Index.fromEnd(2)))); + + assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + assertEquals(Linq.of(new int[]{4}), Linq.of(source).skip(1).take(new Range(Index.fromEnd(4), Index.fromEnd(3)))); + assertEquals(Linq.of(new int[]{6}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(2))).skip(2)); + assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(2))).take(new Range(Index.fromEnd(4), Index.fromEnd(2)))); } @Test void TakeCanOnlyBeOneNotList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{2, 4, 6, 8, 10})); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{2, 4, 6, 8, 10})); assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(1)); assertEquals(Linq.of(new int[]{4}), Linq.of(source).skip(1).take(1)); assertEquals(Linq.of(new int[]{6}), Linq.of(source).take(3).skip(2)); assertEquals(Linq.of(new int[]{2}), Linq.of(source).take(3).take(1)); + + assertEquals(Linq.of(new int[]{2}), source.take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(new int[]{4}), source.skip(1).take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(new int[]{6}), source.take(new Range(Index.Start, Index.fromStart(3))).skip(2)); + assertEquals(Linq.of(new int[]{2}), source.take(new Range(Index.Start, Index.fromStart(3))).take(new Range(Index.Start, Index.fromStart(1)))); + + assertEquals(Linq.of(new int[]{2}), source.take(new Range(Index.fromEnd(5), Index.fromStart(1)))); + assertEquals(Linq.of(new int[]{4}), source.skip(1).take(new Range(Index.fromEnd(4), Index.fromStart(1)))); + assertEquals(Linq.of(new int[]{6}), source.take(new Range(Index.fromEnd(5), Index.fromStart(3))).skip(2)); + assertEquals(Linq.of(new int[]{2}), source.take(new Range(Index.fromEnd(5), Index.fromStart(3))).take(new Range(Index.fromEnd(4), Index.fromStart(1)))); + + assertEquals(Linq.of(new int[]{2}), source.take(new Range(Index.Start, Index.fromEnd(4)))); + assertEquals(Linq.of(new int[]{4}), source.skip(1).take(new Range(Index.Start, Index.fromEnd(3)))); + assertEquals(Linq.of(new int[]{6}), source.take(new Range(Index.Start, Index.fromEnd(2))).skip(2)); + assertEquals(Linq.of(new int[]{2}), source.take(new Range(Index.Start, Index.fromEnd(2))).take(new Range(Index.Start, Index.fromEnd(2)))); + + assertEquals(Linq.of(new int[]{2}), source.take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + assertEquals(Linq.of(new int[]{4}), source.skip(1).take(new Range(Index.fromEnd(4), Index.fromEnd(3)))); + assertEquals(Linq.of(new int[]{6}), source.take(new Range(Index.fromEnd(5), Index.fromEnd(2))).skip(2)); + assertEquals(Linq.of(new int[]{2}), source.take(new Range(Index.fromEnd(5), Index.fromEnd(2))).take(new Range(Index.fromEnd(4), Index.fromEnd(2)))); } @Test void RepeatEnumerating() { int[] source = new int[]{1, 2, 3, 4, 5}; - IEnumerable taken = Linq.of(source).take(3); - assertEquals(taken, taken); + IEnumerable taken1 = Linq.of(source).take(3); + assertEquals(taken1, taken1); + + IEnumerable taken2 = Linq.of(source).take(new Range(Index.Start, Index.fromStart(3))); + assertEquals(taken2, taken2); + + IEnumerable taken3 = Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromStart(3))); + assertEquals(taken3, taken3); + + IEnumerable taken4 = Linq.of(source).take(new Range(Index.Start, Index.fromEnd(2))); + assertEquals(taken4, taken4); + + IEnumerable taken5 = Linq.of(source).take(new Range(Index.fromEnd(5), Index.fromEnd(2))); + assertEquals(taken5, taken5); } @Test void RepeatEnumeratingNotList() { - IEnumerable source = GuaranteeNotIList(Linq.of(new int[]{1, 2, 3, 4, 5})); - IEnumerable taken = source.take(3); - assertEquals(taken, taken); + IEnumerable source = ForceNotCollection(Linq.of(new int[]{1, 2, 3, 4, 5})); + IEnumerable taken1 = source.take(3); + assertEquals(taken1, taken1); + + IEnumerable taken2 = source.take(new Range(Index.Start, Index.fromStart(3))); + assertEquals(taken2, taken2); + + IEnumerable taken3 = source.take(new Range(Index.fromEnd(5), Index.fromStart(3))); + assertEquals(taken3, taken3); + + IEnumerable taken4 = source.take(new Range(Index.Start, Index.fromEnd(2))); + assertEquals(taken4, taken4); + + IEnumerable taken5 = source.take(new Range(Index.fromEnd(5), Index.fromEnd(2))); + assertEquals(taken5, taken5); } @ParameterizedTest @@ -476,71 +1102,892 @@ void LazySkipAllTakenForLargeNumbers(int largeNumber) { assertEmpty(new FastInfiniteEnumerator().take(largeNumber).skip(largeNumber)); assertEmpty(new FastInfiniteEnumerator().take(largeNumber).skip(largeNumber).skip(42)); assertEmpty(new FastInfiniteEnumerator().take(largeNumber).skip(largeNumber / 2).skip(largeNumber / 2 + 1)); + + assertEmpty(new FastInfiniteEnumerator().take(new Range(Index.Start, Index.fromStart(largeNumber))).skip(largeNumber)); + assertEmpty(new FastInfiniteEnumerator().take(new Range(Index.Start, Index.fromStart(largeNumber))).skip(largeNumber).skip(42)); + assertEmpty(new FastInfiniteEnumerator().take(new Range(Index.Start, Index.fromStart(largeNumber))).skip(largeNumber / 2).skip(largeNumber / 2 + 1)); } @Test void LazyOverflowRegression() { IEnumerable range = NumberRangeGuaranteedNotCollectionType(1, 100); IEnumerable skipped = range.skip(42); // Min index is 42. - IEnumerable taken = skipped.take(Integer.MAX_VALUE); // May try to calculate max index as 42 + Integer.MAX_VALUE, leading to integer overflow. - assertEquals(Linq.range(43, 100 - 42), taken); - assertEquals(100 - 42, taken.count()); - assertEquals(Linq.range(43, 100 - 42), taken.toArray()); - assertEquals(Linq.range(43, 100 - 42), Linq.of(taken.toList())); + IEnumerable taken1 = skipped.take(Integer.MAX_VALUE); // May try to calculate max index as 42 + Integer.MAX_VALUE, leading to integer overflow. + assertEquals(Linq.range(43, 100 - 42), taken1); + assertEquals(100 - 42, taken1.count()); + assertEquals(Linq.range(43, 100 - 42), taken1.toArray()); + assertEquals(Linq.range(43, 100 - 42), Linq.of(taken1.toList())); + + IEnumerable taken2 = NumberRangeGuaranteedNotCollectionType(1, 100).take(new Range(Index.fromStart(42), Index.fromStart(Integer.MAX_VALUE))); + assertEquals(Linq.range(43, 100 - 42), taken2); + assertEquals(100 - 42, taken2.count()); + assertEquals(Linq.range(43, 100 - 42), taken2.toArray()); + assertEquals(Linq.range(43, 100 - 42), Linq.of(taken2.toList())); + + IEnumerable taken3 = NumberRangeGuaranteedNotCollectionType(1, 100).take(new Range(Index.fromEnd(100 - 42), Index.fromStart(Integer.MAX_VALUE))); + assertEquals(Linq.range(43, 100 - 42), taken3); + assertEquals(100 - 42, taken3.count()); + assertEquals(Linq.range(43, 100 - 42), taken3.toArray()); + assertEquals(Linq.range(43, 100 - 42), Linq.of(taken3.toList())); + + IEnumerable taken4 = NumberRangeGuaranteedNotCollectionType(1, 100).take(new Range(Index.fromStart(42), Index.End)); + assertEquals(Linq.range(43, 100 - 42), taken4); + assertEquals(100 - 42, taken4.count()); + assertEquals(Linq.range(43, 100 - 42), taken4.toArray()); + assertEquals(Linq.range(43, 100 - 42), Linq.of(taken4.toList())); + + IEnumerable taken5 = NumberRangeGuaranteedNotCollectionType(1, 100).take(new Range(Index.fromEnd(100 - 42), Index.End)); + assertEquals(Linq.range(43, 100 - 42), taken5); + assertEquals(100 - 42, taken5.count()); + assertEquals(Linq.range(43, 100 - 42), taken5.toArray()); + assertEquals(Linq.range(43, 100 - 42), Linq.of(taken5.toList())); } @ParameterizedTest @MethodSource("CountOfLazySkipTakeChain_TestData") void CountOfLazySkipTakeChain(int skip, int take, int expected) { - IEnumerable partition = NumberRangeGuaranteedNotCollectionType(1, 100).skip(skip).take(take); + int totalCount = 100; + IEnumerable partition = NumberRangeGuaranteedNotCollectionType(1, totalCount).skip(skip).take(take); assertEquals(expected, partition.count()); assertEquals(expected, partition.select(i -> i).count()); assertEquals(expected, partition.select(i -> i).toArray()._getCount()); + + int end; + try { + end = Math.addExact(skip, take); + } catch (ArithmeticException e) { + end = Integer.MAX_VALUE; + } + + IEnumerable partition2 = NumberRangeGuaranteedNotCollectionType(1, totalCount).take(new Range(Index.fromStart(skip), Index.fromStart(end))); + assertEquals(expected, partition2.count()); + assertEquals(expected, partition2.select(i -> i).count()); + assertEquals(expected, partition2.select(i -> i).toArray()._getCount()); + + IEnumerable partition3 = NumberRangeGuaranteedNotCollectionType(1, totalCount).take(new Range(Index.fromEnd(Math.max(totalCount - skip, 0)), Index.fromStart(end))); + assertEquals(expected, partition3.count()); + assertEquals(expected, partition3.select(i -> i).count()); + assertEquals(expected, partition3.select(i -> i).toArray()._getCount()); + + IEnumerable partition4 = NumberRangeGuaranteedNotCollectionType(1, totalCount).take(new Range(Index.fromStart(skip), Index.fromEnd(Math.max(totalCount - end, 0)))); + assertEquals(expected, partition4.count()); + assertEquals(expected, partition4.select(i -> i).count()); + assertEquals(expected, partition4.select(i -> i).toArray()._getCount()); + + IEnumerable partition5 = NumberRangeGuaranteedNotCollectionType(1, totalCount).take(new Range(Index.fromEnd(Math.max(totalCount - skip, 0)), Index.fromEnd(Math.max(totalCount - end, 0)))); + assertEquals(expected, partition5.count()); + assertEquals(expected, partition5.select(i -> i).count()); + assertEquals(expected, partition5.select(i -> i).toArray()._getCount()); } @ParameterizedTest @MethodSource("FirstAndLastOfLazySkipTakeChain_TestData") void FirstAndLastOfLazySkipTakeChain(IEnumerable source, int skip, int take, Integer first, Integer last) { - IEnumerable partition = ForceNotCollection(source).skip(skip).take(take); + IEnumerable partition1 = ForceNotCollection(source).skip(skip).take(take); + + assertEquals(first, partition1.firstOrDefault()); + assertEquals(first, partition1.elementAtOrDefault(0)); + assertEquals(last, partition1.lastOrDefault()); + assertEquals(last, partition1.elementAtOrDefault(partition1.count() - 1)); + + int end; + try { + end = Math.addExact(skip, take); + } catch (ArithmeticException e) { + end = Integer.MAX_VALUE; + } + + IEnumerable partition2 = ForceNotCollection(source).take(new Range(Index.fromStart(skip), Index.fromStart(end))); + + assertEquals(first, partition2.firstOrDefault()); + assertEquals(first, partition2.elementAtOrDefault(0)); + assertEquals(last, partition2.lastOrDefault()); + assertEquals(last, partition2.elementAtOrDefault(partition2.count() - 1)); + + IEnumerable partition3 = ForceNotCollection(source).take(new Range(Index.fromEnd(Math.max(source.count() - skip, 0)), Index.fromStart(end))); - assertEquals(first, partition.firstOrDefault()); - assertEquals(first, partition.elementAtOrDefault(0)); - assertEquals(last, partition.lastOrDefault()); - assertEquals(last, partition.elementAtOrDefault(partition.count() - 1)); + assertEquals(first, partition3.firstOrDefault()); + assertEquals(first, partition3.elementAtOrDefault(0)); + assertEquals(last, partition3.lastOrDefault()); + assertEquals(last, partition3.elementAtOrDefault(partition3.count() - 1)); + + IEnumerable partition4 = ForceNotCollection(source).take(new Range(Index.fromStart(skip), Index.fromEnd(Math.max(source.count() - end, 0)))); + + assertEquals(first, partition4.firstOrDefault()); + assertEquals(first, partition4.elementAtOrDefault(0)); + assertEquals(last, partition4.lastOrDefault()); + assertEquals(last, partition4.elementAtOrDefault(partition4.count() - 1)); + + IEnumerable partition5 = ForceNotCollection(source).take(new Range(Index.fromEnd(Math.max(source.count() - skip, 0)), Index.fromEnd(Math.max(source.count() - end, 0)))); + + assertEquals(first, partition5.firstOrDefault()); + assertEquals(first, partition5.elementAtOrDefault(0)); + assertEquals(last, partition5.lastOrDefault()); + assertEquals(last, partition5.elementAtOrDefault(partition5.count() - 1)); } @ParameterizedTest @MethodSource("ElementAtOfLazySkipTakeChain_TestData") - void ElementAtOfLazySkipTakeChain(IEnumerable source, int skip, int take, int[] indices, Integer[] expectedValues) { - IEnumerable partition = ForceNotCollection(source).skip(skip).take(take); + void ElementAtOfLazySkipTakeChain(int[] source, int skip, int take, int[] indices, Integer[] expectedValues) { + IEnumerable partition1 = ForceNotCollection(Linq.of(source)).skip(skip).take(take); assertEquals(indices.length, expectedValues.length); for (int i = 0; i < indices.length; i++) { - assertEquals(expectedValues[i], partition.elementAtOrDefault(indices[i])); + assertEquals(expectedValues[i], partition1.elementAtOrDefault(indices[i])); + } + + int end; + try { + end = Math.addExact(skip, take); + } catch (ArithmeticException e) { + end = Integer.MAX_VALUE; + } + + IEnumerable partition2 = ForceNotCollection(Linq.of(source)).take(new Range(Index.fromStart(skip), Index.fromStart(end))); + for (int i = 0; i < indices.length; i++) { + assertEquals(expectedValues[i], partition2.elementAtOrDefault(indices[i])); + } + + IEnumerable partition3 = ForceNotCollection(Linq.of(source)).take(new Range(Index.fromEnd(Math.max(source.length - skip, 0)), Index.fromStart(end))); + for (int i = 0; i < indices.length; i++) { + assertEquals(expectedValues[i], partition3.elementAtOrDefault(indices[i])); + } + + IEnumerable partition4 = ForceNotCollection(Linq.of(source)).take(new Range(Index.fromStart(skip), Index.fromEnd(Math.max(source.length - end, 0)))); + for (int i = 0; i < indices.length; i++) { + assertEquals(expectedValues[i], partition4.elementAtOrDefault(indices[i])); + } + + IEnumerable partition5 = ForceNotCollection(Linq.of(source)).take(new Range(Index.fromEnd(Math.max(source.length - skip, 0)), Index.fromEnd(Math.max(source.length - end, 0)))); + for (int i = 0; i < indices.length; i++) { + assertEquals(expectedValues[i], partition5.elementAtOrDefault(indices[i])); } } @ParameterizedTest @MethodSource("DisposeSource_TestData") void DisposeSource(int sourceCount, int count) { - ref state = ref.init(0); + boolean[] isIteratorDisposed = new boolean[5]; - IEnumerable source = new DelegateIterator<>( - () -> ++state.value <= sourceCount, - () -> 0, - () -> state.value = -1); + List> source = Repeat(index -> { + ref state = ref.init(0); - IEnumerator iterator = source.take(count).enumerator(); - int iteratorCount = Math.min(sourceCount, Math.max(0, count)); - assertAll(Linq.range(0, iteratorCount), x -> assertTrue(iterator.moveNext())); + return new DelegateIterator<>( + () -> ++state.value <= sourceCount, + () -> 0, + () -> { + state.value = -1; + isIteratorDisposed[index] = true; + }); + }, 5); - assertFalse(iterator.moveNext()); + + IEnumerator iterator0 = source.get(0).take(count).enumerator(); + int iteratorCount0 = Math.min(sourceCount, Math.max(0, count)); + assertAll(Linq.range(0, iteratorCount0), x -> assertTrue(iterator0.moveNext())); + + assertFalse(iterator0.moveNext()); // Unlike Skip, Take can tell straightaway that it can return a sequence with no elements if count <= 0. // The enumerable it returns is a specialized empty iterator that has no connections to the source. Hence, // after MoveNext returns false under those circumstances, it won't invoke Dispose on our enumerator. - int expected = count <= 0 ? 0 : -1; - assertEquals(expected, state.value); + boolean isItertorNotEmpty0 = count > 0; + assertEquals(isItertorNotEmpty0, isIteratorDisposed[0]); + + int end = Math.max(0, count); + IEnumerator iterator1 = source.get(1).take(new Range(Index.Start, Index.fromStart(end))).enumerator(); + assertAll(Linq.range(0, Math.min(sourceCount, Math.max(0, count))), x -> assertTrue(iterator1.moveNext())); + assertFalse(iterator1.moveNext()); + // When startIndex end and endIndex are both not from end and startIndex >= endIndex, Take(Range) returns an empty array. + boolean isItertorNotEmpty1 = end != 0; + assertEquals(isItertorNotEmpty1, isIteratorDisposed[1]); + + int startIndexFromEnd = Math.max(sourceCount, end); + int endIndexFromEnd = Math.max(0, sourceCount - end); + + IEnumerator iterator2 = source.get(2).take(new Range(Index.fromEnd(startIndexFromEnd), Index.fromStart(end))).enumerator(); + assertAll(Linq.range(0, Math.min(sourceCount, Math.max(0, count))), x -> assertTrue(iterator2.moveNext())); + assertFalse(iterator2.moveNext()); + // When startIndex is ^0, Take(Range) returns an empty array. + boolean isIteratorNotEmpty2 = startIndexFromEnd != 0; + assertEquals(isIteratorNotEmpty2, isIteratorDisposed[2]); + + IEnumerator iterator3 = source.get(3).take(new Range(Index.Start, Index.fromEnd(endIndexFromEnd))).enumerator(); + assertAll(Linq.range(0, Math.min(sourceCount, Math.max(0, count))), x -> assertTrue(iterator3.moveNext())); + assertFalse(iterator3.moveNext()); + assertTrue(isIteratorDisposed[3]); + + IEnumerator iterator4 = source.get(4).take(new Range(Index.fromEnd(startIndexFromEnd), Index.fromEnd(endIndexFromEnd))).enumerator(); + assertAll(Linq.range(0, Math.min(sourceCount, Math.max(0, count))), x -> assertTrue(iterator4.moveNext())); + assertFalse(iterator4.moveNext()); + // When startIndex is ^0, + // or when startIndex and endIndex are both from end and startIndex <= endIndexFromEnd, Take(Range) returns an empty array. + boolean isIteratorNotEmpty4 = startIndexFromEnd != 0 && startIndexFromEnd > endIndexFromEnd; + assertEquals(isIteratorNotEmpty4, isIteratorDisposed[4]); + } + + + @Test + void DisposeSource_StartIndexFromEnd_ShouldDisposeOnFirstElement() { + final int count = 5; + ref state = ref.init(0); + IEnumerable source = new DelegateIterator<>( + () -> ++state.value <= count, + () -> state.value, + () -> state.value = -1); + + try (IEnumerator e = source.take(Range.startAt(Index.fromEnd(3))).enumerator()) { + assertTrue(e.moveNext()); + assertEquals(3, e.current()); + + assertEquals(-1, state.value); + assertTrue(e.moveNext()); + } + } + + @Test + void DisposeSource_EndIndexFromEnd_ShouldDisposeOnCompletedEnumeration() { + final int count = 5; + ref state = ref.init(0); + IEnumerable source = new DelegateIterator<>( + () -> ++state.value <= count, + () -> state.value, + () -> state.value = -1); + + try (IEnumerator e = source.take(Range.endAt(Index.fromEnd(3))).enumerator()) { + assertTrue(e.moveNext()); + assertEquals(4, state.value); + assertEquals(1, e.current()); + + assertTrue(e.moveNext()); + assertEquals(5, state.value); + assertEquals(2, e.current()); + + assertFalse(e.moveNext()); + } + assertEquals(-1, state.value); + } + + @Test + void OutOfBoundNoException() { + Func0> source = () -> Linq.of(new int[]{1, 2, 3, 4, 5}); + + assertEquals(source.apply(), source.apply().take(new Range(Index.Start, Index.fromStart(6)))); + assertEquals(source.apply(), source.apply().take(new Range(Index.Start, Index.fromStart(Integer.MAX_VALUE)))); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), source.apply().take(new Range(Index.fromEnd(10), Index.fromStart(4)))); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), source.apply().take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(4)))); + assertEquals(source.apply(), source.apply().take(new Range(Index.fromEnd(10), Index.fromStart(6)))); + assertEquals(source.apply(), source.apply().take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(6)))); + assertEquals(source.apply(), source.apply().take(new Range(Index.fromEnd(10), Index.fromStart(Integer.MAX_VALUE)))); + assertEquals(source.apply(), source.apply().take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(Integer.MAX_VALUE)))); + + assertEmpty(source.apply().take(new Range(Index.Start, Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.Start, Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(4), Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(4), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(6), Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(6), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(Integer.MAX_VALUE), Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(Integer.MAX_VALUE), Index.fromEnd(Integer.MAX_VALUE)))); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), source.apply().take(new Range(Index.fromEnd(10), Index.fromEnd(1)))); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), source.apply().take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(1)))); + assertEmpty(source.apply().take(new Range(Index.End, Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(1), Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(10), Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(6)))); + assertEmpty(source.apply().take(new Range(Index.End, Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(1), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(5), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(Integer.MAX_VALUE)))); + } + + @Test + void OutOfBoundNoExceptionNotList() { + IEnumerable source = Linq.of(new int[]{1, 2, 3, 4, 5}); + + assertEquals(source, ForceNotCollection(source).take(new Range(Index.Start, Index.fromStart(6)))); + assertEquals(source, ForceNotCollection(source).take(new Range(Index.Start, Index.fromStart(Integer.MAX_VALUE)))); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromStart(4)))); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), ForceNotCollection(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(4)))); + assertEquals(source, ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromStart(6)))); + assertEquals(source, ForceNotCollection(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(6)))); + assertEquals(source, ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromStart(Integer.MAX_VALUE)))); + assertEquals(source, ForceNotCollection(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(Integer.MAX_VALUE)))); + + assertEmpty(ForceNotCollection(source).take(new Range(Index.Start, Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.Start, Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(4), Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(4), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(6), Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(6), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(Integer.MAX_VALUE), Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(Integer.MAX_VALUE), Index.fromEnd(Integer.MAX_VALUE)))); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromEnd(1)))); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), ForceNotCollection(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(1)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.End, Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(1), Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(6)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.End, Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(1), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(5), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(Integer.MAX_VALUE)))); + } + + @Test + void OutOfBoundNoExceptionListPartition() { + IEnumerable source = Linq.of(new int[]{1, 2, 3, 4, 5}); + + assertEquals(source, ListPartitionOrEmpty(source).take(new Range(Index.Start, Index.fromStart(6)))); + assertEquals(source, ListPartitionOrEmpty(source).take(new Range(Index.Start, Index.fromStart(Integer.MAX_VALUE)))); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(4)))); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(4)))); + assertEquals(source, ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(6)))); + assertEquals(source, ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(6)))); + assertEquals(source, ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(Integer.MAX_VALUE)))); + assertEquals(source, ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(Integer.MAX_VALUE)))); + + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.Start, Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.Start, Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(4), Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(4), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(Integer.MAX_VALUE), Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(Integer.MAX_VALUE), Index.fromEnd(Integer.MAX_VALUE)))); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromEnd(1)))); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(1)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.End, Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(6)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.End, Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(5), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(Integer.MAX_VALUE)))); + } + + @Test + void OutOfBoundNoExceptionEnumerablePartition() { + IEnumerable source = Linq.of(new int[]{1, 2, 3, 4, 5}); + + assertEquals(source, EnumerablePartitionOrEmpty(source).take(new Range(Index.Start, Index.fromStart(6)))); + assertEquals(source, EnumerablePartitionOrEmpty(source).take(new Range(Index.Start, Index.fromStart(Integer.MAX_VALUE)))); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(4)))); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(4)))); + assertEquals(source, EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(6)))); + assertEquals(source, EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(6)))); + assertEquals(source, EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(Integer.MAX_VALUE)))); + assertEquals(source, EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromStart(Integer.MAX_VALUE)))); + + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.Start, Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.Start, Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(4), Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(4), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(Integer.MAX_VALUE), Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(Integer.MAX_VALUE), Index.fromEnd(Integer.MAX_VALUE)))); + + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromEnd(1)))); + assertEquals(Linq.of(new int[]{1, 2, 3, 4}), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(1)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.End, Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(6)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.End, Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(5), Index.fromEnd(Integer.MAX_VALUE)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(Integer.MAX_VALUE), Index.fromEnd(Integer.MAX_VALUE)))); + } + + @Test + void MutableSource() { + List source1 = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + IEnumerable query1 = Linq.of(source1).take(3); + source1.remove(0); + source1.addAll(2, Arrays.asList(-1, -2)); + assertEquals(Linq.of(new int[]{1, 2, -1}), query1); + + List source2 = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + IEnumerable query2 = Linq.of(source2).take(new Range(Index.Start, Index.fromStart(3))); + source2.remove(0); + source2.addAll(2, Arrays.asList(-1, -2)); + assertEquals(Linq.of(new int[]{1, 2, -1}), query2); + + List source3 = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + IEnumerable query3 = Linq.of(source3).take(new Range(Index.fromEnd(6), Index.fromStart(3))); + source3.remove(0); + source3.addAll(2, Arrays.asList(-1, -2)); + assertEquals(Linq.of(new int[]{1, 2, -1}), query3); + + List source4 = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + IEnumerable query4 = Linq.of(source4).take(new Range(Index.fromEnd(6), Index.fromEnd(3))); + source4.remove(0); + source4.addAll(2, Arrays.asList(-1, -2)); + assertEquals(Linq.of(new int[]{1, 2, -1}), query4); + } + + @Test + void MutableSourceNotList() { + List source1 = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + IEnumerable query1 = ForceNotCollection(Linq.of(source1)).select(i -> i).take(3); + source1.remove(0); + source1.addAll(2, Arrays.asList(-1, -2)); + assertEquals(Linq.of(new int[]{1, 2, -1}), query1); + + List source2 = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + IEnumerable query2 = ForceNotCollection(Linq.of(source2)).select(i -> i).take(new Range(Index.Start, Index.fromStart(3))); + source2.remove(0); + source2.addAll(2, Arrays.asList(-1, -2)); + assertEquals(Linq.of(new int[]{1, 2, -1}), query2); + + List source3 = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + IEnumerable query3 = ForceNotCollection(Linq.of(source3)).select(i -> i).take(new Range(Index.fromEnd(6), Index.fromStart(3))); + source3.remove(0); + source3.addAll(2, Arrays.asList(-1, -2)); + assertEquals(Linq.of(new int[]{1, 2, -1}), query3); + + List source4 = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4)); + IEnumerable query4 = ForceNotCollection(Linq.of(source4)).select(i -> i).take(new Range(Index.fromEnd(6), Index.fromEnd(3))); + source4.remove(0); + source4.addAll(2, Arrays.asList(-1, -2)); + assertEquals(Linq.of(new int[]{1, 2, -1}), query4); + } + + @Test + void NonEmptySource_ConsistencyWithCountable() { + Func0> source = () -> Linq.of(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + + // Multiple elements in the middle. + assertEquals(Linq.of(1, 2, 3, 4), source.apply().take(new Range(Index.fromEnd(9), Index.fromStart(5)))); + assertEquals(Linq.of(2, 3, 4, 5, 6), source.apply().take(new Range(Index.fromStart(2), Index.fromStart(7)))); + assertEquals(Linq.of(2, 3, 4, 5), source.apply().take(new Range(Index.fromStart(2), Index.fromEnd(4)))); + assertEquals(Linq.of(3, 4, 5), source.apply().take(new Range(Index.fromEnd(7), Index.fromEnd(4)))); + + // Range with default index. + assertEquals(Linq.of(1, 2, 3, 4, 5, 6, 7, 8, 9), source.apply().take(Range.startAt(Index.fromEnd(9)))); + assertEquals(Linq.of(2, 3, 4, 5, 6, 7, 8, 9), source.apply().take(Range.startAt(Index.fromStart(2)))); + assertEquals(Linq.of(0, 1, 2, 3, 4, 5), source.apply().take(Range.endAt(Index.fromEnd(4)))); + assertEquals(Linq.of(0, 1, 2, 3, 4, 5), source.apply().take(Range.endAt(Index.fromStart(6)))); + + // All. + assertEquals(Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), source.apply().take(Range.All)); + + // Single element in the middle. + assertEquals(Linq.of(1), source.apply().take(new Range(Index.fromEnd(9), Index.fromStart(2)))); + assertEquals(Linq.of(2), source.apply().take(new Range(Index.fromStart(2), Index.fromStart(3)))); + assertEquals(Linq.of(2), source.apply().take(new Range(Index.fromStart(2), Index.fromEnd(7)))); + assertEquals(Linq.of(5), source.apply().take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + + // Single element at start. + assertEquals(Linq.of(0), source.apply().take(new Range(Index.fromEnd(10), Index.fromStart(1)))); + assertEquals(Linq.of(0), source.apply().take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(0), source.apply().take(new Range(Index.Start, Index.fromEnd(9)))); + assertEquals(Linq.of(0), source.apply().take(new Range(Index.fromEnd(10), Index.fromEnd(9)))); + + // Single element at end. + assertEquals(Linq.of(9), source.apply().take(new Range(Index.fromEnd(1), Index.fromStart(10)))); + assertEquals(Linq.of(9), source.apply().take(new Range(Index.fromStart(9), Index.fromStart(10)))); + assertEquals(Linq.of(9), source.apply().take(new Range(Index.fromStart(9), Index.End))); + assertEquals(Linq.of(9), source.apply().take(new Range(Index.fromEnd(1), Index.End))); + + // No element. + assertEquals(Linq.empty(), source.apply().take(new Range(Index.fromStart(3), Index.fromStart(3)))); + assertEquals(Linq.empty(), source.apply().take(new Range(Index.fromStart(6), Index.fromEnd(4)))); + assertEquals(Linq.empty(), source.apply().take(new Range(Index.fromStart(3), Index.fromEnd(7)))); + assertEquals(Linq.empty(), source.apply().take(new Range(Index.fromEnd(3), Index.fromStart(7)))); + assertEquals(Linq.empty(), source.apply().take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + } + + @Test + void NonEmptySource_ConsistencyWithCountable_NotList() { + IEnumerable source = Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + // Multiple elements in the middle. + assertEquals(Linq.of(1, 2, 3, 4), ForceNotCollection(source).take(new Range(Index.fromEnd(9), Index.fromStart(5)))); + assertEquals(Linq.of(2, 3, 4, 5, 6), ForceNotCollection(source).take(new Range(Index.fromStart(2), Index.fromStart(7)))); + assertEquals(Linq.of(2, 3, 4, 5), ForceNotCollection(source).take(new Range(Index.fromStart(2), Index.fromEnd(4)))); + assertEquals(Linq.of(3, 4, 5), ForceNotCollection(source).take(new Range(Index.fromEnd(7), Index.fromEnd(4)))); + + // Range with default index. + assertEquals(Linq.of(1, 2, 3, 4, 5, 6, 7, 8, 9), ForceNotCollection(source).take(Range.startAt(Index.fromEnd(9)))); + assertEquals(Linq.of(2, 3, 4, 5, 6, 7, 8, 9), ForceNotCollection(source).take(Range.startAt(Index.fromStart(2)))); + assertEquals(Linq.of(0, 1, 2, 3, 4, 5), ForceNotCollection(source).take(Range.endAt(Index.fromEnd(4)))); + assertEquals(Linq.of(0, 1, 2, 3, 4, 5), ForceNotCollection(source).take(Range.endAt(Index.fromStart(6)))); + + // All. + assertEquals(Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), ForceNotCollection(source).take(Range.All)); + + // Single element in the middle. + assertEquals(Linq.of(1), ForceNotCollection(source).take(new Range(Index.fromEnd(9), Index.fromStart(2)))); + assertEquals(Linq.of(2), ForceNotCollection(source).take(new Range(Index.fromStart(2), Index.fromStart(3)))); + assertEquals(Linq.of(2), ForceNotCollection(source).take(new Range(Index.fromStart(2), Index.fromEnd(7)))); + assertEquals(Linq.of(5), ForceNotCollection(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + + // Single element at start. + assertEquals(Linq.of(0), ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromStart(1)))); + assertEquals(Linq.of(0), ForceNotCollection(source).take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(0), ForceNotCollection(source).take(new Range(Index.Start, Index.fromEnd(9)))); + assertEquals(Linq.of(0), ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromEnd(9)))); + + // Single element at end. + assertEquals(Linq.of(9), ForceNotCollection(source).take(new Range(Index.fromEnd(1), Index.fromStart(10)))); + assertEquals(Linq.of(9), ForceNotCollection(source).take(new Range(Index.fromStart(9), Index.fromStart(10)))); + assertEquals(Linq.of(9), ForceNotCollection(source).take(new Range(Index.fromStart(9), Index.End))); + assertEquals(Linq.of(9), ForceNotCollection(source).take(new Range(Index.fromEnd(1), Index.End))); + + // No element. + assertEquals(Linq.empty(), ForceNotCollection(source).take(new Range(Index.fromStart(3), Index.fromStart(3)))); + assertEquals(Linq.empty(), ForceNotCollection(source).take(new Range(Index.fromStart(6), Index.fromEnd(4)))); + assertEquals(Linq.empty(), ForceNotCollection(source).take(new Range(Index.fromStart(3), Index.fromEnd(7)))); + assertEquals(Linq.empty(), ForceNotCollection(source).take(new Range(Index.fromEnd(3), Index.fromStart(7)))); + assertEquals(Linq.empty(), ForceNotCollection(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + } + + @Test + void NonEmptySource_ConsistencyWithCountable_ListPartition() { + IEnumerable source = Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + // Multiple elements in the middle. + assertEquals(Linq.of(1, 2, 3, 4), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(9), Index.fromStart(5)))); + assertEquals(Linq.of(2, 3, 4, 5, 6), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromStart(7)))); + assertEquals(Linq.of(2, 3, 4, 5), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromEnd(4)))); + assertEquals(Linq.of(3, 4, 5), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(7), Index.fromEnd(4)))); + + // Range with default index. + assertEquals(Linq.of(1, 2, 3, 4, 5, 6, 7, 8, 9), ListPartitionOrEmpty(source).take(Range.startAt(Index.fromEnd(9)))); + assertEquals(Linq.of(2, 3, 4, 5, 6, 7, 8, 9), ListPartitionOrEmpty(source).take(Range.startAt(Index.fromStart(2)))); + assertEquals(Linq.of(0, 1, 2, 3, 4, 5), ListPartitionOrEmpty(source).take(Range.endAt(Index.fromEnd(4)))); + assertEquals(Linq.of(0, 1, 2, 3, 4, 5), ListPartitionOrEmpty(source).take(Range.endAt(Index.fromStart(6)))); + + // All. + assertEquals(Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), ListPartitionOrEmpty(source).take(Range.All)); + + // Single element in the middle. + assertEquals(Linq.of(1), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(9), Index.fromStart(2)))); + assertEquals(Linq.of(2), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromStart(3)))); + assertEquals(Linq.of(2), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromEnd(7)))); + assertEquals(Linq.of(5), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + + // Single element at start. + assertEquals(Linq.of(0), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(1)))); + assertEquals(Linq.of(0), ListPartitionOrEmpty(source).take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(0), ListPartitionOrEmpty(source).take(new Range(Index.Start, Index.fromEnd(9)))); + assertEquals(Linq.of(0), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromEnd(9)))); + + // Single element at end. + assertEquals(Linq.of(9), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromStart(10)))); + assertEquals(Linq.of(9), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(9), Index.fromStart(10)))); + assertEquals(Linq.of(9), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(9), Index.End))); + assertEquals(Linq.of(9), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.End))); + + // No element. + assertEquals(Linq.empty(), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromStart(3)))); + assertEquals(Linq.empty(), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(4)))); + assertEquals(Linq.empty(), ListPartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromEnd(7)))); + assertEquals(Linq.empty(), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(3), Index.fromStart(7)))); + assertEquals(Linq.empty(), ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + } + + @Test + void NonEmptySource_ConsistencyWithCountable_EnumerablePartition() { + IEnumerable source = Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + // Multiple elements in the middle. + assertEquals(Linq.of(1, 2, 3, 4), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(9), Index.fromStart(5)))); + assertEquals(Linq.of(2, 3, 4, 5, 6), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromStart(7)))); + assertEquals(Linq.of(2, 3, 4, 5), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromEnd(4)))); + assertEquals(Linq.of(3, 4, 5), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(7), Index.fromEnd(4)))); + + // Range with default index. + assertEquals(Linq.of(1, 2, 3, 4, 5, 6, 7, 8, 9), EnumerablePartitionOrEmpty(source).take(Range.startAt(Index.fromEnd(9)))); + assertEquals(Linq.of(2, 3, 4, 5, 6, 7, 8, 9), EnumerablePartitionOrEmpty(source).take(Range.startAt(Index.fromStart(2)))); + assertEquals(Linq.of(0, 1, 2, 3, 4, 5), EnumerablePartitionOrEmpty(source).take(Range.endAt(Index.fromEnd(4)))); + assertEquals(Linq.of(0, 1, 2, 3, 4, 5), EnumerablePartitionOrEmpty(source).take(Range.endAt(Index.fromStart(6)))); + + // All. + assertEquals(Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), EnumerablePartitionOrEmpty(source).take(Range.All)); + + // Single element in the middle. + assertEquals(Linq.of(1), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(9), Index.fromStart(2)))); + assertEquals(Linq.of(2), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromStart(3)))); + assertEquals(Linq.of(2), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromEnd(7)))); + assertEquals(Linq.of(5), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + + // Single element at start. + assertEquals(Linq.of(0), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(1)))); + assertEquals(Linq.of(0), EnumerablePartitionOrEmpty(source).take(new Range(Index.Start, Index.fromStart(1)))); + assertEquals(Linq.of(0), EnumerablePartitionOrEmpty(source).take(new Range(Index.Start, Index.fromEnd(9)))); + assertEquals(Linq.of(0), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromEnd(9)))); + + // Single element at end. + assertEquals(Linq.of(9), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromStart(10)))); + assertEquals(Linq.of(9), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(9), Index.fromStart(10)))); + assertEquals(Linq.of(9), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(9), Index.End))); + assertEquals(Linq.of(9), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.End))); + + // No element. + assertEquals(Linq.empty(), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromStart(3)))); + assertEquals(Linq.empty(), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(4)))); + assertEquals(Linq.empty(), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromEnd(7)))); + assertEquals(Linq.empty(), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(3), Index.fromStart(7)))); + assertEquals(Linq.empty(), EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + } + + @Test + void NonEmptySource_DoNotThrowException() { + Func0> source = () -> Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + assertEmpty(source.apply().take(new Range(Index.fromStart(3), Index.fromStart(2)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(6), Index.fromEnd(5)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(3), Index.fromEnd(8)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(6), Index.fromEnd(7)))); + } + + @Test + void NonEmptySource_DoNotThrowException_NotList() { + IEnumerable source = Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(3), Index.fromStart(2)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(6), Index.fromEnd(5)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(3), Index.fromEnd(8)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(6), Index.fromEnd(7)))); + } + + @Test + void NonEmptySource_DoNotThrowException_ListPartition() { + IEnumerable source = Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromStart(2)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(5)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromEnd(8)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(7)))); + } + + @Test + void NonEmptySource_DoNotThrowException_EnumerablePartition() { + IEnumerable source = Linq.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromStart(2)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(5)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromEnd(8)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(7)))); + } + + @Test + void EmptySource_DoNotThrowException() { + Func0> source = () -> Linq.of(Collections.emptyList()); + + // Multiple elements in the middle. + assertEmpty(source.apply().take(new Range(Index.fromEnd(9), Index.fromStart(5)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(2), Index.fromStart(7)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(2), Index.fromEnd(4)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(7), Index.fromEnd(4)))); + + // Range with default index. + assertEmpty(source.apply().take(Range.startAt(Index.fromEnd(9)))); + assertEmpty(source.apply().take(Range.startAt(Index.fromStart(2)))); + assertEmpty(source.apply().take(Range.endAt(Index.fromEnd(4)))); + assertEmpty(source.apply().take(Range.endAt(Index.fromStart(6)))); + + // All. + assertEquals(Linq.empty(), source.apply().take(Range.All)); + + // Single element in the middle. + assertEmpty(source.apply().take(new Range(Index.fromEnd(9), Index.fromStart(2)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(2), Index.fromStart(3)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(2), Index.fromEnd(7)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + + // Single element at start. + assertEmpty(source.apply().take(new Range(Index.fromEnd(10), Index.fromStart(1)))); + assertEmpty(source.apply().take(new Range(Index.Start, Index.fromStart(1)))); + assertEmpty(source.apply().take(new Range(Index.Start, Index.fromEnd(9)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(10), Index.fromEnd(9)))); + + // Single element at end. + assertEmpty(source.apply().take(new Range(Index.fromEnd(1), Index.fromEnd(10)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(9), Index.fromStart(10)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(9), Index.fromEnd(9)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(1), Index.fromEnd(9)))); + + // No element. + assertEmpty(source.apply().take(new Range(Index.fromStart(3), Index.fromStart(3)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(6), Index.fromEnd(4)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(3), Index.fromEnd(7)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(3), Index.fromStart(7)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + + // Invalid range. + assertEmpty(source.apply().take(new Range(Index.fromStart(3), Index.fromStart(2)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(6), Index.fromEnd(5)))); + assertEmpty(source.apply().take(new Range(Index.fromStart(3), Index.fromEnd(8)))); + assertEmpty(source.apply().take(new Range(Index.fromEnd(6), Index.fromEnd(7)))); + } + + @Test + void EmptySource_DoNotThrowException_NotList() { + IEnumerable source = Linq.of(Collections.emptyList()); + + // Multiple elements in the middle. + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(9), Index.fromStart(5)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(2), Index.fromStart(7)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(2), Index.fromEnd(4)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(7), Index.fromEnd(4)))); + + // Range with default index. + assertEmpty(ForceNotCollection(source).take(Range.startAt(Index.fromEnd(9)))); + assertEmpty(ForceNotCollection(source).take(Range.startAt(Index.fromStart(2)))); + assertEmpty(ForceNotCollection(source).take(Range.endAt(Index.fromEnd(4)))); + assertEmpty(ForceNotCollection(source).take(Range.endAt(Index.fromStart(6)))); + + // All. + assertEquals(Linq.empty(), ForceNotCollection(source).take(Range.All)); + + // Single element in the middle. + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(9), Index.fromStart(2)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(2), Index.fromStart(3)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(2), Index.fromEnd(7)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + + // Single element at start. + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromStart(1)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.Start, Index.fromStart(1)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.Start, Index.fromEnd(9)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(10), Index.fromEnd(9)))); + + // Single element at end. + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(1), Index.fromEnd(10)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(9), Index.fromStart(10)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(9), Index.fromEnd(9)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(1), Index.fromEnd(9)))); + + // No element. + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(3), Index.fromStart(3)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(6), Index.fromEnd(4)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(3), Index.fromEnd(7)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(3), Index.fromStart(7)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + + // Invalid range. + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(3), Index.fromStart(2)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(6), Index.fromEnd(5)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromStart(3), Index.fromEnd(8)))); + assertEmpty(ForceNotCollection(source).take(new Range(Index.fromEnd(6), Index.fromEnd(7)))); + } + + @Test + void EmptySource_DoNotThrowException_ListPartition() { + IEnumerable source = Linq.of(Collections.emptyList()); + + // Multiple elements in the middle. + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(9), Index.fromStart(5)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromStart(7)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromEnd(4)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(7), Index.fromEnd(4)))); + + // Range with default index. + assertEmpty(ListPartitionOrEmpty(source).take(Range.startAt(Index.fromEnd(9)))); + assertEmpty(ListPartitionOrEmpty(source).take(Range.startAt(Index.fromStart(2)))); + assertEmpty(ListPartitionOrEmpty(source).take(Range.endAt(Index.fromEnd(4)))); + assertEmpty(ListPartitionOrEmpty(source).take(Range.endAt(Index.fromStart(6)))); + + // All. + assertEquals(Linq.empty(), ListPartitionOrEmpty(source).take(Range.All)); + + // Single element in the middle. + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(9), Index.fromStart(2)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromStart(3)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromEnd(7)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + + // Single element at start. + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(1)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.Start, Index.fromStart(1)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.Start, Index.fromEnd(9)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromEnd(9)))); + + // Single element at end. + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromEnd(10)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(9), Index.fromStart(10)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(9), Index.fromEnd(9)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromEnd(9)))); + + // No element. + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromStart(3)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(4)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromEnd(7)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(3), Index.fromStart(7)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + + // Invalid range. + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromStart(2)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(5)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromEnd(8)))); + assertEmpty(ListPartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(7)))); + } + + @Test + void EmptySource_DoNotThrowException_EnumerablePartition() { + IEnumerable source = Linq.of(Collections.emptyList()); + + // Multiple elements in the middle. + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(9), Index.fromStart(5)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromStart(7)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromEnd(4)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(7), Index.fromEnd(4)))); + + // Range with default index. + assertEmpty(EnumerablePartitionOrEmpty(source).take(Range.startAt(Index.fromEnd(9)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(Range.startAt(Index.fromStart(2)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(Range.endAt(Index.fromEnd(4)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(Range.endAt(Index.fromStart(6)))); + + // All. + assertEquals(Linq.empty(), EnumerablePartitionOrEmpty(source).take(Range.All)); + + // Single element in the middle. + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(9), Index.fromStart(2)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromStart(3)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(2), Index.fromEnd(7)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(5), Index.fromEnd(4)))); + + // Single element at start. + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromStart(1)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.Start, Index.fromStart(1)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.Start, Index.fromEnd(9)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(10), Index.fromEnd(9)))); + + // Single element at end. + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromEnd(10)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(9), Index.fromStart(10)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(9), Index.fromEnd(9)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(1), Index.fromEnd(9)))); + + // No element. + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromStart(3)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(4)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromEnd(7)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(3), Index.fromStart(7)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(6)))); + + // Invalid range. + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromStart(2)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(6), Index.fromEnd(5)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromStart(3), Index.fromEnd(8)))); + assertEmpty(EnumerablePartitionOrEmpty(source).take(new Range(Index.fromEnd(6), Index.fromEnd(7)))); } @Test @@ -626,7 +2073,7 @@ void testIList_TakeLast() { assertEquals(0, emptySource2.count()); assertThrows(InvalidOperationException.class, () -> emptySource2.first()); assertThrows(InvalidOperationException.class, () -> emptySource2.last()); - assertIsType(Linq.empty().getClass(), emptySource2.skip(2)); + assertIsType(EnumerablePartition.class, emptySource2.skip(2)); try (IEnumerator e = emptySource2.enumerator()) { assertFalse(e.moveNext()); assertFalse(e.moveNext()); diff --git a/src/test/java/com/bestvike/linq/enumerable/ToLinkedMapTest.java b/src/test/java/com/bestvike/linq/enumerable/ToLinkedMapTest.java index 263a3730..7309527a 100644 --- a/src/test/java/com/bestvike/linq/enumerable/ToLinkedMapTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/ToLinkedMapTest.java @@ -315,6 +315,19 @@ void testToMapWithSelector() { assertEquals(2, map2.size()); } + @Test + void testToMapWithResultElementSelector() { + Map map = Linq.of(emps).toLinkedMap(emp -> emp.empno, emp -> emp.name,(x,y)->x); + assertTrue(map.get(110).equals("Bill")); + assertEquals(4, map.size()); + + //key 重复,保留第一个 + Map map2 = Linq.of(emps).toLinkedMap(emp -> emp.deptno, emp -> emp.name,(x,y)->y); + assertEquals(emps[3].name, map2.get(10)); + assertEquals(emps[1].name, map2.get(30)); + assertEquals(2, map2.size()); + } + private static class NameScore extends ValueType { private final String Name; diff --git a/src/test/java/com/bestvike/linq/enumerable/ToMapTest.java b/src/test/java/com/bestvike/linq/enumerable/ToMapTest.java index 126932e4..f6d62efe 100644 --- a/src/test/java/com/bestvike/linq/enumerable/ToMapTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/ToMapTest.java @@ -315,6 +315,19 @@ void testToMapWithSelector() { assertEquals(2, map2.size()); } + @Test + void testToMapWithResultElementSelector() { + Map map = Linq.of(emps).toMap(emp -> emp.empno, emp -> emp.name, (x, y) -> x); + assertTrue(map.get(110).equals("Bill")); + assertEquals(4, map.size()); + + //key 重复,保留第一个 + Map map2 = Linq.of(emps).toMap(emp -> emp.deptno, emp -> emp.name, (x, y) -> y); + assertEquals(emps[3].name, map2.get(10)); + assertEquals(emps[1].name, map2.get(30)); + assertEquals(2, map2.size()); + } + private static class NameScore extends ValueType { private final String Name; diff --git a/src/test/java/com/bestvike/linq/enumerable/UnionByTest.java b/src/test/java/com/bestvike/linq/enumerable/UnionByTest.java index 3fa10cc1..46395f9b 100644 --- a/src/test/java/com/bestvike/linq/enumerable/UnionByTest.java +++ b/src/test/java/com/bestvike/linq/enumerable/UnionByTest.java @@ -11,8 +11,14 @@ import com.bestvike.linq.Linq; import com.bestvike.linq.entity.Employee; import com.bestvike.linq.exception.ArgumentNullException; +import com.bestvike.linq.util.ArgsList; import com.bestvike.linq.util.HashSet; +import com.bestvike.out; +import com.bestvike.tuple.Tuple; +import com.bestvike.tuple.Tuple2; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /** * Created by 许崇雷 on 2018-05-10. @@ -21,6 +27,125 @@ class UnionByTest extends TestCase { private static final Func1 IntKeySelector = x -> x; private static final Func1 StringKeySelector = x -> x; + private static Object[] WrapArgs(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + return new Object[]{first, second, keySelector, comparer, expected}; + } + + private static IEnumerable TestData() { + ArgsList argsList = new ArgsList(); + argsList.add(WrapArgs( + Linq.range(0, 7), + Linq.range(3, 7), + x -> x, + null, + Linq.range(0, 10))); + + argsList.add(WrapArgs( + Linq.range(0, 10), + Linq.range(10, 10), + x -> x, + null, + Linq.range(0, 20))); + + argsList.add(WrapArgs( + Linq.empty(), + Linq.range(0, 5), + x -> x, + null, + Linq.range(0, 5))); + + argsList.add(WrapArgs( + Linq.repeat(5, 20), + Linq.empty(), + x -> x, + null, + Linq.repeat(5, 1))); + + argsList.add(WrapArgs( + Linq.repeat(5, 20), + Linq.repeat(5, 3), + x -> x, + null, + Linq.repeat(5, 1))); + + argsList.add(WrapArgs( + Linq.of("Bob", "Tim", "Robert", "Chris"), + Linq.of("bBo", "shriC"), + x -> x, + null, + Linq.of("Bob", "Tim", "Robert", "Chris", "bBo", "shriC"))); + + argsList.add(WrapArgs( + Linq.of("Bob", "Tim", "Robert", "Chris"), + Linq.of("bBo", "shriC"), + x -> x, + new AnagramEqualityComparer(), + Linq.of("Bob", "Tim", "Robert", "Chris"))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40), Tuple.create("Martin", 20)), + Linq.of(Tuple.create("Peter", 21), Tuple.create("John", 30), Tuple.create("Toby", 33)), + Tuple2::getItem2, + null, + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40), Tuple.create("Peter", 21), Tuple.create("Toby", 33)))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40), Tuple.create("Martin", 20)), + Linq.of(Tuple.create("Toby", 33), Tuple.create("Harry", 35), Tuple.create("tom", 67)), + Tuple2::getItem1, + null, + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40), Tuple.create("Martin", 20), Tuple.create("Toby", 33), Tuple.create("tom", 67)))); + + argsList.add(WrapArgs( + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40), Tuple.create("Martin", 20)), + Linq.of(Tuple.create("Toby", 33), Tuple.create("Harry", 35), Tuple.create("tom", 67)), + Tuple2::getItem1, + StringComparer.OrdinalIgnoreCase, + Linq.of(Tuple.create("Tom", 20), Tuple.create("Dick", 30), Tuple.create("Harry", 40), Tuple.create("Martin", 20), Tuple.create("Toby", 33)))); + + return argsList; + } + + @Test + void FirstNull_ThrowsArgumentNullException() { + IEnumerable first = null; + IEnumerable second = Linq.of("bBo", "shriC"); + + assertThrows(NullPointerException.class, () -> first.unionBy(second, x -> x)); + assertThrows(NullPointerException.class, () -> first.unionBy(second, x -> x, new AnagramEqualityComparer())); + } + + @Test + void SecondNull_ThrowsArgumentNullException() { + IEnumerable first = Linq.of("Bob", "Tim", "Robert", "Chris"); + IEnumerable second = null; + + assertThrows(ArgumentNullException.class, () -> first.unionBy(second, x -> x)); + assertThrows(ArgumentNullException.class, () -> first.unionBy(second, x -> x, new AnagramEqualityComparer())); + } + + @Test + void KeySelectorNull_ThrowsArgumentNullException() { + IEnumerable first = Linq.of("Bob", "Tim", "Robert", "Chris"); + IEnumerable second = Linq.of("bBo", "shriC"); + Func1 keySelector = null; + + assertThrows(ArgumentNullException.class, () -> first.unionBy(second, keySelector)); + assertThrows(ArgumentNullException.class, () -> first.unionBy(second, keySelector, new AnagramEqualityComparer())); + } + + @ParameterizedTest + @MethodSource("TestData") + void HasExpectedOutput(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + assertEquals(expected, first.unionBy(second, keySelector, comparer)); + } + + @ParameterizedTest + @MethodSource("TestData") + void RunOnce_HasExpectedOutput(IEnumerable first, IEnumerable second, Func1 keySelector, IEqualityComparer comparer, IEnumerable expected) { + assertEquals(expected, first.runOnce().unionBy(second.runOnce(), keySelector, comparer)); + } + @Test void SameResultsRepeatCallsIntQuery() { IEnumerable q1 = Linq.of(2, 3, null, 2, null, 4, 5); @@ -82,38 +207,6 @@ void RunOnce() { assertEquals(Linq.of(expected), Linq.of(first).runOnce().unionBy(Linq.of(second).runOnce(), StringKeySelector, comparer), comparer); } - @Test - void FirstNullCustomComparer() { - IEnumerable first = null; - String[] second = {"ttaM", "Charlie", "Bbo"}; - - assertThrows(NullPointerException.class, () -> first.unionBy(Linq.of(second), StringKeySelector, new AnagramEqualityComparer())); - } - - @Test - void SecondNullCustomComparer() { - String[] first = {"Bob", "Robert", "Tim", "Matt", "miT"}; - IEnumerable second = null; - - assertThrows(ArgumentNullException.class, () -> Linq.of(first).unionBy(second, StringKeySelector, new AnagramEqualityComparer())); - } - - @Test - void FirstNullNoComparer() { - IEnumerable first = null; - String[] second = {"ttaM", "Charlie", "Bbo"}; - - assertThrows(NullPointerException.class, () -> first.unionBy(Linq.of(second), StringKeySelector)); - } - - @Test - void SecondNullNoComparer() { - String[] first = {"Bob", "Robert", "Tim", "Matt", "miT"}; - IEnumerable second = null; - - assertThrows(ArgumentNullException.class, () -> Linq.of(first).unionBy(second, StringKeySelector)); - } - @Test void SingleNullWithEmpty() { String[] first = {null}; @@ -389,12 +482,12 @@ void testMultiUnionBySameSelector() { }; IEnumerable peoples = Linq.of(source1).unionBy(Linq.of(source2), x -> x.groupId).unionBy(Linq.of(source3), x -> x.groupId); - assertSame(UnionByIterator2.class, peoples.getClass()); + assertSame(UnionByIterator.class, peoples.getClass()); assertEquals(Linq.of(expect), peoples); Func1 groupSelector = x -> x.groupId; IEnumerable peoples2 = Linq.of(source1).unionBy(Linq.of(source2), groupSelector).unionBy(Linq.of(source3), groupSelector); - assertSame(UnionByIteratorN.class, peoples2.getClass()); + assertSame(UnionByIterator.class, peoples2.getClass()); assertEquals(Linq.of(expect), peoples2); } @@ -426,7 +519,7 @@ void testMultiUnionByDiffSelector() { }; IEnumerable peoples = Linq.of(source1).unionBy(Linq.of(source2), x -> x.groupId).unionBy(Linq.of(source3), x -> x.id); - assertSame(UnionByIterator2.class, peoples.getClass()); + assertSame(UnionByIterator.class, peoples.getClass()); assertEquals(Linq.of(expect), peoples); } @@ -436,24 +529,24 @@ void testUnionBy() { .unionBy(Linq.of(badEmps), emp -> emp.deptno) .unionBy(Linq.of(emps), emp -> emp.deptno); assertEquals(4, enumerable.count()); - UnionByIterator2 unionByIterator2 = (UnionByIterator2) enumerable; - assertEquals(4, unionByIterator2._toArray(Employee.class).length); - assertEquals(4, unionByIterator2._toArray().length); - assertEquals(4, unionByIterator2._toList().size()); - assertEquals(-1, unionByIterator2._getCount(true)); - assertEquals(4, unionByIterator2._getCount(false)); + UnionByIterator unionByIterator2 = (UnionByIterator) enumerable; + assertEquals(4, unionByIterator2.toArray(Employee.class).length); + assertEquals(4, unionByIterator2.toArray().size()); + assertEquals(4, unionByIterator2.toList().size()); + assertFalse(Count.tryGetNonEnumeratedCount(unionByIterator2, out.init())); + assertEquals(4, unionByIterator2.count()); Func1 selector = emp -> emp.deptno; IEnumerable enumerable2 = Linq.of(emps) .unionBy(Linq.of(badEmps), selector) .unionBy(Linq.of(emps), selector); assertEquals(4, enumerable2.count()); - UnionByIteratorN unionByIterator22 = (UnionByIteratorN) enumerable2; - assertEquals(4, unionByIterator22._toArray(Employee.class).length); - assertEquals(4, unionByIterator22._toArray().length); - assertEquals(4, unionByIterator22._toList().size()); - assertEquals(-1, unionByIterator22._getCount(true)); - assertEquals(4, unionByIterator22._getCount(false)); + UnionByIterator unionByIterator22 = (UnionByIterator) enumerable2; + assertEquals(4, unionByIterator22.toArray(Employee.class).length); + assertEquals(4, unionByIterator22.toArray().size()); + assertEquals(4, unionByIterator22.toList().size()); + assertFalse(Count.tryGetNonEnumeratedCount(unionByIterator22, out.init())); + assertEquals(4, unionByIterator22.count()); IEnumerable source = Linq.of(emps) .unionBy(Linq.of(badEmps), selector) @@ -484,24 +577,24 @@ public int hashCode(Integer obj) { .unionBy(Linq.of(badEmps), emp -> emp.deptno, comparer) .unionBy(Linq.of(emps), emp -> emp.deptno, comparer); assertEquals(1, enumerable.count()); - UnionByIterator2 unionByIterator2 = (UnionByIterator2) enumerable; - assertEquals(1, unionByIterator2._toArray(Employee.class).length); - assertEquals(1, unionByIterator2._toArray().length); - assertEquals(1, unionByIterator2._toList().size()); - assertEquals(-1, unionByIterator2._getCount(true)); - assertEquals(1, unionByIterator2._getCount(false)); + UnionByIterator unionByIterator2 = (UnionByIterator) enumerable; + assertEquals(1, unionByIterator2.toArray(Employee.class).length); + assertEquals(1, unionByIterator2.toArray().size()); + assertEquals(1, unionByIterator2.toList().size()); + assertFalse(Count.tryGetNonEnumeratedCount(unionByIterator2, out.init())); + assertEquals(1, unionByIterator2.count()); Func1 selector = emp -> emp.deptno; IEnumerable enumerable2 = Linq.of(emps) .unionBy(Linq.of(badEmps), selector, comparer) .unionBy(Linq.of(emps), selector, comparer); assertEquals(1, enumerable2.count()); - UnionByIteratorN unionByIterator22 = (UnionByIteratorN) enumerable2; - assertEquals(1, unionByIterator22._toArray(Employee.class).length); - assertEquals(1, unionByIterator22._toArray().length); - assertEquals(1, unionByIterator22._toList().size()); - assertEquals(-1, unionByIterator22._getCount(true)); - assertEquals(1, unionByIterator22._getCount(false)); + UnionByIterator unionByIterator22 = (UnionByIterator) enumerable2; + assertEquals(1, unionByIterator22.toArray(Employee.class).length); + assertEquals(1, unionByIterator22.toArray().size()); + assertEquals(1, unionByIterator22.toList().size()); + assertFalse(Count.tryGetNonEnumeratedCount(unionByIterator22, out.init())); + assertEquals(1, unionByIterator22.count()); } diff --git a/src/test/java/com/bestvike/linq/util/AssertEqualityComparer.java b/src/test/java/com/bestvike/linq/util/AssertEqualityComparer.java index 016e9a62..932b9706 100644 --- a/src/test/java/com/bestvike/linq/util/AssertEqualityComparer.java +++ b/src/test/java/com/bestvike/linq/util/AssertEqualityComparer.java @@ -2,6 +2,7 @@ import com.bestvike.collections.generic.IEqualityComparer; import com.bestvike.linq.IEnumerable; +import com.bestvike.linq.Linq; import java.util.Objects; @@ -13,8 +14,21 @@ public final class AssertEqualityComparer implements IEqualityComparer { @Override public boolean equals(T x, T y) { - if (x instanceof IEnumerable && y instanceof IEnumerable) - return ((IEnumerable) x).sequenceEqual((IEnumerable) y, innerComparer); + IEnumerable left = null; + IEnumerable right = null; + + if (x instanceof Object[]) + left = Linq.of((Object[]) x); + else if (x instanceof IEnumerable) + left = ((IEnumerable) x); + + if (y instanceof Object[]) + right = Linq.of((Object[]) y); + else if (y instanceof IEnumerable) + right = ((IEnumerable) y); + + if (left != null && right != null) + return left.sequenceEqual(right, innerComparer); return Objects.equals(x, y); }