diff --git a/projects/elinrin/pom.xml b/projects/elinrin/pom.xml
new file mode 100644
index 0000000..2a0e80e
--- /dev/null
+++ b/projects/elinrin/pom.xml
@@ -0,0 +1,120 @@
+
+ 4.0.0
+
+ ru.mipt.diht.students
+ parent
+ 1.0-SNAPSHOT
+
+
+ ru.mipt.diht.students
+ elinrin
+ 1.0-SNAPSHOT
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.8
+ 1.8
+
+
+
+
+ jar
+
+ elinrin
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+
+ com.h2database
+ h2
+ 1.4.190
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+
+ com.beust
+ jcommander
+ 1.48
+
+
+
+ org.twitter4j
+ twitter4j-stream
+ 4.0.4
+
+
+
+ org.json
+ json
+ 20141113
+
+
+
+ com.google.guava
+ guava
+ 17.0
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+ org.mockito
+ mockito-all
+ 1.9.5
+
+
+
+ org.powermock
+ powermock-module-junit4
+ 1.6.1
+ test
+ true
+
+
+ junit
+ junit
+
+
+
+
+
+ org.powermock
+ powermock-api-mockito
+ 1.6.1
+ test
+ true
+
+
+ mockito-all
+ org.mockito
+
+
+
+
+ com.tngtech.java
+ junit-dataprovider
+ 1.10.1
+
+
+
+
diff --git a/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Aggregates.java b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Aggregates.java
new file mode 100644
index 0000000..c590518
--- /dev/null
+++ b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Aggregates.java
@@ -0,0 +1,27 @@
+package ru.mipt.diht.students.elinrin.collectionquery;
+
+import ru.mipt.diht.students.elinrin.collectionquery.aggregatesImpl.Avg;
+import ru.mipt.diht.students.elinrin.collectionquery.aggregatesImpl.Count;
+import ru.mipt.diht.students.elinrin.collectionquery.aggregatesImpl.Max;
+import ru.mipt.diht.students.elinrin.collectionquery.aggregatesImpl.Min;
+
+import java.util.function.Function;
+
+public class Aggregates {
+ public static Function max(final Function expression) {
+ return new Max<>(expression);
+ }
+
+ public static > Function min(final Function expression) {
+ return new Min<>(expression);
+ }
+
+ public static Function count(final Function expression) {
+ return new Count<>(expression);
+ }
+
+ public static Function avg(final Function expression) {
+ return new Avg<>(expression);
+ }
+
+}
diff --git a/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/CollectionQuery.java b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/CollectionQuery.java
new file mode 100644
index 0000000..6c7d32d
--- /dev/null
+++ b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/CollectionQuery.java
@@ -0,0 +1,217 @@
+package ru.mipt.diht.students.elinrin.collectionquery;
+
+import ru.mipt.diht.students.elinrin.collectionquery.impl.Tuple;
+
+import java.lang.reflect.InvocationTargetException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Objects;
+
+import static ru.mipt.diht.students.elinrin.collectionquery.Aggregates.avg;
+import static ru.mipt.diht.students.elinrin.collectionquery.Aggregates.count;
+import static ru.mipt.diht.students.elinrin.collectionquery.CollectionQuery.Student.student;
+import static ru.mipt.diht.students.elinrin.collectionquery.Conditions.rlike;
+import static ru.mipt.diht.students.elinrin.collectionquery.OrderByConditions.asc;
+import static ru.mipt.diht.students.elinrin.collectionquery.OrderByConditions.desc;
+import static ru.mipt.diht.students.elinrin.collectionquery.Sources.list;
+import static ru.mipt.diht.students.elinrin.collectionquery.impl.FromStmt.from;
+
+/*nnn*/
+
+public class CollectionQuery {
+ static final int TWENTY = 20;
+ static final int HUNDRED = 100;
+ /**
+ * Make this code work!
+ *
+ * @param args
+ */
+ public static void main(final String[] args) throws InvocationTargetException, NoSuchMethodException,
+ InstantiationException, IllegalAccessException {
+ Iterable statistics =
+ from(list(
+ student("ivanov", LocalDate.parse("1986-08-06"), "496"),
+ student("ivanov", LocalDate.parse("1986-08-06"), "496")))
+ .select(Statistics.class, Student::getGroup, count(Student::getGroup), avg(Student::age))
+ .where(rlike(Student::getName, ".*ov").and(s -> s.age() > TWENTY))
+ .groupBy(Student::getName)
+ .having(s -> s.getCount() > 0)
+ .orderBy(asc(Statistics::getGroup), desc(count(Statistics::getGroup)))
+ .limit(HUNDRED)
+ .union()
+ .from(list(student("ivanov", LocalDate.parse("1985-08-06"), "496")))
+ .selectDistinct(Statistics.class, s -> "all",
+ count(Student::getDateOfBith), avg(Student::age))
+ .groupBy(Student::getGroup)
+ .execute();
+ statistics.forEach(System.out::print);
+
+
+ Iterable> mentorsByStudent =
+ from(list(student("ivanov", LocalDate.parse("1985-08-06"), "496"),
+ student("sidorov", LocalDate.parse("1986-08-06"), "497"),
+ student("vasilev", LocalDate.parse("1986-08-06"), "496"),
+ student("petrov", LocalDate.parse("1986-08-06"), "497")))
+ .join(list(Group.group("497", "mr.solovev")))
+ .on((s, g) -> Objects.equals(s.getGroup(), g.getGroup()))
+ .select(sg -> sg.getFirst().getName(), sg -> sg.getSecond().getMentor())
+ .where(s -> Objects.equals(s.getFirst().getName(), "sidorov"))
+ .union()
+ .from(list(student("ivanov", LocalDate.parse("1985-08-06"), "496"),
+ student("sidorov", LocalDate.parse("1986-08-06"), "497"),
+ student("vasilev", LocalDate.parse("1986-08-06"), "496"),
+ student("petrov", LocalDate.parse("1986-08-06"), "497")))
+ .join(list(Group.group("496", "mr.ilanov")))
+ .on(s -> s.getGroup(), f -> f.getGroup())
+ .select(sg -> sg.getFirst().getName(), sg -> sg.getSecond().getMentor())
+ .execute();
+ mentorsByStudent.forEach(System.out::print);
+ }
+
+ public static class Student {
+ private final String name;
+
+ private final LocalDate dateOfBith;
+
+ private final String group;
+
+ public final String getName() {
+ return name;
+ }
+
+ public Student(final String gottenName, final LocalDate gottenDateOfBith, final String gottenGroup) {
+ name = gottenName;
+ dateOfBith = gottenDateOfBith;
+ group = gottenGroup;
+ }
+
+ public Student(final String gottenName, final String gottenGroup) {
+ name = gottenName;
+ dateOfBith = null;
+ group = gottenGroup;
+ }
+
+ public final LocalDate getDateOfBith() {
+ return dateOfBith;
+ }
+
+ public final String getGroup() {
+ return group;
+ }
+
+ public final Double age() {
+ return (double) ChronoUnit.YEARS.between(getDateOfBith(), LocalDateTime.now());
+ }
+
+ public static Student student(final String name, final LocalDate dateOfBith, final String group) {
+ return new Student(name, dateOfBith, group);
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder result = new StringBuilder().append("Student{");
+ if (group != null) {
+ result.append("group='").append(group).append('\'');
+ }
+ if (name != null) {
+ result.append(", name=").append(name);
+ }
+ if (dateOfBith != null) {
+ result.append(", age=").append(dateOfBith);
+ }
+ result.append("}\n");
+ return result.toString();
+ }
+ }
+
+ public static class Group {
+ private final String group;
+ private final String mentor;
+
+ public Group(final String gottenGroup, final String gottenMentor) {
+ group = gottenGroup;
+ mentor = gottenMentor;
+ }
+
+ public final String getGroup() {
+ return group;
+ }
+
+ public final String getMentor() {
+ return mentor;
+ }
+
+ public static Group group(final String ggroup, final String mmentor) {
+ return new Group(ggroup, mmentor);
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder result = new StringBuilder().append("Student{");
+ if (group != null) {
+ result.append("group='").append(group).append('\'');
+ }
+ if (mentor != null) {
+ result.append(", name=").append(mentor);
+ }
+ result.append("}\n");
+ return result.toString();
+ }
+ }
+
+
+ public static class Statistics {
+
+ private final String group;
+ private final Integer count;
+ private final Double age;
+
+ public final String getGroup() {
+ return group;
+ }
+
+ public final Integer getCount() {
+ return count;
+ }
+
+ public final Double getAge() {
+ return age;
+ }
+
+ public Statistics(final String gottenGroup, final Integer gottenCount) {
+ group = gottenGroup;
+ count = gottenCount;
+ age = null;
+ }
+
+ public Statistics(final String gottenGroup, final Integer gottenCount, final Double gottenAge) {
+ group = gottenGroup;
+ count = gottenCount;
+ age = gottenAge;
+ }
+
+ public Statistics(final String gottenGroup) {
+ group = gottenGroup;
+ count = null;
+ age = null;
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder result = new StringBuilder().append("Statistics{");
+ if (group != null) {
+ result.append("group='").append(group).append('\'');
+ }
+ if (count != null) {
+ result.append(", count=").append(count);
+ }
+ if (age != null) {
+ result.append(", age=").append(age);
+ }
+ result.append("}\n");
+ return result.toString();
+ }
+ }
+
+}
diff --git a/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Conditions.java b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Conditions.java
new file mode 100644
index 0000000..53b0b54
--- /dev/null
+++ b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Conditions.java
@@ -0,0 +1,14 @@
+package ru.mipt.diht.students.elinrin.collectionquery;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class Conditions {
+ public static Predicate rlike(final Function expression, final String regexp) {
+ return element -> expression.apply(element).matches(regexp);
+ }
+
+ public static Predicate like(final Function expression, final String pattern) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/OrderByConditions.java b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/OrderByConditions.java
new file mode 100644
index 0000000..0b61963
--- /dev/null
+++ b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/OrderByConditions.java
@@ -0,0 +1,16 @@
+package ru.mipt.diht.students.elinrin.collectionquery;
+
+import java.util.Comparator;
+import java.util.function.Function;
+
+public final class OrderByConditions {
+
+ public static > Comparator asc(final Function expression) {
+ return (o1, o2) -> expression.apply(o1).compareTo(expression.apply(o2));
+ }
+
+ public static > Comparator desc(final Function expression) {
+ return (o1, o2) -> expression.apply(o2).compareTo(expression.apply(o1));
+ }
+
+}
diff --git a/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Sources.java b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Sources.java
new file mode 100644
index 0000000..9f740d9
--- /dev/null
+++ b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/Sources.java
@@ -0,0 +1,12 @@
+package ru.mipt.diht.students.elinrin.collectionquery;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class Sources {
+
+ @SafeVarargs
+ public static List list(final T... items) {
+ return Arrays.asList(items);
+ }
+}
diff --git a/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Aggregator.java b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Aggregator.java
new file mode 100644
index 0000000..c5fc669
--- /dev/null
+++ b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Aggregator.java
@@ -0,0 +1,9 @@
+package ru.mipt.diht.students.elinrin.collectionquery.aggregatesImpl;
+
+import java.util.List;
+import java.util.function.Function;
+
+
+public interface Aggregator extends Function {
+ C apply(List elements);
+}
diff --git a/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Avg.java b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Avg.java
new file mode 100644
index 0000000..d5d63ca
--- /dev/null
+++ b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Avg.java
@@ -0,0 +1,26 @@
+package ru.mipt.diht.students.elinrin.collectionquery.aggregatesImpl;
+
+import java.util.List;
+import java.util.function.Function;
+
+public class Avg implements Aggregator {
+
+ private Function function;
+ public Avg(final Function expression) {
+ function = expression;
+ }
+
+ @Override
+ public final Double apply(final List elements) {
+ Double result = 0.0;
+ for (T element : elements) {
+ result += (Double) function.apply(element);
+ }
+ return result / elements.size();
+ }
+
+ @Override
+ public final Double apply(final T t) {
+ return null;
+ }
+}
diff --git a/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Count.java b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Count.java
new file mode 100644
index 0000000..1053414
--- /dev/null
+++ b/projects/elinrin/src/main/java/ru/mipt/diht/students/elinrin/collectionquery/aggregatesImpl/Count.java
@@ -0,0 +1,29 @@
+package ru.mipt.diht.students.elinrin.collectionquery.aggregatesImpl;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+
+public class Count implements Aggregator {
+
+ private Function function;
+ public Count(final Function expression) {
+ function = expression;
+ }
+
+ @Override
+ public final Integer apply(final List elements) {
+ Set