diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e43b0f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/hw3_Trie/.idea/compiler.xml b/hw3_Trie/.idea/compiler.xml
new file mode 100644
index 0000000..4d5c80c
--- /dev/null
+++ b/hw3_Trie/.idea/compiler.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw3_Trie/.idea/gradle.xml b/hw3_Trie/.idea/gradle.xml
new file mode 100644
index 0000000..a5c3ae1
--- /dev/null
+++ b/hw3_Trie/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw3_Trie/.idea/misc.xml b/hw3_Trie/.idea/misc.xml
new file mode 100644
index 0000000..e208459
--- /dev/null
+++ b/hw3_Trie/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw3_Trie/.idea/modules.xml b/hw3_Trie/.idea/modules.xml
new file mode 100644
index 0000000..6e5f594
--- /dev/null
+++ b/hw3_Trie/.idea/modules.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw3_Trie/.idea/modules/bor.iml b/hw3_Trie/.idea/modules/bor.iml
new file mode 100644
index 0000000..51b3933
--- /dev/null
+++ b/hw3_Trie/.idea/modules/bor.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw3_Trie/.idea/modules/bor_main.iml b/hw3_Trie/.idea/modules/bor_main.iml
new file mode 100644
index 0000000..cb10f7b
--- /dev/null
+++ b/hw3_Trie/.idea/modules/bor_main.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw3_Trie/.idea/modules/bor_test.iml b/hw3_Trie/.idea/modules/bor_test.iml
new file mode 100644
index 0000000..f59354e
--- /dev/null
+++ b/hw3_Trie/.idea/modules/bor_test.iml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hw3_Trie/build.gradle b/hw3_Trie/build.gradle
new file mode 100644
index 0000000..7eb3763
--- /dev/null
+++ b/hw3_Trie/build.gradle
@@ -0,0 +1,14 @@
+group 'ru.spbau'
+version '1.0-SNAPSHOT'
+
+apply plugin: 'java'
+
+sourceCompatibility = 1.8
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ testCompile group: 'junit', name: 'junit', version: '4.12'
+}
diff --git a/hw3_Trie/gradle/wrapper/gradle-wrapper.jar b/hw3_Trie/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5d7d246
Binary files /dev/null and b/hw3_Trie/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/hw3_Trie/gradle/wrapper/gradle-wrapper.properties b/hw3_Trie/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4d36e15
--- /dev/null
+++ b/hw3_Trie/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Oct 02 23:10:25 MSK 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-rc-2-bin.zip
diff --git a/hw3_Trie/settings.gradle b/hw3_Trie/settings.gradle
new file mode 100644
index 0000000..7a2e130
--- /dev/null
+++ b/hw3_Trie/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'bor'
+
diff --git a/hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java b/hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java
new file mode 100644
index 0000000..3bf4cf9
--- /dev/null
+++ b/hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java
@@ -0,0 +1,245 @@
+package ru.spbau.solikov.src;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of data structure "trie" that can store strings over some finite alphabet
+ */
+public class Trie implements Serializable {
+
+ private static final int ALPHABET_SIZE = 26;
+
+ /**
+ * Function to obtain the letter in String from integer
+ *
+ * @param i number of letter
+ * @return string
+ */
+ private static String getCharForNumber(int i) {
+ return i > 0 && i < 27 ? String.valueOf((char) (i + 64)) : null;
+ }
+
+ private static final Map alphabet;
+
+ static {
+ alphabet = new HashMap();
+ for (int i = 0; i < ALPHABET_SIZE; i++) {
+ alphabet.put(getCharForNumber(i + 1), i);
+ }
+ }
+
+ /**
+ * Implementation of trie's node with static array
+ */
+ private class TrieNode implements Serializable {
+ private boolean isTerminal = false;
+ private int size = 0;
+ private TrieNode nodes[] = new TrieNode[ALPHABET_SIZE];
+ }
+
+ private TrieNode root;
+
+ public Trie() {
+ root = new TrieNode();
+ }
+
+ /**
+ * Function that adds string from letters of alphabet to the trie.
+ * Returns true if the trie didn't have that string before.
+ * False otherwise.
+ * Supports size.
+ *
+ * @param element string to be added
+ * @return true if element already in trie, false otherwise
+ */
+ public boolean add(String element) {
+
+ TrieNode current = root;
+ int index = -1;
+ int indexLastElement = 0;
+
+ for (int i = 0; i < element.length(); i++) {
+ indexLastElement = i;
+ char c = element.charAt(i);
+ index = alphabet.get(Character.toString(c));
+ if (current.nodes[index] != null) {
+ current = current.nodes[index];
+ } else {
+ current.nodes[index] = new TrieNode();
+ current = current.nodes[index];
+ }
+ }
+
+ if (current.isTerminal) {
+ return false;
+ }
+
+ current.isTerminal = true;
+
+
+ current.size++;
+ current = root;
+ for (int i = 0; i <= indexLastElement; i++) {
+ char c = element.charAt(i);
+ int indexCurrent = alphabet.get(Character.toString(c));
+ current.size++;
+ current = current.nodes[indexCurrent];
+ }
+ return true;
+
+ }
+
+ /**
+ * Checks if element is in trie.
+ *
+ * @param element to be checked
+ * @return true if element is in trie, false otherwise
+ */
+ public boolean contains(String element) {
+
+ TrieNode current = root;
+
+ for (int i = 0; i < element.length(); i++) {
+ char c = element.charAt(i);
+ int index = alphabet.get(Character.toString(c));
+ if (current.nodes[index] != null) {
+ current = current.nodes[index];
+ } else {
+ return false;
+ }
+ }
+
+ return current.isTerminal;
+ }
+
+ /**
+ * Removes element from trie. If that element is not prefix of any other element in trie,
+ * then deletes all nodes till last terminal vertex from up to down.
+ *
+ * @param element to be removed
+ * @return true if element was in trie, false otherwise
+ */
+ public boolean remove(String element) {
+
+ TrieNode current = root;
+ TrieNode lastElement = root;
+ int indexLastElement = 0;
+ int index = -1;
+
+ for (int i = 0; i < element.length(); i++) {
+ char c = element.charAt(i);
+ index = alphabet.get(Character.toString(c));
+
+ if (current.isTerminal) {
+ lastElement = current;
+ indexLastElement = i;
+ }
+
+ if (current.nodes[index] != null) {
+ current = current.nodes[index];
+ } else {
+ return false;
+ }
+ }
+
+ if (current.isTerminal) {
+ for (int i = 0; i < ALPHABET_SIZE; i++) {
+ if (current.nodes[i] != null) {
+ current.isTerminal = false;
+ current = root;
+ for (int j = 0; j <= indexLastElement; j++) {
+ char c = element.charAt(j);
+ int indexCurrent = alphabet.get(Character.toString(c));
+ current.size--;
+ current = current.nodes[indexCurrent];
+ }
+ return true;
+ }
+ }
+
+ removePath(lastElement, element.substring(indexLastElement));
+
+ current = root;
+ for (int i = 0; i <= indexLastElement; i++) {
+ char c = element.charAt(i);
+ int indexCurrent = alphabet.get(Character.toString(c));
+ current.size--;
+ current = current.nodes[indexCurrent];
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void removePath(TrieNode node, String element) {
+
+ if (element.length() == 0) {
+ return;
+ }
+
+ removePath(node.nodes[alphabet.get(Character.toString(element.charAt(0)))], element.substring(1));
+ node.nodes[alphabet.get(Character.toString(element.charAt(0)))] = null;
+
+ }
+
+ /**
+ * Returns number of strings stored in trie. Is equal to number of terminal vertices.
+ *
+ * @return size
+ */
+ public int size() {
+ return (root != null) ? root.size : 0;
+ }
+
+ /**
+ * Returns number of strings started with prefix stored in trie.
+ * Is equal to number of terminal vertices in subtrie of last prefix letter's node.
+ *
+ * @param prefix to be checked for size
+ * @return size
+ */
+ public int howManyStartsWithPrefix(String prefix) {
+
+ TrieNode current = root;
+
+ for (int i = 0; i < prefix.length(); i++) {
+ char c = prefix.charAt(i);
+ int index = alphabet.get(Character.toString(c));
+ if (current.nodes[index] != null) {
+ current = current.nodes[index];
+ } else {
+ return 0;
+ }
+ }
+
+ return current.size;
+ }
+
+ /**
+ * Takes output stream and serializes class Trie to that stream with standard Java method
+ *
+ * @param out stream in what it will write Trie
+ * @throws IOException exceptions that could appear while writing
+ */
+ public void serialize(OutputStream out) throws IOException {
+ ObjectOutputStream oos = new ObjectOutputStream(out);
+ oos.writeObject(this);
+ oos.flush();
+ oos.close();
+ }
+
+ /**
+ * Takes input stream and reads class from that stream with standard Java method
+ *
+ * @param in stream from what it will read information about Trie
+ * @throws IOException exceptions that could appear while reading
+ * @throws ClassNotFoundException exception if there's no such file
+ */
+ public void deserialize(InputStream in) throws IOException, ClassNotFoundException {
+ ObjectInputStream oin = new ObjectInputStream(in);
+ Trie other = (Trie) oin.readObject();
+ this.root = other.root;
+ }
+}
diff --git a/hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java b/hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java
new file mode 100644
index 0000000..cf00f5e
--- /dev/null
+++ b/hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java
@@ -0,0 +1,238 @@
+package ru.spbau.solikov.test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import ru.spbau.solikov.src.Trie;
+
+import static org.junit.Assert.*;
+
+import java.io.*;
+import java.nio.file.*;
+
+/**
+ * Class for testing methods from "Trie" class. Creates a file for testing serialize and deserialize and deletes it after tests.
+ */
+public class Main {
+
+ private Trie trie;
+
+ @Before
+ public void setUp() {
+ trie = new Trie();
+ }
+
+ @Test
+ public void testIfAddOneElement() {
+ assertEquals(true, trie.add("A"));
+ }
+
+ @Test
+ public void testIfAddOneSizeReturnsOne() {
+ trie.add("A");
+ assertEquals(1, trie.size());
+ }
+
+ @Test
+ public void testContainsAfterAddOne() {
+ trie.add("ASDFGTYMVNCXMKENRBVEK");
+ assertEquals(true, trie.contains("ASDFGTYMVNCXMKENRBVEK"));
+ }
+
+ @Test
+ public void testAddTheSameOneElement() {
+ trie.add("A");
+ assertEquals(false, trie.add("A"));
+ }
+
+ @Test
+ public void testSizeAfterAddingTheSameOneElement() {
+ trie.add("A");
+ trie.add("A");
+ assertEquals(1, trie.size());
+ }
+
+ @Test
+ public void testAddLotsElements() {
+ trie.add("A");
+ assertEquals(true, trie.add("B"));
+ }
+
+ @Test
+ public void testSizeAfterAddLotsElements() {
+ trie.add("A");
+ trie.add("B");
+ trie.add("SPBAU");
+ assertEquals(3, trie.size());
+ }
+
+ @Test
+ public void testContainsAfterAddLotsElements() {
+ trie.add("A");
+ trie.add("B");
+ trie.add("SPBAU");
+ assertEquals(true, trie.contains("SPBAU"));
+ }
+
+ @Test
+ public void testDoesNotContainAfterAddLotsElements() {
+ trie.add("A");
+ trie.add("B");
+ trie.add("SPBAU");
+ assertEquals(false, trie.contains("SPBSU"));
+ }
+
+ @Test
+ public void testRemoveFromOneElementTrie() {
+ trie.add("A");
+ assertEquals(true, trie.remove("A"));
+ }
+
+ @Test
+ public void testFalseRemoveFromOneElementTrie() {
+ trie.add("A");
+ assertEquals(false, trie.remove("ABBA"));
+ }
+
+ @Test
+ public void testRemoveFromLotsElementTrie() {
+ for (int i = 1; i < 11; i++) {
+ trie.add(String.valueOf((char) (i + 64)));
+ }
+ for (int i = 1; i < 11; i++) {
+ assertEquals(true, (trie.remove(String.valueOf((char) (i + 64)))));
+ }
+ }
+
+ @Test
+ public void testSizeAfterLotsOfAdds() {
+ for (int i = 1; i < 11; i++) {
+ trie.add(String.valueOf((char) (i + 64)));
+ }
+ assertEquals(10, trie.size());
+ }
+
+ @Test
+ public void testSizeAfterRemovingFromLotsElementTrie() {
+ for (int i = 1; i < 11; i++) {
+ trie.add(String.valueOf((char) (i + 64)));
+ }
+ trie.remove("B");
+ assertEquals(9, trie.size());
+ }
+
+ @Test
+ public void testSizeOfZeroTrie() {
+ assertEquals(0, trie.size());
+ }
+
+ @Test
+ public void testHowManyStartsWithPrefixOfZeroTable() {
+ assertEquals(0, trie.howManyStartsWithPrefix(""));
+ }
+
+ @Test
+ public void testHowManyStartsWithPrefixOfOneElementTable() {
+ trie.add("A");
+ assertEquals(1, trie.howManyStartsWithPrefix("A"));
+ }
+
+ @Test
+ public void testFalseHowManyStartsWithPrefixOfOneElementTable() {
+ trie.add("A");
+ assertEquals(0, trie.howManyStartsWithPrefix("B"));
+ }
+
+ @Test
+ public void testHowManyStartsWithPrefixOfLotsElementTable() {
+ for (int i = 1; i < 11; i++) {
+ trie.add(String.valueOf((char) (i + 64)));
+ }
+ assertEquals(1, trie.howManyStartsWithPrefix("A"));
+ }
+
+ @Test
+ public void testManyStartsWithPrefix() {
+ trie.add("A");
+ trie.add("AB");
+ trie.add("BA");
+ trie.add("ABC");
+ trie.add("ABCD");
+ trie.add("ABCDE");
+ assertEquals(5, trie.howManyStartsWithPrefix("A"));
+ }
+
+ @Test
+ public void testSerializeEmpty() throws IOException {
+ FileOutputStream fos = new FileOutputStream("test.trie");
+ trie.serialize(fos);
+ }
+
+ @Test
+ public void testDeserializeEmpty() throws IOException, ClassNotFoundException {
+ FileOutputStream fos = new FileOutputStream("test.trie");
+ FileInputStream fis = new FileInputStream("test.trie");
+ trie.serialize(fos);
+ trie.deserialize(fis);
+ }
+
+ @Test
+ public void testSerializeLotsElements() throws IOException {
+ FileOutputStream fos = new FileOutputStream("test.trie");
+ for (int i = 1; i < 11; i++) {
+ trie.add(String.valueOf((char) (i + 64)));
+ }
+ trie.serialize(fos);
+ }
+
+ @Test
+ public void testDeserializeLotsElements() throws IOException, ClassNotFoundException {
+ FileOutputStream fos = new FileOutputStream("test.trie");
+ FileInputStream fis = new FileInputStream("test.trie");
+ for (int i = 1; i < 11; i++) {
+ trie.add(String.valueOf((char) (i + 64)));
+ }
+ trie.serialize(fos);
+ trie.deserialize(fis);
+ }
+
+ @Test
+ public void testSizeAfterDeserializeLotsElements() throws IOException, ClassNotFoundException {
+ FileOutputStream fos = new FileOutputStream("test.trie");
+ FileInputStream fis = new FileInputStream("test.trie");
+ for (int i = 1; i < 11; i++) {
+ trie.add(String.valueOf((char) (i + 64)));
+ }
+ trie.serialize(fos);
+ Trie other = new Trie();
+ other.deserialize(fis);
+ assertEquals(true, other.size() == trie.size());
+ }
+
+ @Test
+ public void testContaintsAfterDeserializeLotsElements() throws IOException, ClassNotFoundException {
+ FileOutputStream fos = new FileOutputStream("test.trie");
+ FileInputStream fis = new FileInputStream("test.trie");
+ for (int i = 1; i < 11; i++) {
+ trie.add(String.valueOf((char) (i + 64)));
+ }
+ trie.serialize(fos);
+ Trie other = new Trie();
+ other.deserialize(fis);
+ assertEquals(true, other.contains("A"));
+ }
+
+ @After
+ public void rmDir() {
+ try {
+ Path path = FileSystems.getDefault().getPath("test.trie");
+ Files.delete(path);
+ } catch (NoSuchFileException ignored) {
+ } catch (DirectoryNotEmptyException x) {
+ System.err.format("%s not empty%n", "test.trie");
+ } catch (IOException x) {
+ System.err.println(x);
+ }
+ }
+
+}