diff --git a/solutions/chipak/pom.xml b/solutions/chipak/pom.xml
new file mode 100644
index 0000000..4c43ee9
--- /dev/null
+++ b/solutions/chipak/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+ ru.fizteh.fivt.students
+ parent
+ 1.0-SNAPSHOT
+
+ ru.fizteh.fivt.students
+ chipak
+ 1.0-SNAPSHOT
+ chipak
+ http://maven.apache.org
+
+ UTF-8
+
+
+
+ junit
+ junit
+ 3.8.1
+
+
+ org.twitter4j
+ twitter4j-stream
+ [4.0,)
+
+
+ com.beust
+ jcommander
+ 1.48
+
+
+ com.h2database
+ h2
+ 1.4.190
+
+
+
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/Threads/BlockingQueue.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/Threads/BlockingQueue.java
new file mode 100644
index 0000000..581c169
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/Threads/BlockingQueue.java
@@ -0,0 +1,106 @@
+package ru.fizteh.fivt.students.chipak.Threads;
+
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BlockingQueue {
+
+ private Queue queue;
+ private int maxSize;
+ private final Lock lock = new ReentrantLock();
+ private final Condition notEnoughSpace = lock.newCondition();
+ private final Condition notEnoughElements = lock.newCondition();
+ private final Object offerSynchronizer = new Object();
+ private final Object takeSynchronizer = new Object();
+
+ BlockingQueue(int size) {
+ queue = new ArrayDeque();
+ maxSize = size;
+ }
+
+ void offer(List toAdd) throws InterruptedException {
+ synchronized (offerSynchronizer) {
+ lock.lock();
+ try {
+ while ((queue.size() + toAdd.size()) > maxSize) {
+ notEnoughSpace.await();
+ }
+ queue.addAll(toAdd);
+ notEnoughElements.signalAll();
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+
+ List take(int n) throws InterruptedException {
+ synchronized (takeSynchronizer) {
+ lock.lock();
+ List ans = new ArrayList();
+ try {
+
+ while (queue.size() < n) {
+ notEnoughElements.await();
+ }
+ for (int i = 0; i < n; ++i) {
+ ans.add(queue.remove());
+ }
+ notEnoughElements.signalAll();
+ } finally {
+ lock.unlock();
+ return ans;
+ }
+ }
+ }
+
+ void offer(List toAdd, long timeout) throws InterruptedException {
+ synchronized (offerSynchronizer) {
+ lock.lock();
+ long waitingTime = timeout;
+ final long startTime = System.currentTimeMillis();
+ try {
+ while (queue.size() + toAdd.size() > maxSize && waitingTime > 0) {
+ notEnoughElements.await(waitingTime, TimeUnit.MILLISECONDS);
+ waitingTime = timeout - (System.currentTimeMillis() - startTime);
+ }
+ if (queue.size() + toAdd.size() <= maxSize) {
+ queue.addAll(toAdd);
+ notEnoughElements.notifyAll();
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+
+ List take(int n, long timeout) throws InterruptedException {
+ synchronized (takeSynchronizer) {
+ lock.lock();
+ List ans = new ArrayList();
+ long waitingTime = timeout;
+ final long startTime = System.currentTimeMillis();
+ try {
+ while (queue.size() < n && waitingTime > 0) {
+ notEnoughElements.await(waitingTime, TimeUnit.MILLISECONDS);
+ waitingTime = timeout - (System.currentTimeMillis() - startTime);
+ }
+ if (queue.size() >= n) {
+ for (int i = 0; i < n; ++i) {
+ ans.add(queue.remove());
+ }
+ notEnoughElements.notifyAll();
+ }
+ } finally {
+ lock.unlock();
+ return ans;
+ }
+ }
+ }
+}
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/Threads/Counter.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/Threads/Counter.java
new file mode 100644
index 0000000..7129209
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/Threads/Counter.java
@@ -0,0 +1,54 @@
+package ru.fizteh.fivt.students.chipak.Threads;
+
+
+
+public class Counter {
+
+ private static volatile int currentID;
+
+ private static Object synchronizer = new Object();
+
+ private static class OneCounter implements Runnable {
+ private int id, size;
+
+
+ OneCounter(int id, int size) {
+ this.id = id;
+ this.size = size;
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ synchronized (synchronizer) {
+ while (id != currentID) {
+ synchronizer.wait();
+ }
+ System.out.println("Thread-" + String.valueOf(id));
+ currentID++;
+ if (currentID > size) {
+ currentID %= size;
+ }
+ synchronizer.notifyAll();
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args) throws IllegalArgumentException {
+ if (args.length != 1) {
+ throw new IllegalArgumentException();
+ }
+ int n;
+ n = Integer.valueOf(args[0]);
+ currentID = 1;
+ for (int i = 0; i < n; i++) {
+ Thread oneCounter = new Thread(new OneCounter(i + 1, n));
+ oneCounter.start();
+ }
+ }
+}
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Aggregates.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Aggregates.java
new file mode 100644
index 0000000..cfc4af3
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Aggregates.java
@@ -0,0 +1,102 @@
+package ru.fizteh.fivt.students.chipak.collectionquery;
+
+import java.util.List;
+import java.util.function.Function;
+
+
+public class Aggregates {
+
+
+ public static > Function max(Function expression) {
+ return new AggregationFunction() {
+ private Function converter = expression;
+
+ @Override
+ public T apply(List list) {
+ T result = null;
+ for (C element : list) {
+ if (result == null) {
+ result = converter.apply(element);
+ } else {
+ T currentResult = converter.apply(element);
+ if (currentResult.compareTo(result) > 0) {
+ result = currentResult;
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public T apply(C c) {
+ return null;
+ }
+ };
+ }
+
+
+ public static > Function min(Function expression) {
+ return new AggregationFunction() {
+ private Function converter = expression;
+
+ @Override
+ public T apply(List list) {
+ T result = null;
+ for (C element : list) {
+ if (result == null) {
+ result = converter.apply(element);
+ } else {
+ T currentResult = converter.apply(element);
+ if (currentResult.compareTo(result) < 0) {
+ result = currentResult;
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public T apply(C c) {
+ return null;
+ }
+ };
+ }
+
+
+ public static Function count(Function expression) {
+ return new AggregationFunction() {
+ @Override
+ public Integer apply(List list) {
+ return list.size();
+ }
+
+ @Override
+ public Integer apply(C c) {
+ return null;
+ }
+ };
+ }
+
+
+ public static Function avg(Function expression) {
+ return new AggregationFunction() {
+ private Function converter = expression;
+
+ @Override
+ public Double apply(List list) {
+ Double result = 0d;
+ for (C element : list) {
+ result += converter.apply(element).doubleValue();
+ }
+ result /= list.size();
+ return result;
+ }
+
+ @Override
+ public Double apply(C t) {
+ return null;
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/AggregationFunction.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/AggregationFunction.java
new file mode 100644
index 0000000..27267f3
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/AggregationFunction.java
@@ -0,0 +1,8 @@
+package ru.fizteh.fivt.students.chipak.collectionquery;
+
+import java.util.List;
+import java.util.function.Function;
+
+public interface AggregationFunction extends Function {
+ E apply(List list);
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/CollectionQuery.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/CollectionQuery.java
new file mode 100644
index 0000000..b336c26
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/CollectionQuery.java
@@ -0,0 +1,145 @@
+package ru.fizteh.fivt.students.chipak.collectionquery;
+
+import ru.fizteh.fivt.students.chipak.collectionquery.impl.Tuple;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Objects;
+
+import static ru.fizteh.fivt.students.chipak.collectionquery.Aggregates.avg;
+import static ru.fizteh.fivt.students.chipak.collectionquery.Aggregates.count;
+import static ru.fizteh.fivt.students.chipak.collectionquery.CollectionQuery.Student.student;
+import static ru.fizteh.fivt.students.chipak.collectionquery.Conditions.rlike;
+import static ru.fizteh.fivt.students.chipak.collectionquery.OrderByConditions.asc;
+import static ru.fizteh.fivt.students.chipak.collectionquery.OrderByConditions.desc;
+import static ru.fizteh.fivt.students.chipak.collectionquery.Sources.list;
+import static ru.fizteh.fivt.students.chipak.collectionquery.impl.FromStmt.from;
+
+/**
+ * @author akormushin
+ */
+public class CollectionQuery {
+
+ /**
+ * Make this code work!
+ *
+ * @param args
+ */
+ public static void main(String[] args) throws Exception {
+ Iterable statistics =
+ from(list(
+ student("ivanov", LocalDate.parse("1986-08-06"), "494"),
+ student("sidorov", LocalDate.parse("1986-08-06"), "495"),
+ student("smith", LocalDate.parse("1986-08-06"), "495"),
+ student("petrov", LocalDate.parse("2006-08-06"), "494")))
+ .select(Statistics.class, Student::getGroup, count(Student::getGroup), avg(Student::age))
+ .where(rlike(Student::getName, ".*ov").and(s -> s.age() > 20))
+ .groupBy(Student::getGroup)
+ .having(s -> s.getCount() > 0)
+ .orderBy(asc(Statistics::getGroup), desc(Statistics::getCount))
+ .limit(100)
+ .union()
+ .from(list(student("ivanov", LocalDate.parse("1985-08-06"), "494")))
+ .selectDistinct(Statistics.class, s -> "all", count(s -> 1), avg(Student::age))
+ .execute();
+ System.out.println(statistics);
+
+ Iterable> mentorsByStudent =
+ from(list(student("ivanov", LocalDate.parse("1985-08-06"), "494")))
+ .join(list(new Group("494", "mr.sidorov")))
+ .on((s, g) -> Objects.equals(s.getGroup(), g.getGroup()))
+ .select(sg -> sg.getFirst().getName(), sg -> sg.getSecond().getMentor())
+ .execute();
+ System.out.println(mentorsByStudent);
+ }
+
+
+ public static class Student {
+ private final String name;
+
+ private final LocalDate dateOfBith;
+
+ private final String group;
+
+ public String getName() {
+ return name;
+ }
+
+ public Student(String name, LocalDate dateOfBith, String group) {
+ this.name = name;
+ this.dateOfBith = dateOfBith;
+ this.group = group;
+ }
+
+ public LocalDate getDateOfBith() {
+ return dateOfBith;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public long age() {
+ return ChronoUnit.YEARS.between(getDateOfBith(), LocalDateTime.now());
+ }
+
+ public static Student student(String name, LocalDate dateOfBith, String group) {
+ return new Student(name, dateOfBith, group);
+ }
+ }
+
+ public static class Group {
+ private final String group;
+ private final String mentor;
+
+ public Group(String group, String mentor) {
+ this.group = group;
+ this.mentor = mentor;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public String getMentor() {
+ return mentor;
+ }
+ }
+
+
+ public static class Statistics {
+
+ private final String group;
+ private final Integer count;
+ private final Double age;
+
+ public String getGroup() {
+ return group;
+ }
+
+ public Integer getCount() {
+ return count;
+ }
+
+ public Double getAge() {
+ return age;
+ }
+
+ public Statistics(String group, Integer count, Double age) {
+ this.group = group;
+ this.count = count;
+ this.age = age;
+ }
+
+ @Override
+ public String toString() {
+ return "Statistics{"
+ + "group='" + group + '\''
+ + ", count=" + count
+ + ", age=" + age
+ + '}';
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Conditions.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Conditions.java
new file mode 100644
index 0000000..39eab30
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Conditions.java
@@ -0,0 +1,37 @@
+package ru.fizteh.fivt.students.chipak.collectionquery;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Where clause conditions.
+ *
+ * @author akormushin
+ */
+public class Conditions {
+
+ /**
+ * Matches string result of expression against regexp pattern.
+ *
+ * @param expression expression result to match
+ * @param regexp pattern to match to
+ * @param source object type
+ * @return
+ */
+ public static Predicate rlike(Function expression, String regexp) {
+ return (object) -> (expression.apply(object).matches(regexp));
+ }
+
+ /**
+ * Matches string result of expression against SQL like pattern.
+ *
+ * @param expression expression result to match
+ * @param pattern pattern to match to
+ * @param source object type
+ * @return
+ */
+ public static Predicate like(Function expression, String pattern) {
+ throw new UnsupportedOperationException();
+ }
+
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/OrderByConditions.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/OrderByConditions.java
new file mode 100644
index 0000000..67c262d
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/OrderByConditions.java
@@ -0,0 +1,37 @@
+package ru.fizteh.fivt.students.chipak.collectionquery;
+
+import java.util.Comparator;
+import java.util.function.Function;
+
+/**
+ * OrderBy sort order helper methods.
+ *
+ * @author akormushin
+ */
+public class OrderByConditions {
+
+ /**
+ * Ascending comparator.
+ *
+ * @param expression
+ * @param
+ * @param
+ * @return
+ */
+ public static > Comparator asc(Function expression) {
+ return (o1, o2) -> expression.apply(o1).compareTo(expression.apply(o2));
+ }
+
+ /**
+ * Descending comparator.
+ *
+ * @param expression
+ * @param
+ * @param
+ * @return
+ */
+ public static > Comparator desc(Function expression) {
+ return (o1, o2) -> expression.apply(o2).compareTo(expression.apply(o1));
+ }
+
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Sources.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Sources.java
new file mode 100644
index 0000000..633838a
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/Sources.java
@@ -0,0 +1,56 @@
+package ru.fizteh.fivt.students.chipak.collectionquery;
+
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+/**
+ * Helper methods to create collections.
+ *
+ * @author akormushin
+ */
+public class Sources {
+
+ /**
+ * @param items
+ * @param
+ * @return
+ */
+ @SafeVarargs
+ public static List list(T... items) {
+ return new ArrayList<>(Arrays.asList(items));
+ }
+
+ /**
+ * @param items
+ * @param
+ * @return
+ */
+ @SafeVarargs
+ public static Set set(T... items) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param inputStream
+ * @param
+ * @return
+ */
+ public static Stream lines(InputStream inputStream) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param file
+ * @param
+ * @return
+ */
+ public static Stream lines(Path file) {
+ throw new UnsupportedOperationException();
+ }
+
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/FromStmt.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/FromStmt.java
new file mode 100644
index 0000000..434952a
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/FromStmt.java
@@ -0,0 +1,158 @@
+package ru.fizteh.fivt.students.chipak.collectionquery.impl;
+
+import com.beust.jcommander.internal.Lists;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+public class FromStmt {
+ private List> previousResult;
+ private List elements;
+
+
+ public FromStmt(Iterable iterable) {
+ elements = StreamSupport.stream(iterable.spliterator(), false).
+ collect(Collectors.toList());
+ }
+
+ public FromStmt(Stream stream) {
+ elements = stream.collect(Collectors.toList());
+ }
+
+ public void setPreviousResult(List previousResult) {
+ this.previousResult = previousResult;
+ }
+
+
+ public static FromStmt from(Iterable iterable) {
+ return new FromStmt(iterable);
+ }
+
+ public static FromStmt from(Stream stream) {
+ return new FromStmt(stream);
+ }
+
+ public static FromStmt from(Query query) throws
+ ReflectiveOperationException {
+ return new FromStmt(query.execute());
+ }
+
+ @SafeVarargs
+ public final SelectStmt select(Class clazz, Function... s) {
+ return new SelectStmt<>(previousResult, elements, clazz, false, s);
+ }
+
+ /**
+ * Selects the only defined expression as is without wrapper.
+ *
+ * @param s
+ * @param
+ * @return statement resulting in collection of R
+ */
+ public final SelectStmt select(Function s) {
+ return new SelectStmt<>(previousResult, elements, false, s);
+ }
+
+ /**
+ * Selects the only defined expression as is without wrapper.
+ *
+ * @param first
+ * @param second
+ * @param
+ * @param
+ * @return statement resulting in collection of R
+ */
+ public final SelectStmt> select(Function first, Function second) {
+ Function> converter = (element) -> (new Tuple<>(first.apply(element),
+ second.apply(element)));
+ return new SelectStmt<>(previousResult, elements, false, converter);
+ }
+
+ @SafeVarargs
+ public final SelectStmt selectDistinct(Class clazz, Function... s) {
+ return new SelectStmt(previousResult, elements, clazz, true, s);
+ }
+
+ /**
+ * Selects the only defined expression as is without wrapper.
+ *
+ * @param s
+ * @param
+ * @return statement resulting in collection of R
+ */
+ public final SelectStmt selectDistinct(Function s) {
+ return new SelectStmt(previousResult, elements, true, s);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public JoinClause join(Iterable iterable) {
+ return new JoinClause<>(this, StreamSupport.stream(iterable.spliterator(), false).
+ collect(Collectors.toList()));
+ }
+
+ public JoinClause join(Stream stream) {
+ return new JoinClause<>(this, stream.collect(Collectors.toList()));
+ }
+
+ public JoinClause join(Query stream) throws ReflectiveOperationException {
+ return new JoinClause<>(this,
+ StreamSupport.stream(stream.execute().spliterator(), false).
+ collect(Collectors.toList()));
+ }
+
+ public class JoinClause {
+ private FromStmt fromStmt;
+ private List extraSource;
+
+ public JoinClause(FromStmt fromStmt, List extraSource) {
+ this.fromStmt = fromStmt;
+ this.extraSource = extraSource;
+ }
+
+ public FromStmt> on(BiPredicate condition) {
+ List> sourceForFrom = new ArrayList<>();
+ for (T firstElement : fromStmt.elements) {
+ for (J secondElement : extraSource) {
+ if (condition.test(firstElement, secondElement)) {
+ sourceForFrom.add(new Tuple<>(firstElement, secondElement));
+ }
+ }
+ }
+ FromStmt> newFrom = new FromStmt>(sourceForFrom);
+ newFrom.setPreviousResult(fromStmt.previousResult);
+ return newFrom;
+ }
+
+ public > FromStmt> on(
+ Function leftKey,
+ Function rightKey) {
+ Map> mapForFirstSource = new HashMap<>();
+ List> sourceForFrom = new ArrayList<>();
+ for (T element : fromStmt.elements) {
+ if (mapForFirstSource.containsKey(leftKey.apply(element))) {
+ mapForFirstSource.get(leftKey.apply(element)).add(element);
+ } else {
+ mapForFirstSource.put(leftKey.apply(element), Lists.newArrayList(element));
+ }
+ }
+ extraSource.stream().filter(element -> mapForFirstSource.
+ containsKey(rightKey.apply(element))).forEach(element -> {
+ sourceForFrom.addAll(mapForFirstSource.
+ get(rightKey.apply(element)).stream().
+ map(firstElement -> new Tuple<>(firstElement, element)).
+ collect(Collectors.toList()));
+ });
+ FromStmt> newFrom = new FromStmt<>(sourceForFrom);
+ newFrom.setPreviousResult(fromStmt.previousResult);
+ return newFrom;
+ }
+ }
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/GroupByResult.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/GroupByResult.java
new file mode 100644
index 0000000..574a77e
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/GroupByResult.java
@@ -0,0 +1,40 @@
+package ru.fizteh.fivt.students.chipak.collectionquery.impl;
+
+import java.util.function.Function;
+
+public class GroupByResult {
+ private Object[] results;
+
+ public GroupByResult(T element, Function[] functions) {
+ results = new Object[functions.length];
+ for (int i = 0; i < functions.length; ++i) {
+ results[i] = functions[i].apply(element);
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GroupByResult)) {
+ return false;
+ }
+ GroupByResult anotherResult = (GroupByResult) obj;
+ if (results.length != anotherResult.results.length) {
+ return false;
+ }
+ for (int i = 0; i < results.length; ++i) {
+ if (!results[i].equals(anotherResult.results[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int ans = 0;
+ for (Object element : results) {
+ ans += element.hashCode();
+ }
+ return ans;
+ }
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/Query.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/Query.java
new file mode 100644
index 0000000..9736e7a
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/Query.java
@@ -0,0 +1,11 @@
+package ru.fizteh.fivt.students.chipak.collectionquery.impl;
+
+import java.util.stream.Stream;
+
+
+public interface Query {
+
+ Iterable execute() throws ReflectiveOperationException;
+
+ Stream stream() throws ReflectiveOperationException;
+}
\ No newline at end of file
diff --git a/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/SelectStmt.java b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/SelectStmt.java
new file mode 100644
index 0000000..3698a39
--- /dev/null
+++ b/solutions/chipak/src/main/java/ru/fizteh/fivt/students/chipak/collectionquery/impl/SelectStmt.java
@@ -0,0 +1,254 @@
+package ru.fizteh.fivt.students.chipak.collectionquery.impl;
+
+import ru.fizteh.fivt.students.chipak.collectionquery.AggregationFunction;
+
+import java.lang.reflect.Constructor;
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+
+public class SelectStmt implements Query {
+ private List source;
+ private boolean isDistinct;
+ private boolean hasConvertingFunction;
+ private Function converter;
+ private Function[] functions;
+ private Class rClass;
+ private List> previousResult;
+
+ @SafeVarargs
+ public SelectStmt(List> previousResult, List source, Class rClass,
+ boolean isDistinct, Function... functions) {
+ this.functions = functions;
+ hasConvertingFunction = false;
+ this.converter = null;
+ this.isDistinct = isDistinct;
+ this.source = source;
+ this.rClass = rClass;
+ this.previousResult = previousResult;
+ }
+
+ public SelectStmt(List> previousResult, List source, boolean isDistinct,
+ Function s) {
+ this.source = source;
+ hasConvertingFunction = true;
+ this.isDistinct = isDistinct;
+ this.converter = s;
+ this.functions = null;
+ this.previousResult = previousResult;
+ }
+
+
+ public WhereStmt where(Predicate predicate) {
+ return new WhereStmt<>(source, predicate, isDistinct, hasConvertingFunction,
+ converter, functions, rClass);
+ }
+
+ @Override
+ public Iterable execute() throws ReflectiveOperationException {
+ return new WhereStmt(source, element -> true, isDistinct,
+ hasConvertingFunction, converter, functions, rClass).execute();
+
+ }
+
+ @Override
+ public Stream stream() throws ReflectiveOperationException {
+ return StreamSupport.stream(execute().spliterator(), false);
+ }
+
+
+ public class WhereStmt implements Query {
+
+ private List source;
+ private Predicate wherePredicate;
+ private Function[] groupByExpressions;
+ private Comparator[] orderByComparators;
+ private Predicate havingPredicate;
+ private boolean isDistinct;
+ private boolean hasConvertingFunction;
+ private Function converter;
+ private Function[] functions;
+ private Class rClass;
+ private int limit = -1;
+
+ public WhereStmt(List source, Predicate wherePredicate, boolean isDistinct,
+ boolean hasConvertingFunction, Function converter,
+ Function[] functions, Class rClass) {
+ this.source = source;
+ this.wherePredicate = wherePredicate;
+ this.isDistinct = isDistinct;
+ this.hasConvertingFunction = hasConvertingFunction;
+ this.converter = converter;
+ this.functions = functions;
+ this.rClass = rClass;
+ }
+
+ @SafeVarargs
+ public final WhereStmt groupBy(Function... expressions) {
+ groupByExpressions = expressions;
+ return this;
+ }
+
+ @SafeVarargs
+ public final WhereStmt orderBy(Comparator... comparators) {
+ orderByComparators = comparators;
+ return this;
+ }
+
+ public WhereStmt having(Predicate condition) {
+ havingPredicate = condition;
+ return this;
+ }
+
+ public WhereStmt limit(int amount) {
+ if (amount < 0) {
+ throw new IllegalArgumentException("limit < 0");
+ }
+ limit = amount;
+ return this;
+ }
+
+ public UnionStmt union() throws ReflectiveOperationException {
+ return new UnionStmt(StreamSupport.stream(execute().spliterator(), false)
+ .collect(Collectors.toList()));
+ }
+
+ @Override
+ public Iterable execute() throws ReflectiveOperationException {
+ List> result = new ArrayList<>();
+ if (wherePredicate != null) {
+ source = source.stream().filter(wherePredicate::test).collect(Collectors.toList());
+ }
+ if (groupByExpressions != null) {
+ List> groupedSource = new ArrayList<>();
+ Map, Integer> mapOfResultWithNumber = new HashMap<>();
+ Map> mapOfElementWithResult = new HashMap<>();
+ for (T element : source) {
+ GroupByResult resultForElement = new GroupByResult<>(element, groupByExpressions);
+ mapOfElementWithResult.put(element, resultForElement);
+ if (!mapOfResultWithNumber.containsKey(resultForElement)) {
+ mapOfResultWithNumber.put(resultForElement, mapOfResultWithNumber.size());
+ }
+ }
+ for (int i = 0; i < mapOfResultWithNumber.size(); ++i) {
+ groupedSource.add(new ArrayList<>());
+ }
+ for (T element : source) {
+ groupedSource.get(mapOfResultWithNumber.get(mapOfElementWithResult.get(element))).
+ add(element);
+ }
+ for (List source : groupedSource) {
+ result.add(executeOnList(source));
+ }
+ } else {
+ result.add(executeOnList(source));
+ }
+ if (havingPredicate != null) {
+ List> newResult = new ArrayList<>();
+ for (List element : result) {
+ newResult.add(element.stream().filter(havingPredicate::test).
+ collect(Collectors.toList()));
+ }
+ result = newResult;
+ }
+ if (orderByComparators != null) {
+ for (List list : result) {
+ list.sort(compileComparator(orderByComparators));
+ }
+ }
+ List ans = new ArrayList<>();
+ for (List element : result) {
+ ans.addAll(element);
+ }
+ if (limit >= 0) {
+ ans = ans.subList(0, Math.min(ans.size(), limit));
+ }
+ if (previousResult != null) {
+ if (previousResult.size() > 0) {
+ if (rClass != null && previousResult.get(0).getClass() != rClass
+ || ans.size() > 0
+ && previousResult.get(0).getClass() != ans.get(0).getClass()) {
+ throw new IllegalArgumentException("Can't call union");
+ } else {
+ List newAns = (List) previousResult;
+ newAns.addAll(ans);
+ ans = newAns;
+ }
+ }
+ }
+
+ return ans;
+ }
+
+ private Comparator compileComparator(Comparator[] orderByComparators) {
+ return (first, second) -> {
+ for (int i = 0; i < orderByComparators.length - 1; ++i) {
+ if (orderByComparators[i].compare(first, second) != 0) {
+ return orderByComparators[i].compare(first, second);
+ }
+ }
+ return orderByComparators[orderByComparators.length - 1].compare(first, second);
+ };
+ }
+
+ @Override
+ public Stream stream() throws ReflectiveOperationException {
+ return StreamSupport.stream(execute().spliterator(), false);
+ }
+
+ private List executeOnList(List currentSource) throws ReflectiveOperationException {
+ List result = new ArrayList<>();
+ if (hasConvertingFunction) {
+ for (T element : currentSource) {
+ result.add(converter.apply(element));
+ }
+ } else {
+ List> temporaryResults;
+ temporaryResults = new ArrayList<>();
+ for (int i = 0; i < currentSource.size(); ++i) {
+ temporaryResults.add(new ArrayList<>(functions.length));
+ }
+ for (Function function : functions) {
+ if (function instanceof AggregationFunction) {
+ Object resultOfFunction = ((AggregationFunction) function).apply(currentSource);
+ for (int j = 0; j < currentSource.size(); ++j) {
+ temporaryResults.get(j).add(resultOfFunction);
+ }
+ } else {
+ for (int j = 0; j < currentSource.size(); ++j) {
+ temporaryResults.get(j).add(function.apply(currentSource.get(j)));
+ }
+ }
+ }
+ for (List