diff --git a/hw_03/pom.xml b/hw_03/pom.xml
new file mode 100644
index 0000000..307b47f
--- /dev/null
+++ b/hw_03/pom.xml
@@ -0,0 +1,27 @@
+
+ 4.0.0
+
+ 1
+ hw_01
+ 0.0.1-SNAPSHOT
+ jar
+
+ hw_01
+ http://maven.apache.org
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+
diff --git a/hw_03/src/main/java/hw_01/Bor.java b/hw_03/src/main/java/hw_01/Bor.java
new file mode 100644
index 0000000..d5a4a3a
--- /dev/null
+++ b/hw_03/src/main/java/hw_01/Bor.java
@@ -0,0 +1,145 @@
+package hw_01;
+
+import java.io.*;
+
+public class Bor implements Trie, StreamSerializable {
+ final private Node bor;
+
+ public Bor() {
+ bor = new Node();
+ }
+
+ public boolean add(String element) {
+ if (contains(element)) {
+ return false;
+ }
+
+ Node p = bor;
+
+ p.nwords++;
+ for (int i = 0; i < element.length(); i++) {
+ int currIdx = arrayIndex(element.charAt(i));
+
+ if (p.lifes[currIdx] == null) {
+ p.lifes[currIdx] = new Node();
+ }
+ p = p.lifes[currIdx];
+ p.nwords++;
+ }
+
+ p.isTerminal = true;
+ return true;
+ }
+
+ public boolean contains(String element) {
+ Node p = bor;
+ for (int i = 0; i < element.length(); i++) {
+ int currIdx = arrayIndex(element.charAt(i));
+
+ if (p.lifes[currIdx] == null) {
+ return false;
+ }
+ p = p.lifes[currIdx];
+ }
+
+ return p.isTerminal;
+ }
+
+ public boolean remove(String element) {
+ if (!contains(element)) {
+ return false;
+ }
+
+ Node p = bor;
+ p.nwords--;
+ for (int i = 0; i < element.length(); i++) {
+ int currIdx = arrayIndex(element.charAt(i));
+
+ if (p.lifes[currIdx].nwords == 1) {
+ p.lifes[currIdx] = null;
+ return true;
+ }
+ p = p.lifes[currIdx];
+ p.nwords--;
+ }
+
+ p.isTerminal = false;
+
+ return true;
+ }
+
+ public int size() {
+ return bor.nwords;
+ }
+
+ public int howManyStartsWithPrefix(String prefix) {
+ Node p = bor;
+ for (int i = 0; i < prefix.length(); i++) {
+ int currIdx = arrayIndex(prefix.charAt(i));
+
+ if (p.lifes[currIdx] == null) {
+ return 0;
+ }
+ p = p.lifes[currIdx];
+ }
+
+ return p.nwords;
+ }
+
+ private int arrayIndex(char a) {
+ if ('a' <= a && a <= 'z') {
+ return a - 'a';
+ }
+
+ if ('A' <= a && a <= 'Z') {
+ return a - 'A' + 'z' - 'a' + 1;
+ }
+
+ return -1;
+ }
+
+ private class Node {
+ public boolean isTerminal;
+ public int nwords;
+ Node[] lifes = new Node[2*('z' - 'a' + 1)];
+ }
+
+ private void serializeNode(Node r, DataOutputStream dos) throws IOException {
+ dos.writeBoolean(r.isTerminal);
+ dos.writeInt(r.nwords);
+
+ for(int i = 0; i < r.lifes.length; i++) {
+ if (r.lifes[i] == null) {
+ dos.writeBoolean(false);
+ } else {
+ dos.writeBoolean(true);
+ serializeNode(r.lifes[i], dos);
+ }
+ }
+
+ }
+
+ private void deserializeNode(Node r, DataInputStream dis) throws IOException {
+ r.isTerminal = dis.readBoolean();
+ r.nwords = dis.readInt();
+
+ for(int i = 0; i < r.lifes.length; i++) {
+ boolean b = dis.readBoolean();
+ if (b) {
+ r.lifes[i] = new Node();
+ deserializeNode(r.lifes[i], dis);
+ }
+ }
+ }
+
+ @Override
+ public void serialize(OutputStream out) throws IOException {
+ serializeNode(bor, new DataOutputStream(out));
+ out.flush();
+ }
+
+ @Override
+ public void deserialize(InputStream in) throws IOException {
+ deserializeNode(bor, new DataInputStream(in));;
+ }
+}
diff --git a/hw_03/src/main/java/hw_01/StreamSerializable.java b/hw_03/src/main/java/hw_01/StreamSerializable.java
new file mode 100644
index 0000000..e9cb9df
--- /dev/null
+++ b/hw_03/src/main/java/hw_01/StreamSerializable.java
@@ -0,0 +1,15 @@
+package hw_01;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public interface StreamSerializable {
+
+ void serialize(OutputStream out) throws IOException;
+
+ /**
+ * Replace current state with data from input stream
+ */
+ void deserialize(InputStream in) throws IOException;
+}
\ No newline at end of file
diff --git a/hw_03/src/main/java/hw_01/Trie.java b/hw_03/src/main/java/hw_01/Trie.java
new file mode 100644
index 0000000..5c043b4
--- /dev/null
+++ b/hw_03/src/main/java/hw_01/Trie.java
@@ -0,0 +1,32 @@
+package hw_01;
+
+public interface Trie {
+
+ /**
+ * Expected complexity: O(|element|)
+ * @return true if this set did not already contain the specified
+ * element
+ */
+ boolean add(String element);
+
+ /**
+ * Expected complexity: O(|element|)
+ */
+ boolean contains(String element);
+
+ /**
+ * Expected complexity: O(|element|)
+ * @return true if this set contained the specified element
+ */
+ boolean remove(String element);
+
+ /**
+ * Expected complexity: O(1)
+ */
+ int size();
+
+ /**
+ * Expected complexity: O(|prefix|)
+ */
+ int howManyStartsWithPrefix(String prefix);
+}
diff --git a/hw_03/src/test/java/hw_01/TrieImplTest.java b/hw_03/src/test/java/hw_01/TrieImplTest.java
new file mode 100644
index 0000000..20fd175
--- /dev/null
+++ b/hw_03/src/test/java/hw_01/TrieImplTest.java
@@ -0,0 +1,78 @@
+package hw_01;
+
+import java.io.*;
+
+import junit.framework.TestCase;
+
+public class TrieImplTest extends TestCase {
+ private Bor b;
+
+ public void setUp() {
+ b = new Bor();
+ }
+
+ public void testAddSizeContains() {
+ assertFalse(b.contains("Hello"));
+ assertTrue(b.add("Hello"));
+ assertTrue(b.contains("Hello"));
+ assertEquals(1, b.size());
+ assertEquals(1, b.howManyStartsWithPrefix("Hell"));
+ }
+
+ public void testAddContainsPrefix() {
+ b.add("Hello");
+ assertFalse(b.contains("Hell"));
+ assertTrue(b.add("Hell"));
+ assertTrue(b.contains("Hell"));
+ assertEquals(2, b.size());
+ assertEquals(2, b.howManyStartsWithPrefix("Hell"));
+ }
+
+ public void testAddOutstandingWord() {
+ b.add("Hello");
+ assertTrue(b.add("Head"));
+ assertEquals(2, b.size());
+ }
+
+ public void testRemovePrefix() {
+ b.add("Hello");
+ b.add("Hell");
+ assertFalse(b.remove("He"));
+ assertTrue(b.remove("Hell"));
+ assertEquals(1, b.size());
+ assertEquals(1, b.howManyStartsWithPrefix("Hell"));
+ }
+
+ public void testTemoveSuffix() {
+ b.add("Hello");
+ b.add("Hell");
+ assertTrue(b.remove("Hello"));
+ assertEquals(1, b.size());
+ }
+
+ public void testSerialDeserial() throws IOException {
+ b.add("Hello");
+ b.add("Head");
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ b.serialize(baos);
+ byte[] buf1 = baos.toByteArray();
+
+ Bor b1 = new Bor();
+ b1.deserialize(new ByteArrayInputStream(buf1));
+ baos = new ByteArrayOutputStream();
+ b1.serialize(baos);
+ byte[] buf2 = baos.toByteArray();
+
+ assertEquals(buf1.length, buf2.length);
+
+ for(int i = 0; i < buf1.length; i++) {
+ assertEquals(buf1[i], buf2[i]);
+ }
+
+ assertTrue(b1.contains("Hello"));
+ assertTrue(b1.contains("Head"));
+ assertEquals(2, b1.size());
+ }
+
+}