diff --git a/Function/.gitignore b/Function/.gitignore
new file mode 100644
index 0000000..345e61a
--- /dev/null
+++ b/Function/.gitignore
@@ -0,0 +1,49 @@
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/dictionaries
+
+# Sensitive or high-churn files:
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.xml
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+
+# Gradle:
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# CMake
+cmake-build-debug/
+
+# Mongo Explorer plugin:
+.idea/**/mongoSettings.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
diff --git a/Function/.idea/.name b/Function/.idea/.name
new file mode 100644
index 0000000..1ff17c5
--- /dev/null
+++ b/Function/.idea/.name
@@ -0,0 +1 @@
+Function
\ No newline at end of file
diff --git a/Function/.idea/compiler.xml b/Function/.idea/compiler.xml
new file mode 100644
index 0000000..8f81c05
--- /dev/null
+++ b/Function/.idea/compiler.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Function/.idea/misc.xml b/Function/.idea/misc.xml
new file mode 100644
index 0000000..c092288
--- /dev/null
+++ b/Function/.idea/misc.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Function/.idea/modules.xml b/Function/.idea/modules.xml
new file mode 100644
index 0000000..8962661
--- /dev/null
+++ b/Function/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Function/Function.iml b/Function/Function.iml
new file mode 100644
index 0000000..e6ccb8a
--- /dev/null
+++ b/Function/Function.iml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Function/pom.xml b/Function/pom.xml
new file mode 100644
index 0000000..b49ba25
--- /dev/null
+++ b/Function/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ ru.spbau.mit.kazakov
+ Function
+ 1.0-SNAPSHOT
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.3.2
+
+ 1.8
+ 1.8
+
+
+
+
+
+
+
+ junit
+ junit
+ 4.8
+
+
+ org.jetbrains
+ annotations
+ 13.0
+
+
+ com.google.guava
+ guava
+ 23.2-jre
+
+
+
+
+ UTF-8
+ UTF-8
+
+
+
\ No newline at end of file
diff --git a/Function/src/main/java/ru/spbau/mit/kazakov/Function/Collections.java b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Collections.java
new file mode 100644
index 0000000..f4ecfc1
--- /dev/null
+++ b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Collections.java
@@ -0,0 +1,126 @@
+package ru.spbau.mit.kazakov.Function;
+
+
+import com.google.common.collect.Lists;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * Class providing methods for working with collections using functions.
+ */
+public class Collections {
+ /**
+ * Applies specified function to specified container's elements and adds mapped elements in new list.
+ *
+ * @param function specified function
+ * @param elements specified container
+ * @param type of elements in specified container
+ * @param type of elements in result list
+ * @return result list
+ */
+ static public List map(@NotNull final Function1 super T, ? extends U> function, @NotNull final Iterable elements) {
+ List resultList = new ArrayList<>();
+
+ for (T element : elements) {
+ resultList.add(function.apply(element));
+ }
+
+ return resultList;
+ }
+
+ /**
+ * Creates new list composed of elements from specified container that satisfy specified predicate.
+ *
+ * @param predicate specified predicate
+ * @param elements specified container
+ * @param type of elements in specified container
+ * @return result list
+ */
+ static public List filter(@NotNull final Predicate super T> predicate, @NotNull final Iterable elements) {
+ List resultList = new ArrayList<>();
+
+ for (T element : elements) {
+ if (predicate.apply(element)) {
+ resultList.add(element);
+ }
+ }
+
+ return resultList;
+ }
+
+ /**
+ * Creates new list by adding elements from specified container while they satisfy specified predicate.
+ *
+ * @param predicate specified predicate
+ * @param elements specified container
+ * @param type of elements in specified container
+ * @return result list
+ */
+ static public List takeWhile(@NotNull final Predicate super T> predicate, @NotNull final Iterable elements) {
+ List resultList = new ArrayList<>();
+
+ for (T element : elements) {
+ if (!predicate.apply(element)) {
+ break;
+ }
+ resultList.add(element);
+ }
+
+ return resultList;
+ }
+
+ /**
+ * Creates new list by adding elements from specified container unless they satisfy specified predicate.
+ *
+ * @param predicate specified predicate
+ * @param elements specified container
+ * @param type of elements in specified container
+ * @return result list
+ */
+ static public List takeUnless(@NotNull final Predicate super T> predicate, @NotNull final Iterable elements) {
+ return takeWhile(predicate.not(), elements);
+ }
+
+ /**
+ * Left-associative fold of specified collection. Reduces the list using specified function from left to right.
+ *
+ * @param function specified function
+ * @param startValue first value for function's first element
+ * @param elements specified collection
+ * @param type of elements in specified collection
+ * @param specified function's codomain
+ * @return result value
+ */
+ static public U foldl(@NotNull final Function2 super U, ? super T, ? extends U> function,
+ U startValue, @NotNull final Collection elements) {
+ U resultValue = startValue;
+
+ for (T element : elements) {
+ resultValue = function.apply(resultValue, element);
+ }
+
+ return resultValue;
+ }
+
+ /**
+ * Right-associative fold of specified collection. Reduces the list using specified function from right to left.
+ *
+ * @param function specified function
+ * @param startValue first value for function's second element
+ * @param elements specified collection
+ * @param type of elements in specified collection
+ * @param specified function's codomain
+ * @return result value
+ */
+ static public U foldr(@NotNull final Function2 super T, ? super U, ? extends U> function,
+ U startValue, @NotNull final Collection elements) {
+ U resultValue = startValue;
+
+ for (T element : Lists.reverse(new ArrayList<>(elements))) {
+ resultValue = function.apply(element, resultValue);
+ }
+
+ return resultValue;
+ }
+}
diff --git a/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function1.java b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function1.java
new file mode 100644
index 0000000..9bbfe30
--- /dev/null
+++ b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function1.java
@@ -0,0 +1,32 @@
+package ru.spbau.mit.kazakov.Function;
+
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The interface for representing a function with one argument.
+ *
+ * @param domain's type
+ * @param codomain's type
+ */
+@FunctionalInterface
+public interface Function1 {
+ /**
+ * Applies function to specified argument.
+ *
+ * @param argument for applying to
+ * @return mapped value
+ */
+ U apply(T argument);
+
+ /**
+ * Creates composition of functions.
+ *
+ * @param function for composition
+ * @param specified function's codomain's type
+ * @return result of function composition
+ */
+ default Function1 compose(@NotNull final Function1 super U, ? extends V> function) {
+ return argument -> function.apply(apply(argument));
+ }
+}
diff --git a/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function2.java b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function2.java
new file mode 100644
index 0000000..3532a7b
--- /dev/null
+++ b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Function2.java
@@ -0,0 +1,62 @@
+package ru.spbau.mit.kazakov.Function;
+
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The interface for representing a function with two arguments.
+ *
+ * @param first argument's type
+ * @param second argument's type
+ * @param codomain's type
+ */
+@FunctionalInterface
+public interface Function2 {
+ /**
+ * Applies function to specified argument.
+ *
+ * @param firstArgument for applying to
+ * @param secondArgument for applying to
+ * @return mapped value
+ */
+ K apply(T firstArgument, U secondArgument);
+
+ /**
+ * Creates composition of functions.
+ *
+ * @param specified function's codomain's type
+ * @return result of function composition
+ */
+ default Function2 compose(@NotNull final Function1 super K, ? extends R> function) {
+ return (firstArgument, secondArgument) -> function.apply(apply(firstArgument, secondArgument));
+ }
+
+ /**
+ * Binds first argument to specified value.
+ *
+ * @param firstArgument specified value
+ * @return function of one argument
+ */
+ default Function1 bind1(T firstArgument) {
+ return secondArgument -> apply(firstArgument, secondArgument);
+ }
+
+ /**
+ * Binds second argument to specified value.
+ *
+ * @param secondArgument specified value
+ * @return function of one argument
+ */
+ default Function1 bind2(U secondArgument) {
+ return firstArgument -> apply(firstArgument, secondArgument);
+ }
+
+ /**
+ * Converts current function with two arguments into two functions with one argument.
+ *
+ * @return new function with one argument
+ */
+ default Function1> curry() {
+ return this::bind1;
+ }
+}
\ No newline at end of file
diff --git a/Function/src/main/java/ru/spbau/mit/kazakov/Function/Predicate.java b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Predicate.java
new file mode 100644
index 0000000..2a4c177
--- /dev/null
+++ b/Function/src/main/java/ru/spbau/mit/kazakov/Function/Predicate.java
@@ -0,0 +1,53 @@
+package ru.spbau.mit.kazakov.Function;
+
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The interface for representing a predicate with one argument.
+ *
+ * @param domain's type
+ */
+@FunctionalInterface
+public interface Predicate extends Function1 {
+ /**
+ * Predicate always returning true.
+ */
+ Predicate ALWAYS_TRUE = argument -> true;
+
+ /**
+ * Predicate always returning false.
+ */
+ Predicate ALWAYS_FALSE = ALWAYS_TRUE.not();
+
+ /**
+ * Makes new predicate with two argument composed of logical OR of current and specified predicates.
+ *
+ * @param predicate a specified predicate
+ * @param specified predicate's argument's type
+ * @return new predicate with two variables
+ */
+ default Function2 or(@NotNull final Predicate predicate) {
+ return (firstArgument, secondArgument) -> Predicate.this.apply(firstArgument) || predicate.apply(secondArgument);
+ }
+
+ /**
+ * Makes new predicate with two argument composed of logical AND of current and specified predicates.
+ *
+ * @param predicate a specified predicate
+ * @param specified predicate's argument's type
+ * @return new predicate with two variables
+ */
+ default Function2 and(@NotNull final Predicate predicate) {
+ return (firstArgument, secondArgument) -> Predicate.this.apply(firstArgument) && predicate.apply(secondArgument);
+ }
+
+ /**
+ * Makes new predicate composed of logical NOT of current predicate.
+ *
+ * @return new predicate
+ */
+ default Predicate not() {
+ return argument -> !apply(argument);
+ }
+}
diff --git a/Function/src/test/java/ru/spbau/mit/kazakov/Function/CollectionsTest.java b/Function/src/test/java/ru/spbau/mit/kazakov/Function/CollectionsTest.java
new file mode 100644
index 0000000..8a9a311
--- /dev/null
+++ b/Function/src/test/java/ru/spbau/mit/kazakov/Function/CollectionsTest.java
@@ -0,0 +1,167 @@
+package ru.spbau.mit.kazakov.Function;
+
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.*;
+
+public class CollectionsTest {
+ @Test
+ public void testMapEmpty() {
+ Function1 function = argument -> argument * argument;
+ List list = new ArrayList<>();
+ List expected = new ArrayList<>();
+
+ assertEquals(expected, Collections.map(function, list));
+ }
+
+ @Test
+ public void testMap() {
+ Function1 function = argument -> argument * argument;
+ List list = asList(-2, -1, 0, 4, 10);
+ List expected = asList(4, 1, 0, 16, 100);
+
+ assertEquals(expected, Collections.map(function, list));
+ }
+
+ @Test
+ public void testFilterEmptyInput() {
+ Predicate function = argument -> Math.abs(argument) > 10;
+ List list = new ArrayList<>();
+ List expected = new ArrayList<>();
+
+ assertEquals(expected, Collections.filter(function, list));
+ }
+
+ @Test
+ public void testFilterEmptyOutput() {
+ Predicate function = argument -> Math.abs(argument) == 1;
+ List list = asList(10, 3, -10, -99, 0, -23, 1001);
+ List expected = new ArrayList<>();
+
+ assertEquals(expected, Collections.filter(function, list));
+ }
+
+ @Test
+ public void testFilterAllListOutput() {
+ Predicate function = argument -> Math.abs(argument) != 1010;
+ List list = asList(10, 3, -10, -99, 0, -23, 1001);
+ List expected = asList(10, 3, -10, -99, 0, -23, 1001);
+
+ assertEquals(expected, Collections.filter(function, list));
+ }
+
+ @Test
+ public void testFilter() {
+ Predicate function = argument -> Math.abs(argument) > 10;
+ List list = asList(10, 3, -10, -99, 0, -23, 1001);
+ List expected = asList(-99, -23, 1001);
+
+ assertEquals(expected, Collections.filter(function, list));
+ }
+
+ @Test
+ public void testTakeWhileEmptyInput() {
+ Predicate function = argument -> Math.abs(argument) == 77;
+ List list = new ArrayList<>();
+ List expected = new ArrayList<>();
+
+ assertEquals(expected, Collections.takeWhile(function, list));
+ }
+
+ @Test
+ public void testTakeWhileEmptyOutput() {
+ Predicate function = argument -> Math.abs(argument) == 77;
+ List list = asList(1, -77, 77, 77, 0, 77, -77, 198);
+ List expected = new ArrayList<>();
+
+ assertEquals(expected, Collections.takeWhile(function, list));
+ }
+
+ @Test
+ public void testTakeWhileAllListOutput() {
+ Predicate function = argument -> Math.abs(argument) == 77;
+ List list = asList(-77, 77, 77, 77, -77);
+ List expected = asList(-77, 77, 77, 77, -77);
+
+ assertEquals(expected, Collections.takeWhile(function, list));
+ }
+
+ @Test
+ public void testTakeWhile() {
+ Predicate function = argument -> Math.abs(argument) == 77;
+ List list = asList(-77, 77, 77, 0, 77, -77, 198);
+ List expected = asList(-77, 77, 77);
+
+ assertEquals(expected, Collections.takeWhile(function, list));
+ }
+
+ @Test
+ public void testTakeUnlessEmptyInput() {
+ Predicate function = argument -> Math.abs(argument) == 77;
+ List list = new ArrayList<>();
+ List expected = new ArrayList<>();
+
+ assertEquals(expected, Collections.takeUnless(function, list));
+ }
+
+ @Test
+ public void testTakeUnlessEmptyOutput() {
+ Predicate function = argument -> Math.abs(argument) == 77;
+ List list = asList(-77, 77, 77, 0, 77, -77, 198);
+ List expected = new ArrayList<>();
+
+ assertEquals(expected, Collections.takeUnless(function, list));
+ }
+
+ @Test
+ public void testTakeUnlessAllListOutput() {
+ Predicate function = argument -> Math.abs(argument) == 77;
+ List list = asList(0, 1, -99, 121, 89898, 32423);
+ List expected = asList(0, 1, -99, 121, 89898, 32423);
+
+ assertEquals(expected, Collections.takeUnless(function, list));
+ }
+
+ @Test
+ public void testTakeUnless() {
+ Predicate function = argument -> Math.abs(argument) == 77;
+ List list = asList(0, 1, -99, 121, -77, 89898, 77, 32423);
+ List expected = asList(0, 1, -99, 121);
+
+ assertEquals(expected, Collections.takeUnless(function, list));
+ }
+
+ @Test
+ public void testFoldlEmpty() {
+ Function2 function = (firstArgument, secondArgument) -> secondArgument / firstArgument;
+ List list = new ArrayList<>();
+ assertEquals(1, (int) Collections.foldl(function, 1, list));
+ }
+
+ @Test
+ public void testFoldl() {
+ Function2 function = (firstArgument, secondArgument) -> secondArgument / firstArgument;
+ List list = asList(10, 20, 100);
+ assertEquals(50, (int) Collections.foldl(function, 1, list));
+ }
+
+ @Test
+ public void testFoldrEmpty() {
+ Function2 function = (firstArgument, secondArgument) -> firstArgument / secondArgument;
+ List list = new ArrayList<>();
+ assertEquals(1, (int) Collections.foldr(function, 1, list));
+ }
+
+ @Test
+ public void testFoldr() {
+ Function2 function = (firstArgument, secondArgument) -> firstArgument / secondArgument;
+ List list = asList(100, 20, 10);
+ assertEquals(50, (int) Collections.foldr(function, 1, list));
+ }
+}
\ No newline at end of file
diff --git a/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function1Test.java b/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function1Test.java
new file mode 100644
index 0000000..3862672
--- /dev/null
+++ b/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function1Test.java
@@ -0,0 +1,32 @@
+package ru.spbau.mit.kazakov.Function;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class Function1Test {
+ @Test
+ public void testConstructor() {
+ new Function1() {
+ @Override
+ public Double apply(Integer argument) {
+ return argument / 2.0;
+ }
+ };
+ }
+
+ @Test
+ public void testApply() {
+ Function1 function = argument -> argument * 2;
+ assertEquals(new Integer(10), function.apply(5));
+ }
+
+ @Test
+ public void compose() {
+ Function1 someFunction = argument -> (long) (argument / 5);
+ Function1 anotherFunction = Object::toString;
+ Function1 composition = someFunction.compose(anotherFunction);
+ assertEquals("6", composition.apply(30));
+ }
+
+}
\ No newline at end of file
diff --git a/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function2Test.java b/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function2Test.java
new file mode 100644
index 0000000..a3d9e30
--- /dev/null
+++ b/Function/src/test/java/ru/spbau/mit/kazakov/Function/Function2Test.java
@@ -0,0 +1,63 @@
+package ru.spbau.mit.kazakov.Function;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class Function2Test {
+ @Test
+ public void testConstructor() {
+ new Function2() {
+ @Override
+ public Double apply(Long firstArgument, Integer secondArgument) {
+ return (firstArgument * secondArgument) / 5.0;
+ }
+ };
+ }
+
+ @Test
+ public void testApply() {
+ Function2 function =
+ (firstArgument, secondArgument) -> firstArgument.toString() + secondArgument.toString();
+ assertEquals("8j", function.apply((byte) 8, 'j'));
+ }
+
+ @Test
+ public void testCompose() {
+ Function2 someFunction = Integer::equals;
+ Function1 anotherFunction = argument -> {
+ if (argument) {
+ return (byte) 1;
+ } else {
+ return (byte) 0;
+ }
+ };
+ Function2 composition = someFunction.compose(anotherFunction);
+ assertEquals(1, (byte) composition.apply(979, 979));
+ }
+
+ @Test
+ public void bind1() {
+ Function2 someFunction =
+ (firstArgument, secondArgument) -> (int) Math.pow(firstArgument, secondArgument);
+ Function1 anotherFunction = someFunction.bind1(2);
+ assertEquals(32, (int) anotherFunction.apply(5));
+ }
+
+ @Test
+ public void bind2() {
+ Function2 someFunction =
+ (firstArgument, secondArgument) -> (int) Math.pow(firstArgument, secondArgument);
+ Function1 anotherFunction = someFunction.bind2(3);
+ assertEquals(64, (int) anotherFunction.apply(4));
+ }
+
+ @Test
+ public void curry() {
+ Function2 function =
+ (firstArgument, secondArgument) -> Math.abs(firstArgument) + Math.abs(secondArgument);
+ Function1> curried = function.curry();
+ assertEquals(46, (int) curried.apply(-44).apply(2));
+ }
+
+}
\ No newline at end of file
diff --git a/Function/src/test/java/ru/spbau/mit/kazakov/Function/PredicateTest.java b/Function/src/test/java/ru/spbau/mit/kazakov/Function/PredicateTest.java
new file mode 100644
index 0000000..e354a30
--- /dev/null
+++ b/Function/src/test/java/ru/spbau/mit/kazakov/Function/PredicateTest.java
@@ -0,0 +1,61 @@
+package ru.spbau.mit.kazakov.Function;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class PredicateTest {
+ @Test
+ public void testConstructor() {
+ new Predicate(){
+ @Override
+ public Boolean apply(Double argument) {
+ return argument > 7;
+ }
+ };
+ }
+
+ @Test
+ public void testALWAYS_TRUE() {
+ assertTrue((boolean) Predicate.ALWAYS_TRUE.apply(new Object()));
+ }
+
+ @Test
+ public void testALWAYS_FALSE() {
+ assertFalse((boolean) Predicate.ALWAYS_FALSE.apply(new Object()));
+ }
+
+ @Test
+ public void testOr() {
+ Predicate somePredicate = argument -> argument;
+ Predicate anotherPredicate = argument -> argument >= 111;
+ Function2 or = somePredicate.or(anotherPredicate);
+
+ assertFalse(or.apply(false, 1));
+ assertTrue(or.apply(true, 110));
+ assertTrue(or.apply(false, 111));
+ assertTrue(or.apply(true, 1000));
+ }
+
+ @Test
+ public void testAnd() {
+ Predicate somePredicate = argument -> argument;
+ Predicate anotherPredicate = argument -> argument >= 111;
+ Function2 and = somePredicate.and(anotherPredicate);
+
+ assertFalse(and.apply(false, 1));
+ assertFalse(and.apply(true, 110));
+ assertFalse(and.apply(false, 111));
+ assertTrue(and.apply(true, 1000));
+ }
+
+ @Test
+ public void testNot() {
+ Predicate predicate = argument -> argument.equals("Math is painful");
+ Predicate not = predicate.not();
+
+ assertTrue(not.apply("Math is wonderful"));
+ assertFalse(not.apply("Math is painful"));
+ }
+
+}
\ No newline at end of file