summaryrefslogtreecommitdiff
path: root/graphs/java/seq
diff options
context:
space:
mode:
Diffstat (limited to 'graphs/java/seq')
-rw-r--r--graphs/java/seq/.gitignore38
-rw-r--r--graphs/java/seq/pom.xml133
-rw-r--r--graphs/java/seq/src/main/java/fr/epita/assistants/seq/ExtendedStream.java204
-rw-r--r--graphs/java/seq/src/main/java/fr/epita/assistants/seq/Seq.java413
-rw-r--r--graphs/java/seq/src/test/java/fr/epita/assistants/seq/ExtendedStreamTest.java244
5 files changed, 1032 insertions, 0 deletions
diff --git a/graphs/java/seq/.gitignore b/graphs/java/seq/.gitignore
new file mode 100644
index 0000000..5ff6309
--- /dev/null
+++ b/graphs/java/seq/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store \ No newline at end of file
diff --git a/graphs/java/seq/pom.xml b/graphs/java/seq/pom.xml
new file mode 100644
index 0000000..46ba851
--- /dev/null
+++ b/graphs/java/seq/pom.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>fr.epita.assistants</groupId>
+ <artifactId>seq</artifactId>
+ <version>1.0</version>
+
+ <properties>
+ <versions.java>21</versions.java>
+ <versions.junit>5.9.1</versions.junit>
+ <versions.maven-compiler-plugin>3.13.0</versions.maven-compiler-plugin>
+ <versions.maven-surefire-plugin>3.5.0</versions.maven-surefire-plugin>
+ <versions.maven-jar-plugin>3.1.1</versions.maven-jar-plugin>
+ <versions.maven-install-plugin>3.1.0</versions.maven-install-plugin>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+ <surefire.reportsDirectory>${project.build.directory}/surefire-reports</surefire.reportsDirectory>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <version>${versions.junit}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.surefire</groupId>
+ <artifactId>surefire-junit-platform</artifactId>
+ <version>${versions.maven-surefire-plugin}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-compat</artifactId>
+ <version>3.9.8</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>3.9.8</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-project</artifactId>
+ <version>2.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-core</artifactId>
+ <version>3.8.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-monitor</artifactId>
+ <version>2.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-utils</artifactId>
+ <version>3.0.24</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.shared</groupId>
+ <artifactId>maven-filtering</artifactId>
+ <version>3.3.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-interpolation</artifactId>
+ <version>1.13</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-profile</artifactId>
+ <version>2.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-artifact-manager</artifactId>
+ <version>2.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-registry</artifactId>
+ <version>2.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-repository-metadata</artifactId>
+ <version>2.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>classworlds</groupId>
+ <artifactId>classworlds</artifactId>
+ <version>1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-commons</artifactId>
+ <version>1.9.3</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${versions.maven-compiler-plugin}</version>
+ <configuration>
+ <source>${versions.java}</source>
+ <target>${versions.java}</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <version>${versions.maven-install-plugin}</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${versions.maven-surefire-plugin}</version>
+ <configuration>
+ <reportsDirectory>${surefire.reportsDirectory}</reportsDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/graphs/java/seq/src/main/java/fr/epita/assistants/seq/ExtendedStream.java b/graphs/java/seq/src/main/java/fr/epita/assistants/seq/ExtendedStream.java
new file mode 100644
index 0000000..0ba5f6a
--- /dev/null
+++ b/graphs/java/seq/src/main/java/fr/epita/assistants/seq/ExtendedStream.java
@@ -0,0 +1,204 @@
+package fr.epita.assistants.seq;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+/**
+ * Extends the {@link Stream} interface, adding some very strangely lacking useful methods.
+ * You should use the Forwarding-Interface pattern.
+ * <p>
+ * The use of infinite, unordered or parallel stream in this implementation will
+ * not be tested.
+ *
+ * @param <ELEMENT_TYPE> the type of stream.
+ */
+@SuppressWarnings("unused")
+public interface ExtendedStream<ELEMENT_TYPE> extends Stream<ELEMENT_TYPE> {
+
+
+ /**
+ * Creates a map out of the stream.
+ * In case of duplicate keys, the latest element in the original stream will overwrite the one(s) in place.
+ *
+ * @param keyMapper mapping function to extract map keys.
+ * @param <KEY_TYPE> the expected type of key.
+ * @return the created map.
+ */
+ <KEY_TYPE> Map<KEY_TYPE, ELEMENT_TYPE>
+ toMap(final Function<ELEMENT_TYPE, KEY_TYPE> keyMapper);
+
+ /**
+ * Creates a map out of the stream.
+ * In case of key duplicates, the latest element in the original stream will overwrite the one(s) in place.
+ *
+ * @param map the map to fill/update.
+ * @param keyMapper mapping function to extract map keys.
+ * @param valueMapper mapping function to extract map values.
+ * @param <KEY_TYPE> the expected type of key.
+ * @param <VALUE_TYPE> the expected type of value.
+ * @param <MAP_TYPE> the complete return type.
+ * @return the created map.
+ */
+ <KEY_TYPE, VALUE_TYPE, MAP_TYPE extends Map<KEY_TYPE, VALUE_TYPE>>
+ MAP_TYPE toMap(final MAP_TYPE map,
+ final Function<ELEMENT_TYPE, KEY_TYPE> keyMapper,
+ final Function<ELEMENT_TYPE, VALUE_TYPE> valueMapper);
+
+ /**
+ * Creates a map out of the stream.
+ * In case of duplicate keys, the latest element in the original stream will overwrite the one(s) in place.
+ *
+ * @param keyMapper mapping function to extract map keys.
+ * @param valueMapper mapping function to extract map values.
+ * @param <KEY_TYPE> the expected type of key.
+ * @param <VALUE_TYPE> the expected type of value.
+ * @return the created map.
+ */
+ <KEY_TYPE, VALUE_TYPE>
+ Map<KEY_TYPE, VALUE_TYPE> toMap(final Function<ELEMENT_TYPE, KEY_TYPE> keyMapper,
+ final Function<ELEMENT_TYPE, VALUE_TYPE> valueMapper);
+
+ /**
+ * Converts the stream to a list.
+ *
+ * @return the created list.
+ */
+ List<ELEMENT_TYPE> toList();
+
+ /**
+ * Dumps the content of the stream to the given list.
+ *
+ * @param list the list to dump values to.
+ * @param <LIST> the exact type of list.
+ * @return the updated list.
+ */
+ <LIST extends List<ELEMENT_TYPE>> LIST toList(final LIST list);
+
+ /**
+ * Converts the stream to a set.
+ *
+ * @return the built set.
+ */
+ Set<ELEMENT_TYPE> toSet();
+
+ /**
+ * Dumps the content of the stream to the given set.
+ *
+ * @param set the set to update
+ * @param <SET> the set type.
+ * @return the updated set.
+ */
+ <SET extends Set<ELEMENT_TYPE>> SET toSet(final SET set);
+
+ /**
+ * Creates a stream of pairs of the content of the stream and values produced by a supplier.
+ *
+ * @param supplier the value supplier.
+ * @param <ASSOCIATED_TYPE> the type of associated values.
+ * @return the built stream.
+ */
+ <ASSOCIATED_TYPE>
+ ExtendedStream<Pair<ELEMENT_TYPE, ASSOCIATED_TYPE>> associate(final Supplier<ASSOCIATED_TYPE> supplier);
+
+ /**
+ * Creates a stream of pairs of the content of the stream and values produces by another stream.
+ * Once any of the two streams is closed, the produced stream is complete, regardless of potential values remaining
+ * in the other stream.
+ *
+ * @param supplier the value supplier.
+ * @param <ASSOCIATED_TYPE> the type of associated values.
+ * @return the built stream.
+ */
+ <ASSOCIATED_TYPE>
+ ExtendedStream<Pair<ELEMENT_TYPE, ASSOCIATED_TYPE>> associate(final Stream<ASSOCIATED_TYPE> supplier);
+
+ /**
+ * Prints the element of the stream on the standard output.
+ *
+ * @return this.
+ */
+ ExtendedStream<ELEMENT_TYPE> print();
+
+ /**
+ * Adds the content of the given stream to the current stream and returns it as a new one.
+ *
+ * @param stream the stream to add.
+ * @return a new stream containing the current one then the given one.
+ */
+ ExtendedStream<ELEMENT_TYPE> plus(final Stream<ELEMENT_TYPE> stream);
+
+ /**
+ * Builds a string by joining the string representation of all contained values, interspersed with the given string
+ * delimiter.
+ *
+ * @param delimiter the delimiter string.
+ * @return the built {@link String}.
+ */
+ String join(final String delimiter);
+
+ /**
+ * Builds a string by joining the string representation of all contained values.
+ *
+ * @return the built {@link String}.
+ */
+ String join();
+
+ /**
+ * Builds a pair of streams by partitioning the current one using the given pivot function.
+ *
+ * @param pivot the function to segregate the values of the given stream.
+ * @param <KEY_TYPE> type of partition key.
+ * @return the pair of created streams.
+ */
+ <KEY_TYPE>
+ ExtendedStream<Pair<KEY_TYPE, ExtendedStream<ELEMENT_TYPE>>>
+ partition(final Function<ELEMENT_TYPE, KEY_TYPE> pivot);
+
+ /**
+ * A utility class representing a pair.
+ *
+ * @param <FIRST_TYPE> the first value type.
+ * @param <SECOND_TYPE> the second value type.
+ */
+ @SuppressWarnings("WeakerAccess")
+ class Pair<FIRST_TYPE, SECOND_TYPE> {
+ /**
+ * The first value.
+ */
+ public final FIRST_TYPE first;
+
+ /**
+ * The second value.
+ */
+ public final SECOND_TYPE second;
+
+ /**
+ * Default CTor.
+ *
+ * @param first value of the same name.
+ * @param second value of the same name.
+ */
+ public Pair(final FIRST_TYPE first, final SECOND_TYPE second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) return false;
+ if (!obj.getClass().equals(Pair.class)) return false;
+ final Pair pair = (Pair) obj;
+ return Objects.equals(first, pair.first) && Objects.equals(second, pair.second);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(first, second);
+ }
+ }
+}
diff --git a/graphs/java/seq/src/main/java/fr/epita/assistants/seq/Seq.java b/graphs/java/seq/src/main/java/fr/epita/assistants/seq/Seq.java
new file mode 100644
index 0000000..31e5cab
--- /dev/null
+++ b/graphs/java/seq/src/main/java/fr/epita/assistants/seq/Seq.java
@@ -0,0 +1,413 @@
+package fr.epita.assistants.seq;
+
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
+
+public interface Seq<ELEMENT_TYPE> extends ExtendedStream<ELEMENT_TYPE> {
+ Stream<ELEMENT_TYPE> giveStream();
+
+ static <ELEMENT_TYPE> Seq<ELEMENT_TYPE> of(Stream<ELEMENT_TYPE> stream) {
+ return () -> stream;
+ }
+
+ static <ELEMENT_TYPE> Seq<ELEMENT_TYPE> of(List<ELEMENT_TYPE> list) {
+ return list::stream;
+ }
+
+ static <ELEMENT_TYPE> Seq<ELEMENT_TYPE> of(ELEMENT_TYPE... values) {
+ List<ELEMENT_TYPE> vals = new ArrayList<>(Arrays.asList(values));
+ return vals::stream;
+ }
+
+ /**
+ * Creates a map out of the stream.
+ * In case of duplicate keys, the latest element in the original stream will overwrite the one(s) in place.
+ *
+ * @param keyMapper mapping function to extract map keys.
+ * @param <KEY_TYPE> the expected type of key.
+ * @return the created map.
+ */
+ default <KEY_TYPE> Map<KEY_TYPE, ELEMENT_TYPE>
+ toMap(final Function<ELEMENT_TYPE, KEY_TYPE> keyMapper) {
+ Map<KEY_TYPE, ELEMENT_TYPE> map = new HashMap<>();
+ giveStream().forEach(e -> map.put(keyMapper.apply(e), e));
+ return map;
+ }
+
+ /**
+ * Creates a map out of the stream.
+ * In case of key duplicates, the latest element in the original stream will overwrite the one(s) in place.
+ *
+ * @param map the map to fill/update.
+ * @param keyMapper mapping function to extract map keys.
+ * @param valueMapper mapping function to extract map values.
+ * @param <KEY_TYPE> the expected type of key.
+ * @param <VALUE_TYPE> the expected type of value.
+ * @param <MAP_TYPE> the complete return type.
+ * @return the created map.
+ */
+ default <KEY_TYPE, VALUE_TYPE, MAP_TYPE extends Map<KEY_TYPE, VALUE_TYPE>>
+ MAP_TYPE toMap(final MAP_TYPE map,
+ final Function<ELEMENT_TYPE, KEY_TYPE> keyMapper,
+ final Function<ELEMENT_TYPE, VALUE_TYPE> valueMapper) {
+ giveStream().forEach(e -> map.put(keyMapper.apply(e), valueMapper.apply(e)));
+ return map;
+ }
+
+ /**
+ * Creates a map out of the stream.
+ * In case of duplicate keys, the latest element in the original stream will overwrite the one(s) in place.
+ *
+ * @param keyMapper mapping function to extract map keys.
+ * @param valueMapper mapping function to extract map values.
+ * @param <KEY_TYPE> the expected type of key.
+ * @param <VALUE_TYPE> the expected type of value.
+ * @return the created map.
+ */
+ default <KEY_TYPE, VALUE_TYPE>
+ Map<KEY_TYPE, VALUE_TYPE> toMap(final Function<ELEMENT_TYPE, KEY_TYPE> keyMapper,
+ final Function<ELEMENT_TYPE, VALUE_TYPE> valueMapper) {
+ Map<KEY_TYPE, VALUE_TYPE> map = new HashMap<>();
+ giveStream().forEach(e -> map.put(keyMapper.apply(e), valueMapper.apply(e)));
+ return map;
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> filter(Predicate<? super ELEMENT_TYPE> predicate) {
+ return giveStream().filter(predicate);
+ }
+
+ @Override
+ public default <R> Stream<R> map(Function<? super ELEMENT_TYPE, ? extends R> mapper) {
+ return giveStream().map(mapper);
+ }
+
+ @Override
+ public default IntStream mapToInt(ToIntFunction<? super ELEMENT_TYPE> mapper) {
+ return giveStream().mapToInt(mapper);
+ }
+
+ @Override
+ public default LongStream mapToLong(ToLongFunction<? super ELEMENT_TYPE> mapper) {
+ return giveStream().mapToLong(mapper);
+ }
+
+ @Override
+ public default DoubleStream mapToDouble(ToDoubleFunction<? super ELEMENT_TYPE> mapper) {
+ return giveStream().mapToDouble(mapper);
+ }
+
+ @Override
+ public default <R> Stream<R> flatMap(Function<? super ELEMENT_TYPE, ? extends Stream<? extends R>> mapper) {
+ return giveStream().flatMap(mapper);
+ }
+
+ @Override
+ public default IntStream flatMapToInt(Function<? super ELEMENT_TYPE, ? extends IntStream> mapper) {
+ return giveStream().flatMapToInt(mapper);
+ }
+
+ @Override
+ public default LongStream flatMapToLong(Function<? super ELEMENT_TYPE, ? extends LongStream> mapper) {
+ return giveStream().flatMapToLong(mapper);
+ }
+
+ @Override
+ public default DoubleStream flatMapToDouble(Function<? super ELEMENT_TYPE, ? extends DoubleStream> mapper) {
+ return giveStream().flatMapToDouble(mapper);
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> distinct() {
+ return giveStream().distinct();
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> sorted() {
+ return giveStream().sorted();
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> sorted(Comparator<? super ELEMENT_TYPE> comparator) {
+ return giveStream().sorted(comparator);
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> peek(Consumer<? super ELEMENT_TYPE> action) {
+ return giveStream().peek(action);
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> limit(long maxSize) {
+ return giveStream().limit(maxSize);
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> skip(long n) {
+ return giveStream().skip(n);
+ }
+
+ @Override
+ public default void forEach(Consumer<? super ELEMENT_TYPE> action) {
+ giveStream().forEach(action);
+ }
+
+ @Override
+ public default void forEachOrdered(Consumer<? super ELEMENT_TYPE> action) {
+ giveStream().forEachOrdered(action);
+ }
+
+ @Override
+ public default Object[] toArray() {
+ return giveStream().toArray();
+ }
+
+ @Override
+ public default <A> A[] toArray(IntFunction<A[]> generator) {
+ return giveStream().toArray(generator);
+ }
+
+ @Override
+ public default ELEMENT_TYPE reduce(ELEMENT_TYPE identity, BinaryOperator<ELEMENT_TYPE> accumulator) {
+ return giveStream().reduce(identity, accumulator);
+ }
+
+ @Override
+ public default Optional<ELEMENT_TYPE> reduce(BinaryOperator<ELEMENT_TYPE> accumulator) {
+ return giveStream().reduce(accumulator);
+ }
+
+ @Override
+ public default <U> U reduce(U identity, BiFunction<U, ? super ELEMENT_TYPE, U> accumulator, BinaryOperator<U> combiner) {
+ return giveStream().reduce(identity, accumulator, combiner);
+ }
+
+ @Override
+ public default <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super ELEMENT_TYPE> accumulator, BiConsumer<R, R> combiner) {
+ return giveStream().collect(supplier, accumulator, combiner);
+ }
+
+ @Override
+ public default <R, A> R collect(Collector<? super ELEMENT_TYPE, A, R> collector) {
+ return giveStream().collect(collector);
+ }
+
+ /**
+ * Converts the stream to a list.
+ *
+ * @return the created list.
+ */
+ default List<ELEMENT_TYPE> toList() {
+ return giveStream().toList();
+ }
+
+ @Override
+ public default Optional<ELEMENT_TYPE> min(Comparator<? super ELEMENT_TYPE> comparator) {
+ return giveStream().min(comparator);
+ }
+
+ @Override
+ public default Optional<ELEMENT_TYPE> max(Comparator<? super ELEMENT_TYPE> comparator) {
+ return giveStream().max(comparator);
+ }
+
+ @Override
+ public default long count() {
+ return giveStream().count();
+ }
+
+ @Override
+ public default boolean anyMatch(Predicate<? super ELEMENT_TYPE> predicate) {
+ return giveStream().anyMatch(predicate);
+ }
+
+ @Override
+ public default boolean allMatch(Predicate<? super ELEMENT_TYPE> predicate) {
+ return giveStream().allMatch(predicate);
+ }
+
+ @Override
+ public default boolean noneMatch(Predicate<? super ELEMENT_TYPE> predicate) {
+ return giveStream().noneMatch(predicate);
+ }
+
+ @Override
+ public default Optional<ELEMENT_TYPE> findFirst() {
+ return giveStream().findFirst();
+ }
+
+ @Override
+ public default Optional<ELEMENT_TYPE> findAny() {
+ return giveStream().findAny();
+ }
+
+ /**
+ * Dumps the content of the stream to the given list.
+ *
+ * @param list the list to dump values to.
+ * @param <LIST> the exact type of list.
+ * @return the updated list.
+ */
+ default <LIST extends List<ELEMENT_TYPE>> LIST toList(final LIST list) {
+ giveStream().forEach(list::add);
+ return list;
+ }
+
+ /**
+ * Converts the stream to a set.
+ *
+ * @return the built set.
+ */
+ default Set<ELEMENT_TYPE> toSet() {
+ return giveStream().collect(Collectors.toSet());
+ }
+
+ /**
+ * Dumps the content of the stream to the given set.
+ *
+ * @param set the set to update
+ * @param <SET> the set type.
+ * @return the updated set.
+ */
+ default <SET extends Set<ELEMENT_TYPE>> SET toSet(final SET set) {
+ giveStream().forEach(set::add);
+ return set;
+ }
+
+ /**
+ * Creates a stream of pairs of the content of the stream and values produced by a supplier.
+ *
+ * @param supplier the value supplier.
+ * @param <ASSOCIATED_TYPE> the type of associated values.
+ * @return the built stream.
+ */
+ default <ASSOCIATED_TYPE>
+ ExtendedStream<Pair<ELEMENT_TYPE, ASSOCIATED_TYPE>> associate(final Supplier<ASSOCIATED_TYPE> supplier) {
+ return Seq.of(giveStream().map(e -> new Pair<ELEMENT_TYPE,
+ ASSOCIATED_TYPE>(e, supplier.get())));
+ }
+
+ /**
+ * Creates a stream of pairs of the content of the stream and values produces by another stream.
+ * Once any of the two streams is closed, the produced stream is complete, regardless of potential values remaining
+ * in the other stream.
+ *
+ * @param supplier the value supplier.
+ * @param <ASSOCIATED_TYPE> the type of associated values.
+ * @return the built stream.
+ */
+ default <ASSOCIATED_TYPE>
+ ExtendedStream<Pair<ELEMENT_TYPE, ASSOCIATED_TYPE>> associate(final Stream<ASSOCIATED_TYPE> supplier) {
+ List<ELEMENT_TYPE> elements = giveStream().toList();
+ List<ASSOCIATED_TYPE> assocs = supplier.toList();
+ return Seq.of(IntStream.range(0, Math.min(elements.size(),
+ assocs.size())).mapToObj(i -> new Pair<ELEMENT_TYPE, ASSOCIATED_TYPE>(elements.get(i), assocs.get(i))));
+ }
+
+ /**
+ * Prints the element of the stream on the standard output.
+ *
+ * @return this.
+ */
+ default ExtendedStream<ELEMENT_TYPE> print() {
+ return (ExtendedStream<ELEMENT_TYPE>) giveStream().peek(System.out::println);
+ }
+
+ /**
+ * Adds the content of the given stream to the current stream and returns it as a new one.
+ *
+ * @param stream the stream to add.
+ * @return a new stream containing the current one then the given one.
+ */
+ default ExtendedStream<ELEMENT_TYPE> plus(final Stream<ELEMENT_TYPE> stream) {
+ return Seq.of(Stream.concat(giveStream(), stream));
+ }
+
+ /**
+ * Builds a string by joining the string representation of all contained values, interspersed with the given string
+ * delimiter.
+ *
+ * @param delimiter the delimiter string.
+ * @return the built {@link String}.
+ */
+ default String join(final String delimiter) {
+ return giveStream().map(ELEMENT_TYPE::toString).collect(Collectors.joining(delimiter));
+ }
+
+ /**
+ * Builds a string by joining the string representation of all contained values.
+ *
+ * @return the built {@link String}.
+ */
+ default String join() {
+ return giveStream().map(ELEMENT_TYPE::toString).collect(Collectors.joining());
+ }
+
+ /**
+ * Builds a pair of streams by partitioning the current one using the given pivot function.
+ *
+ * @param pivot the function to segregate the values of the given stream.
+ * @param <KEY_TYPE> type of partition key.
+ * @return the pair of created streams.
+ */
+ default <KEY_TYPE>
+ ExtendedStream<Pair<KEY_TYPE, ExtendedStream<ELEMENT_TYPE>>>
+ partition(final Function<ELEMENT_TYPE, KEY_TYPE> pivot) {
+ Map<KEY_TYPE, List<ELEMENT_TYPE>> map = new HashMap<>();
+ giveStream().forEach(e -> {
+ KEY_TYPE key = pivot.apply(e);
+ List<ELEMENT_TYPE> assoc = new ArrayList<>();
+ if (map.containsKey(key)) {
+ assoc.addAll(map.get(key));
+ }
+ assoc.add(e);
+ map.put(key, assoc);
+ });
+ List<Pair<KEY_TYPE, ExtendedStream<ELEMENT_TYPE>>> list = new ArrayList<>();
+ for (KEY_TYPE key : map.keySet()) {
+ list.add(new Pair<>(key, Seq.of(map.get(key).stream())));
+ }
+ return Seq.of(list.stream());
+ }
+
+ @Override
+ public default Iterator<ELEMENT_TYPE> iterator() {
+ return giveStream().iterator();
+ }
+
+ @Override
+ public default Spliterator<ELEMENT_TYPE> spliterator() {
+ return giveStream().spliterator();
+ }
+
+ @Override
+ public default boolean isParallel() {
+ return giveStream().isParallel();
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> sequential() {
+ return giveStream().sequential();
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> parallel() {
+ return giveStream().parallel();
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> unordered() {
+ return giveStream().unordered();
+ }
+
+ @Override
+ public default Stream<ELEMENT_TYPE> onClose(Runnable closeHandler) {
+ return giveStream().onClose(closeHandler);
+ }
+
+ @Override
+ public default void close() {
+ giveStream().close();
+ }
+}
diff --git a/graphs/java/seq/src/test/java/fr/epita/assistants/seq/ExtendedStreamTest.java b/graphs/java/seq/src/test/java/fr/epita/assistants/seq/ExtendedStreamTest.java
new file mode 100644
index 0000000..b0bb911
--- /dev/null
+++ b/graphs/java/seq/src/test/java/fr/epita/assistants/seq/ExtendedStreamTest.java
@@ -0,0 +1,244 @@
+package fr.epita.assistants.seq;
+
+import java.lang.reflect.Modifier;
+import java.time.Duration;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
+
+import fr.epita.assistants.seq.ExtendedStream.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ExtendedStreamTest {
+
+ private static <TYPE> ExtendedStream<TYPE> ctor(final List<TYPE> values) {
+ // FIXME: replace by your own implementation.
+ return Seq.of(values);
+ }
+
+ private static <TYPE> ExtendedStream<TYPE> ctor(final TYPE... values) {
+ // FIXME: replace by your own implementation.
+ return Seq.of(values);
+ }
+
+ private static <TYPE> ExtendedStream<TYPE> ctor(final Stream<TYPE> values) {
+ // FIXME: replace by your own implementation.
+ return Seq.of(values);
+ }
+
+ private static DummyObject dummy(final int id, final String name) {
+ return new DummyObject(id, name);
+ }
+
+ @Test
+ public void isSeqAnInterface() throws ClassNotFoundException {
+ Class<?> seqClass = Class.forName("fr.epita.assistants.seq.Seq");
+ int modifiers = seqClass.getModifiers();
+ boolean isInterface = Modifier.isInterface(modifiers);
+ assertTrue(isInterface, "Seq must be an interface");
+ }
+
+ @Test
+ public void toMapKeyValue() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<DummyObject> es = ctor(dummy(1, "1"), dummy(2, "2"), dummy(2, "4"), dummy(3, "3"));
+ final Map<Integer, String> map = es.toMap(DummyObject::getId, DummyObject::getName);
+ assertEquals(3, map.size());
+ assertEquals("1", map.get(1));
+ assertEquals("4", map.get(2));
+ assertEquals("3", map.get(3));
+ });
+ }
+
+ @Test
+ public void toMapKeyValueMap() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<DummyObject> es = ctor(dummy(1, "1"), dummy(2, "2"), dummy(2, "4"), dummy(3, "3"));
+ final Map<Integer, DummyObject> source = new HashMap<>();
+ source.put(42, new DummyObject(42, "me"));
+ final Map<Integer, DummyObject> map = es.toMap(source, DummyObject::getId, it -> it);
+ assertEquals(4, map.size());
+ assertEquals("1", map.get(1).name);
+ assertEquals("4", map.get(2).name);
+ assertEquals("3", map.get(3).name);
+ assertEquals("me", map.get(42).name);
+ });
+ }
+
+ @Test
+ public void toMapKey() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<DummyObject> es = ctor(dummy(1, "1"), dummy(2, "2"), dummy(2, "4"), dummy(3, "3"));
+ final Map<Integer, DummyObject> map = es.toMap(DummyObject::getId);
+ assertEquals(3, map.size());
+ assertEquals("1", map.get(1).name);
+ assertEquals("4", map.get(2).name);
+ assertEquals("3", map.get(3).name);
+ });
+ }
+
+
+ @Test
+ public void toList() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<DummyObject> es = ctor(dummy(1, "1"), dummy(2, "2"), dummy(2, "4"), dummy(3, "3"));
+ final List<DummyObject> list = es.toList();
+ assertEquals(4, list.size());
+ assertEquals("1", list.get(0).name);
+ assertEquals("2", list.get(1).name);
+ assertEquals("4", list.get(2).name);
+ assertEquals("3", list.get(3).name);
+
+ });
+ }
+
+ @Test
+ public void toListWithList() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<DummyObject> es = ctor(dummy(1, "1"), dummy(2, "2"), dummy(2, "4"), dummy(3, "3"));
+ final List<DummyObject> source = new ArrayList<>();
+ source.add(new DummyObject(42, "me"));
+ final List<DummyObject> list = es.toList(source);
+ assertEquals(5, list.size());
+ assertEquals("me", list.get(0).name);
+ assertEquals("1", list.get(1).name);
+ assertEquals("2", list.get(2).name);
+ assertEquals("4", list.get(3).name);
+ assertEquals("3", list.get(4).name);
+ });
+ }
+
+ @Test
+ public void toSet() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<Integer> es = ctor(1, 2, 2, 3);
+ final Set<Integer> set = es.toSet();
+ assertEquals(3, set.size());
+ assertTrue(set.contains(1));
+ assertTrue(set.contains(2));
+ assertTrue(set.contains(3));
+ });
+ }
+
+ @Test
+ public void toSetWithSet() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<Integer> es = ctor(1, 2, 2, 3);
+ final Set<Integer> source = new HashSet<>();
+ source.add(1);
+ source.add(2);
+ source.add(42);
+ final Set<Integer> set = es.toSet(source);
+ assertEquals(4, set.size());
+ assertTrue(set.contains(1));
+ assertTrue(set.contains(2));
+ assertTrue(set.contains(3));
+ assertTrue(set.contains(42));
+ });
+ }
+
+ @Test
+ public void associateWithSupplier() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<String> es = ctor("a", "b", "c");
+ final List<Pair<String, Integer>> list = es.associate(new Increment()).toList();
+
+ assertEquals(3, list.size());
+ assertEquals(new Pair<>("a", 0), list.get(0));
+ assertEquals(new Pair<>("b", 1), list.get(1));
+ assertEquals(new Pair<>("c", 2), list.get(2));
+
+
+ });
+ }
+
+ @Test
+ public void associateWithStream() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<String> es = ctor("a", "b", "c");
+ final List<Pair<String, Integer>> list = es.associate(ctor(0, 1, 2, 3, 4, 5)).toList();
+
+ assertEquals(3, list.size());
+ assertEquals(new Pair<>("a", 0), list.get(0));
+ assertEquals(new Pair<>("b", 1), list.get(1));
+ assertEquals(new Pair<>("c", 2), list.get(2));
+ });
+ }
+
+ @Test
+ public void plus() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<String> es = ctor("a", "b", "c").plus(ctor("d", "e", "f"));
+ assertEquals("abcdef", es.join());
+ });
+ }
+
+ @Test
+ public void join() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<String> es = ctor("a", "b", "c", "d", "e", "f");
+ assertEquals("abcdef", es.join());
+ });
+ }
+
+ @Test
+ public void joinWithDelimiter() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<String> es = ctor("a", "b", "c", "d", "e", "f");
+ assertEquals("a-b-c-d-e-f", es.join("-"));
+ });
+ }
+
+ @Test
+ public void partition() {
+ assertTimeoutPreemptively(Duration.ofSeconds(10), () -> {
+ final ExtendedStream<Integer> es = ctor(0, 1, 2, 3, 4, 5, 6, 7);
+ final ExtendedStream<Pair<Boolean, ExtendedStream<Integer>>> partitions = es.partition(val -> val % 2 == 0);
+ final List<Pair<Boolean, ExtendedStream<Integer>>> list = partitions.toList();
+
+
+ assertEquals(list.get(0).first ? "0246" : "1357", list.get(0).second.join());
+ assertEquals(list.get(0).first ? "1357" : "0246", list.get(1).second.join());
+ });
+ }
+
+ static class DummyObject {
+ public final Integer id;
+ public final String name;
+
+ public DummyObject(final Integer id, final String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+
+ static class Increment implements Supplier<Integer> {
+ private int increment = 0;
+
+ @Override
+ public Integer get() {
+ return increment++;
+ }
+ }
+}