From 87df59665ae1e3a852388239230274c2d240e277 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Mon, 18 Sep 2017 07:53:43 +0300 Subject: [PATCH 01/15] New HW --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store From 36c6cd519c337d69e7351ed4497bef59ba79d823 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Mon, 18 Sep 2017 08:26:01 +0300 Subject: [PATCH 02/15] Added src files --- HashTable.java | 162 +++++++++++++++++++++++++++++++++++++++++++++ HashTableTest.java | 41 ++++++++++++ List.java | 115 ++++++++++++++++++++++++++++++++ ListTest.java | 33 +++++++++ 4 files changed, 351 insertions(+) create mode 100644 HashTable.java create mode 100644 HashTableTest.java create mode 100644 List.java create mode 100644 ListTest.java diff --git a/HashTable.java b/HashTable.java new file mode 100644 index 0000000..dc8722d --- /dev/null +++ b/HashTable.java @@ -0,0 +1,162 @@ +package ru.spbau.solikov.hw1.src; + +/** + * Implementation of hash table structure using separate chaining + */ +public class HashTable { + + /** + * Number used in hash function + */ + private static final int DEFAULT_HASH_PRIME_NUMBER = 31; + + private List[] table; + private int capacity; + private int size; + + /** + * Creates a HashTable of capacity 2 + */ + public HashTable() { + capacity = 2; + table = new List[2]; + size = 0; + } + + /** + * Returns number of keys + * + * @return size + */ + public int size() { + return size; + } + + + /** + * Iterates on each list in HashTable and clears it, capacity is the same + */ + public void clear() { + for (List list : table) { + if (list != null) { + list.clear(); + } + } + size = 0; + } + + + /** + * Hash function for obtaining a code from key + * + * @param key + * @return hash code + */ + private int getHash(String key) { + int hash = 0; + for (int i = 0; i < key.length(); i++) { + hash = (DEFAULT_HASH_PRIME_NUMBER * hash + key.charAt(i)) % capacity; + } + return hash; + } + + + /** + * Returns data stored by key or null if not found + * + * @param key + * @return data or null if not found + */ + public String get(String key) { + int hash = getHash(key); + if (table[hash] == null) return null; + return table[hash].find(key); + } + + + /** + * Increases the capacity of HashTable by 2 times + */ + private void increase() { + List[] oldTable = table; + table = new List[2 * capacity]; + capacity *= 2; + + for (List list : oldTable) { + if (list != null) { + List.Node node = list.getHead(); + while (node != null) { + String key = node.getKey(); + int hash = getHash(key); + if (table[hash] == null) { + table[hash] = new List(); + } + table[hash].insert(key, node.getData()); + node = node.getNext(); + } + } + } + } + + /** + * Allows to add the pair of key and data into HashTable, if there's not enough space calls increase() + * + * @param key + * @param data + * @return data that was stored before by the same key or null if this space was empty + */ + public String put(String key, String data) { + int hash = getHash(key); + if (table[hash] == null) { + table[hash] = new List(); + } + + List current = table[hash]; + String deleted = current.delete(key); + current.insert(key, data); + + if (deleted == null) { + size++; + } + + if (size >= capacity) { + increase(); + } + + return deleted; + } + + + /** + * Allows to remove data stored by key + * + * @param key + * @return data stored by key + */ + public String remove(String key) { + int hash = getHash(key); + + if (table[hash] == null) { + return null; + } + + String deleted = table[hash].delete(key); + if (deleted != null) { + size--; + return deleted; + } + + return null; + } + + + /** + * Checks whether HashTable contains some data by passed key + * + * @param key + * @return data or null if the HashTable doesn't contain key + */ + public boolean contains(String key) { + return get(key) != null; + } +} diff --git a/HashTableTest.java b/HashTableTest.java new file mode 100644 index 0000000..2c14322 --- /dev/null +++ b/HashTableTest.java @@ -0,0 +1,41 @@ +package ru.spbau.solikov.hw1.test; + +import ru.spbau.solikov.hw1.src.HashTable; + +/** + * Class for testing all the HashTable functions + */ + +public class HashTableTest { + public static void main(String[] args) { + HashTable hashTable = new HashTable(); + + for (int i = 0; i < 100; i++){ + hashTable.put(String.valueOf(i), String.valueOf(i * i)); + } + + System.out.println(hashTable.size() + " - size of hashtable (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.get("10") + " - square of 10 (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.put("10", "200") + " - changed square of 10 (must have been 100)"); + + System.out.println(); + + System.out.println(hashTable.contains("10") + " - contains '10' (must be true)"); + + System.out.println(); + + System.out.println(hashTable.remove("10") + " - removed '10'"); + + System.out.println(); + + hashTable.clear(); + + System.out.println(hashTable.size() + " - size of hashtable (must be 0)"); + } +} diff --git a/List.java b/List.java new file mode 100644 index 0000000..82fbfea --- /dev/null +++ b/List.java @@ -0,0 +1,115 @@ +package ru.spbau.solikov.hw1.src; + +/** + * Implementation of single-linked list that stores Nodes of keys and data + */ +public class List { + + /** + * Class that stores keys and data and has link for next Node + */ + public class Node { + + private Node next; + private String key; + private String data; + + public Node(Node n, String k, String d) { + next = n; + key = k; + data = d; + } + + public Node getNext() { + return next; + } + + public String getKey() { + return key; + } + + public String getData() { + return data; + } + } + + private Node head; + private Node tail; + + public Node getHead() { + return head; + } + + /** + * Adds a pair of key and data in List + * + * @param key + * @param data + */ + public void insert(String key, String data) { + if (head != null){ + tail.next = new Node(null, key, data); + tail = tail.next; + } + + else{ + tail = head = new Node(null, key, data); + } + } + + /** + * Searches for data by key in List + * + * @param key + * @return data or null if not found + */ + public String find(String key) { + Node current = head; + while (current != null){ + if (current.getKey().equals(key)){ + return current.getData(); + } + current = current.getNext(); + } + + return null; + } + + /** + * Deletes a data stored by key in List + * + * @param key + * @return data that was deleted + */ + public String delete(String key) { + if (head == null){ + return null; + } + + if (head.getKey().equals(key)){ + String deleted = head.data; + head = head.next; + return deleted; + } + + Node current = head; + while (current.next != null){ + if (current.next.getKey().equals(key)){ + String deleted = current.next.data; + current.next = current.next.next; + return deleted; + } + current = current.next; + } + + return null; + } + + /** + * Clears the List + */ + public void clear(){ + head = null; + tail = null; + } +} diff --git a/ListTest.java b/ListTest.java new file mode 100644 index 0000000..4711288 --- /dev/null +++ b/ListTest.java @@ -0,0 +1,33 @@ +package ru.spbau.solikov.hw1.test; + +import ru.spbau.solikov.hw1.src.List; + +/** + * Class for testing all the List functions + */ + +public class ListTest { + public static void main(String[] args) { + List l = new List(); + + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + + for (List.Node node = l.getHead(); node != null; node = node.getNext()) { + System.out.println(node.getKey()); + } + + System.out.println(); + + System.out.println(l.find("5") + " - found key '5'."); + l.delete("5"); + System.out.println(l.delete("5") + " - deleted key '5'."); + + System.out.println(); + + l.clear(); + System.out.print(l.getHead() == null); + System.out.println(" - cleared the list."); + } +} From 06bb8f5ce02bece758d7b7c58733640d35221520 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Mon, 18 Sep 2017 08:26:49 +0300 Subject: [PATCH 03/15] Added .idea files --- misc.xml | 6 ++++++ modules.xml | 8 ++++++++ 2 files changed, 14 insertions(+) create mode 100644 misc.xml create mode 100644 modules.xml diff --git a/misc.xml b/misc.xml new file mode 100644 index 0000000..0548357 --- /dev/null +++ b/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/modules.xml b/modules.xml new file mode 100644 index 0000000..9b27cd1 --- /dev/null +++ b/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From c9f0caea21fe3378c5e1a017f9a267b1bb96a903 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Mon, 18 Sep 2017 08:34:15 +0300 Subject: [PATCH 04/15] New folder --- misc.xml => hw1/.idea/misc.xml | 0 modules.xml => hw1/.idea/modules.xml | 0 HashTable.java => hw1/src/HashTable.java | 0 HashTableTest.java => hw1/src/HashTableTest.java | 0 List.java => hw1/src/List.java | 0 ListTest.java => hw1/src/ListTest.java | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename misc.xml => hw1/.idea/misc.xml (100%) rename modules.xml => hw1/.idea/modules.xml (100%) rename HashTable.java => hw1/src/HashTable.java (100%) rename HashTableTest.java => hw1/src/HashTableTest.java (100%) rename List.java => hw1/src/List.java (100%) rename ListTest.java => hw1/src/ListTest.java (100%) diff --git a/misc.xml b/hw1/.idea/misc.xml similarity index 100% rename from misc.xml rename to hw1/.idea/misc.xml diff --git a/modules.xml b/hw1/.idea/modules.xml similarity index 100% rename from modules.xml rename to hw1/.idea/modules.xml diff --git a/HashTable.java b/hw1/src/HashTable.java similarity index 100% rename from HashTable.java rename to hw1/src/HashTable.java diff --git a/HashTableTest.java b/hw1/src/HashTableTest.java similarity index 100% rename from HashTableTest.java rename to hw1/src/HashTableTest.java diff --git a/List.java b/hw1/src/List.java similarity index 100% rename from List.java rename to hw1/src/List.java diff --git a/ListTest.java b/hw1/src/ListTest.java similarity index 100% rename from ListTest.java rename to hw1/src/ListTest.java From d190bcc50055b6e8b6e4d8fe9aa245d809ea274a Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Mon, 18 Sep 2017 09:41:35 +0300 Subject: [PATCH 05/15] Refactor folder path --- hw1/.idea/misc.xml | 6 + hw1/.idea/modules.xml | 8 + .../ru/spbau/solikov/hw1/src/HashTable.java | 162 ++++++++++++++++++ hw1/src/ru/spbau/solikov/hw1/src/List.java | 115 +++++++++++++ .../spbau/solikov/hw1/test/HashTableTest.java | 41 +++++ .../ru/spbau/solikov/hw1/test/ListTest.java | 33 ++++ 6 files changed, 365 insertions(+) create mode 100644 hw1/.idea/misc.xml create mode 100644 hw1/.idea/modules.xml create mode 100644 hw1/src/ru/spbau/solikov/hw1/src/HashTable.java create mode 100644 hw1/src/ru/spbau/solikov/hw1/src/List.java create mode 100644 hw1/src/ru/spbau/solikov/hw1/test/HashTableTest.java create mode 100644 hw1/src/ru/spbau/solikov/hw1/test/ListTest.java diff --git a/hw1/.idea/misc.xml b/hw1/.idea/misc.xml new file mode 100644 index 0000000..0548357 --- /dev/null +++ b/hw1/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hw1/.idea/modules.xml b/hw1/.idea/modules.xml new file mode 100644 index 0000000..9b27cd1 --- /dev/null +++ b/hw1/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/hw1/src/ru/spbau/solikov/hw1/src/HashTable.java b/hw1/src/ru/spbau/solikov/hw1/src/HashTable.java new file mode 100644 index 0000000..dc8722d --- /dev/null +++ b/hw1/src/ru/spbau/solikov/hw1/src/HashTable.java @@ -0,0 +1,162 @@ +package ru.spbau.solikov.hw1.src; + +/** + * Implementation of hash table structure using separate chaining + */ +public class HashTable { + + /** + * Number used in hash function + */ + private static final int DEFAULT_HASH_PRIME_NUMBER = 31; + + private List[] table; + private int capacity; + private int size; + + /** + * Creates a HashTable of capacity 2 + */ + public HashTable() { + capacity = 2; + table = new List[2]; + size = 0; + } + + /** + * Returns number of keys + * + * @return size + */ + public int size() { + return size; + } + + + /** + * Iterates on each list in HashTable and clears it, capacity is the same + */ + public void clear() { + for (List list : table) { + if (list != null) { + list.clear(); + } + } + size = 0; + } + + + /** + * Hash function for obtaining a code from key + * + * @param key + * @return hash code + */ + private int getHash(String key) { + int hash = 0; + for (int i = 0; i < key.length(); i++) { + hash = (DEFAULT_HASH_PRIME_NUMBER * hash + key.charAt(i)) % capacity; + } + return hash; + } + + + /** + * Returns data stored by key or null if not found + * + * @param key + * @return data or null if not found + */ + public String get(String key) { + int hash = getHash(key); + if (table[hash] == null) return null; + return table[hash].find(key); + } + + + /** + * Increases the capacity of HashTable by 2 times + */ + private void increase() { + List[] oldTable = table; + table = new List[2 * capacity]; + capacity *= 2; + + for (List list : oldTable) { + if (list != null) { + List.Node node = list.getHead(); + while (node != null) { + String key = node.getKey(); + int hash = getHash(key); + if (table[hash] == null) { + table[hash] = new List(); + } + table[hash].insert(key, node.getData()); + node = node.getNext(); + } + } + } + } + + /** + * Allows to add the pair of key and data into HashTable, if there's not enough space calls increase() + * + * @param key + * @param data + * @return data that was stored before by the same key or null if this space was empty + */ + public String put(String key, String data) { + int hash = getHash(key); + if (table[hash] == null) { + table[hash] = new List(); + } + + List current = table[hash]; + String deleted = current.delete(key); + current.insert(key, data); + + if (deleted == null) { + size++; + } + + if (size >= capacity) { + increase(); + } + + return deleted; + } + + + /** + * Allows to remove data stored by key + * + * @param key + * @return data stored by key + */ + public String remove(String key) { + int hash = getHash(key); + + if (table[hash] == null) { + return null; + } + + String deleted = table[hash].delete(key); + if (deleted != null) { + size--; + return deleted; + } + + return null; + } + + + /** + * Checks whether HashTable contains some data by passed key + * + * @param key + * @return data or null if the HashTable doesn't contain key + */ + public boolean contains(String key) { + return get(key) != null; + } +} diff --git a/hw1/src/ru/spbau/solikov/hw1/src/List.java b/hw1/src/ru/spbau/solikov/hw1/src/List.java new file mode 100644 index 0000000..82fbfea --- /dev/null +++ b/hw1/src/ru/spbau/solikov/hw1/src/List.java @@ -0,0 +1,115 @@ +package ru.spbau.solikov.hw1.src; + +/** + * Implementation of single-linked list that stores Nodes of keys and data + */ +public class List { + + /** + * Class that stores keys and data and has link for next Node + */ + public class Node { + + private Node next; + private String key; + private String data; + + public Node(Node n, String k, String d) { + next = n; + key = k; + data = d; + } + + public Node getNext() { + return next; + } + + public String getKey() { + return key; + } + + public String getData() { + return data; + } + } + + private Node head; + private Node tail; + + public Node getHead() { + return head; + } + + /** + * Adds a pair of key and data in List + * + * @param key + * @param data + */ + public void insert(String key, String data) { + if (head != null){ + tail.next = new Node(null, key, data); + tail = tail.next; + } + + else{ + tail = head = new Node(null, key, data); + } + } + + /** + * Searches for data by key in List + * + * @param key + * @return data or null if not found + */ + public String find(String key) { + Node current = head; + while (current != null){ + if (current.getKey().equals(key)){ + return current.getData(); + } + current = current.getNext(); + } + + return null; + } + + /** + * Deletes a data stored by key in List + * + * @param key + * @return data that was deleted + */ + public String delete(String key) { + if (head == null){ + return null; + } + + if (head.getKey().equals(key)){ + String deleted = head.data; + head = head.next; + return deleted; + } + + Node current = head; + while (current.next != null){ + if (current.next.getKey().equals(key)){ + String deleted = current.next.data; + current.next = current.next.next; + return deleted; + } + current = current.next; + } + + return null; + } + + /** + * Clears the List + */ + public void clear(){ + head = null; + tail = null; + } +} diff --git a/hw1/src/ru/spbau/solikov/hw1/test/HashTableTest.java b/hw1/src/ru/spbau/solikov/hw1/test/HashTableTest.java new file mode 100644 index 0000000..2c14322 --- /dev/null +++ b/hw1/src/ru/spbau/solikov/hw1/test/HashTableTest.java @@ -0,0 +1,41 @@ +package ru.spbau.solikov.hw1.test; + +import ru.spbau.solikov.hw1.src.HashTable; + +/** + * Class for testing all the HashTable functions + */ + +public class HashTableTest { + public static void main(String[] args) { + HashTable hashTable = new HashTable(); + + for (int i = 0; i < 100; i++){ + hashTable.put(String.valueOf(i), String.valueOf(i * i)); + } + + System.out.println(hashTable.size() + " - size of hashtable (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.get("10") + " - square of 10 (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.put("10", "200") + " - changed square of 10 (must have been 100)"); + + System.out.println(); + + System.out.println(hashTable.contains("10") + " - contains '10' (must be true)"); + + System.out.println(); + + System.out.println(hashTable.remove("10") + " - removed '10'"); + + System.out.println(); + + hashTable.clear(); + + System.out.println(hashTable.size() + " - size of hashtable (must be 0)"); + } +} diff --git a/hw1/src/ru/spbau/solikov/hw1/test/ListTest.java b/hw1/src/ru/spbau/solikov/hw1/test/ListTest.java new file mode 100644 index 0000000..4711288 --- /dev/null +++ b/hw1/src/ru/spbau/solikov/hw1/test/ListTest.java @@ -0,0 +1,33 @@ +package ru.spbau.solikov.hw1.test; + +import ru.spbau.solikov.hw1.src.List; + +/** + * Class for testing all the List functions + */ + +public class ListTest { + public static void main(String[] args) { + List l = new List(); + + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + + for (List.Node node = l.getHead(); node != null; node = node.getNext()) { + System.out.println(node.getKey()); + } + + System.out.println(); + + System.out.println(l.find("5") + " - found key '5'."); + l.delete("5"); + System.out.println(l.delete("5") + " - deleted key '5'."); + + System.out.println(); + + l.clear(); + System.out.print(l.getHead() == null); + System.out.println(" - cleared the list."); + } +} From 84e6dcbf02995fa09d9fcf7dbc97f295b77ef077 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Mon, 18 Sep 2017 09:50:44 +0300 Subject: [PATCH 06/15] Deleted extra --- .../spbau/solikov/hw1}/src/HashTable.java | 0 hw1/{ => ru/spbau/solikov/hw1}/src/List.java | 0 .../solikov/hw1/test}/HashTableTest.java | 0 .../spbau/solikov/hw1/test}/ListTest.java | 0 .../ru/spbau/solikov/hw1/src/HashTable.java | 162 ------------------ hw1/src/ru/spbau/solikov/hw1/src/List.java | 115 ------------- .../spbau/solikov/hw1/test/HashTableTest.java | 41 ----- .../ru/spbau/solikov/hw1/test/ListTest.java | 33 ---- 8 files changed, 351 deletions(-) rename hw1/{ => ru/spbau/solikov/hw1}/src/HashTable.java (100%) rename hw1/{ => ru/spbau/solikov/hw1}/src/List.java (100%) rename hw1/{src => ru/spbau/solikov/hw1/test}/HashTableTest.java (100%) rename hw1/{src => ru/spbau/solikov/hw1/test}/ListTest.java (100%) delete mode 100644 hw1/src/ru/spbau/solikov/hw1/src/HashTable.java delete mode 100644 hw1/src/ru/spbau/solikov/hw1/src/List.java delete mode 100644 hw1/src/ru/spbau/solikov/hw1/test/HashTableTest.java delete mode 100644 hw1/src/ru/spbau/solikov/hw1/test/ListTest.java diff --git a/hw1/src/HashTable.java b/hw1/ru/spbau/solikov/hw1/src/HashTable.java similarity index 100% rename from hw1/src/HashTable.java rename to hw1/ru/spbau/solikov/hw1/src/HashTable.java diff --git a/hw1/src/List.java b/hw1/ru/spbau/solikov/hw1/src/List.java similarity index 100% rename from hw1/src/List.java rename to hw1/ru/spbau/solikov/hw1/src/List.java diff --git a/hw1/src/HashTableTest.java b/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java similarity index 100% rename from hw1/src/HashTableTest.java rename to hw1/ru/spbau/solikov/hw1/test/HashTableTest.java diff --git a/hw1/src/ListTest.java b/hw1/ru/spbau/solikov/hw1/test/ListTest.java similarity index 100% rename from hw1/src/ListTest.java rename to hw1/ru/spbau/solikov/hw1/test/ListTest.java diff --git a/hw1/src/ru/spbau/solikov/hw1/src/HashTable.java b/hw1/src/ru/spbau/solikov/hw1/src/HashTable.java deleted file mode 100644 index dc8722d..0000000 --- a/hw1/src/ru/spbau/solikov/hw1/src/HashTable.java +++ /dev/null @@ -1,162 +0,0 @@ -package ru.spbau.solikov.hw1.src; - -/** - * Implementation of hash table structure using separate chaining - */ -public class HashTable { - - /** - * Number used in hash function - */ - private static final int DEFAULT_HASH_PRIME_NUMBER = 31; - - private List[] table; - private int capacity; - private int size; - - /** - * Creates a HashTable of capacity 2 - */ - public HashTable() { - capacity = 2; - table = new List[2]; - size = 0; - } - - /** - * Returns number of keys - * - * @return size - */ - public int size() { - return size; - } - - - /** - * Iterates on each list in HashTable and clears it, capacity is the same - */ - public void clear() { - for (List list : table) { - if (list != null) { - list.clear(); - } - } - size = 0; - } - - - /** - * Hash function for obtaining a code from key - * - * @param key - * @return hash code - */ - private int getHash(String key) { - int hash = 0; - for (int i = 0; i < key.length(); i++) { - hash = (DEFAULT_HASH_PRIME_NUMBER * hash + key.charAt(i)) % capacity; - } - return hash; - } - - - /** - * Returns data stored by key or null if not found - * - * @param key - * @return data or null if not found - */ - public String get(String key) { - int hash = getHash(key); - if (table[hash] == null) return null; - return table[hash].find(key); - } - - - /** - * Increases the capacity of HashTable by 2 times - */ - private void increase() { - List[] oldTable = table; - table = new List[2 * capacity]; - capacity *= 2; - - for (List list : oldTable) { - if (list != null) { - List.Node node = list.getHead(); - while (node != null) { - String key = node.getKey(); - int hash = getHash(key); - if (table[hash] == null) { - table[hash] = new List(); - } - table[hash].insert(key, node.getData()); - node = node.getNext(); - } - } - } - } - - /** - * Allows to add the pair of key and data into HashTable, if there's not enough space calls increase() - * - * @param key - * @param data - * @return data that was stored before by the same key or null if this space was empty - */ - public String put(String key, String data) { - int hash = getHash(key); - if (table[hash] == null) { - table[hash] = new List(); - } - - List current = table[hash]; - String deleted = current.delete(key); - current.insert(key, data); - - if (deleted == null) { - size++; - } - - if (size >= capacity) { - increase(); - } - - return deleted; - } - - - /** - * Allows to remove data stored by key - * - * @param key - * @return data stored by key - */ - public String remove(String key) { - int hash = getHash(key); - - if (table[hash] == null) { - return null; - } - - String deleted = table[hash].delete(key); - if (deleted != null) { - size--; - return deleted; - } - - return null; - } - - - /** - * Checks whether HashTable contains some data by passed key - * - * @param key - * @return data or null if the HashTable doesn't contain key - */ - public boolean contains(String key) { - return get(key) != null; - } -} diff --git a/hw1/src/ru/spbau/solikov/hw1/src/List.java b/hw1/src/ru/spbau/solikov/hw1/src/List.java deleted file mode 100644 index 82fbfea..0000000 --- a/hw1/src/ru/spbau/solikov/hw1/src/List.java +++ /dev/null @@ -1,115 +0,0 @@ -package ru.spbau.solikov.hw1.src; - -/** - * Implementation of single-linked list that stores Nodes of keys and data - */ -public class List { - - /** - * Class that stores keys and data and has link for next Node - */ - public class Node { - - private Node next; - private String key; - private String data; - - public Node(Node n, String k, String d) { - next = n; - key = k; - data = d; - } - - public Node getNext() { - return next; - } - - public String getKey() { - return key; - } - - public String getData() { - return data; - } - } - - private Node head; - private Node tail; - - public Node getHead() { - return head; - } - - /** - * Adds a pair of key and data in List - * - * @param key - * @param data - */ - public void insert(String key, String data) { - if (head != null){ - tail.next = new Node(null, key, data); - tail = tail.next; - } - - else{ - tail = head = new Node(null, key, data); - } - } - - /** - * Searches for data by key in List - * - * @param key - * @return data or null if not found - */ - public String find(String key) { - Node current = head; - while (current != null){ - if (current.getKey().equals(key)){ - return current.getData(); - } - current = current.getNext(); - } - - return null; - } - - /** - * Deletes a data stored by key in List - * - * @param key - * @return data that was deleted - */ - public String delete(String key) { - if (head == null){ - return null; - } - - if (head.getKey().equals(key)){ - String deleted = head.data; - head = head.next; - return deleted; - } - - Node current = head; - while (current.next != null){ - if (current.next.getKey().equals(key)){ - String deleted = current.next.data; - current.next = current.next.next; - return deleted; - } - current = current.next; - } - - return null; - } - - /** - * Clears the List - */ - public void clear(){ - head = null; - tail = null; - } -} diff --git a/hw1/src/ru/spbau/solikov/hw1/test/HashTableTest.java b/hw1/src/ru/spbau/solikov/hw1/test/HashTableTest.java deleted file mode 100644 index 2c14322..0000000 --- a/hw1/src/ru/spbau/solikov/hw1/test/HashTableTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package ru.spbau.solikov.hw1.test; - -import ru.spbau.solikov.hw1.src.HashTable; - -/** - * Class for testing all the HashTable functions - */ - -public class HashTableTest { - public static void main(String[] args) { - HashTable hashTable = new HashTable(); - - for (int i = 0; i < 100; i++){ - hashTable.put(String.valueOf(i), String.valueOf(i * i)); - } - - System.out.println(hashTable.size() + " - size of hashtable (must be 100)"); - - System.out.println(); - - System.out.println(hashTable.get("10") + " - square of 10 (must be 100)"); - - System.out.println(); - - System.out.println(hashTable.put("10", "200") + " - changed square of 10 (must have been 100)"); - - System.out.println(); - - System.out.println(hashTable.contains("10") + " - contains '10' (must be true)"); - - System.out.println(); - - System.out.println(hashTable.remove("10") + " - removed '10'"); - - System.out.println(); - - hashTable.clear(); - - System.out.println(hashTable.size() + " - size of hashtable (must be 0)"); - } -} diff --git a/hw1/src/ru/spbau/solikov/hw1/test/ListTest.java b/hw1/src/ru/spbau/solikov/hw1/test/ListTest.java deleted file mode 100644 index 4711288..0000000 --- a/hw1/src/ru/spbau/solikov/hw1/test/ListTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.spbau.solikov.hw1.test; - -import ru.spbau.solikov.hw1.src.List; - -/** - * Class for testing all the List functions - */ - -public class ListTest { - public static void main(String[] args) { - List l = new List(); - - for (int i = 0; i < 10; i++) { - l.insert(String.valueOf(i), String.valueOf(i)); - } - - for (List.Node node = l.getHead(); node != null; node = node.getNext()) { - System.out.println(node.getKey()); - } - - System.out.println(); - - System.out.println(l.find("5") + " - found key '5'."); - l.delete("5"); - System.out.println(l.delete("5") + " - deleted key '5'."); - - System.out.println(); - - l.clear(); - System.out.print(l.getHead() == null); - System.out.println(" - cleared the list."); - } -} From f863ee513b4c7ab7ddff9f35be7d0ef08e40177a Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Wed, 27 Sep 2017 11:01:02 +0300 Subject: [PATCH 07/15] Unit-Test for hw1 --- hw1/hw1.iml | 31 +++ .../hw1/src/primitive/test/HashTableTest.java | 41 ++++ .../hw1/src/primitive/test/ListTest.java | 33 ++++ .../spbau/solikov/hw1/test/HashTableTest.java | 177 ++++++++++++++++-- hw1/ru/spbau/solikov/hw1/test/ListTest.java | 76 ++++++-- 5 files changed, 324 insertions(+), 34 deletions(-) create mode 100644 hw1/hw1.iml create mode 100644 hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java create mode 100644 hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java diff --git a/hw1/hw1.iml b/hw1/hw1.iml new file mode 100644 index 0000000..bf5ed58 --- /dev/null +++ b/hw1/hw1.iml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java b/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java new file mode 100644 index 0000000..6a881ca --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java @@ -0,0 +1,41 @@ +package ru.spbau.solikov.hw1.src.primitive.test; + +import ru.spbau.solikov.hw1.src.HashTable; + +/** + * Class for testing all the HashTable functions + */ + +public class HashTableTest { + public static void main(String[] args) { + HashTable hashTable = new HashTable(); + + for (int i = 0; i < 100; i++){ + hashTable.put(String.valueOf(i), String.valueOf(i * i)); + } + + System.out.println(hashTable.size() + " - size of hashtable (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.get("10") + " - square of 10 (must be 100)"); + + System.out.println(); + + System.out.println(hashTable.put("10", "200") + " - changed square of 10 (must have been 100)"); + + System.out.println(); + + System.out.println(hashTable.contains("10") + " - contains '10' (must be true)"); + + System.out.println(); + + System.out.println(hashTable.remove("10") + " - removed '10'"); + + System.out.println(); + + hashTable.clear(); + + System.out.println(hashTable.size() + " - size of hashtable (must be 0)"); + } +} diff --git a/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java new file mode 100644 index 0000000..4572b49 --- /dev/null +++ b/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java @@ -0,0 +1,33 @@ +package ru.spbau.solikov.hw1.src.primitive.test; + +import ru.spbau.solikov.hw1.src.List; + +/** + * Class for testing all the List functions + */ + +public class ListTest { + public static void main(String[] args) { + List l = new List(); + + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + + for (List.Node node = l.getHead(); node != null; node = node.getNext()) { + System.out.println(node.getKey()); + } + + System.out.println(); + + System.out.println(l.find("5") + " - found key '5'."); + l.delete("5"); + System.out.println(l.delete("5") + " - deleted key '5'."); + + System.out.println(); + + l.clear(); + System.out.print(l.getHead() == null); + System.out.println(" - cleared the list."); + } +} diff --git a/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java b/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java index 2c14322..e66599b 100644 --- a/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java +++ b/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java @@ -1,41 +1,178 @@ package ru.spbau.solikov.hw1.test; +import org.junit.Before; +import org.junit.Test; import ru.spbau.solikov.hw1.src.HashTable; -/** - * Class for testing all the HashTable functions - */ +import static org.junit.Assert.*; public class HashTableTest { - public static void main(String[] args) { - HashTable hashTable = new HashTable(); - for (int i = 0; i < 100; i++){ - hashTable.put(String.valueOf(i), String.valueOf(i * i)); + private HashTable table; + + @Before + public void setUp() { + table = new HashTable(); + } + + @Test + public void testSizeOfEmptyHashTable() { + assertEquals(0, table.size()); + } + + @Test + public void testPut() { + table.put("27", "09"); + assertEquals(true, table.contains("27")); + } + + @Test + public void testSizeAfterPut() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); } + assertEquals(10, table.size()); + } - System.out.println(hashTable.size() + " - size of hashtable (must be 100)"); + @Test + public void testSizeAfterPutExistingKey() { + table.put("123", "321"); + table.put("123", "333"); + assertEquals(1, table.size()); + } - System.out.println(); + @Test + public void testPutExistingKey() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.put("4", "99"); + assertEquals("99", table.get("4")); + } - System.out.println(hashTable.get("10") + " - square of 10 (must be 100)"); + @Test + public void testClearEmpty() { + table.clear(); + assertEquals(0, table.size()); + } - System.out.println(); + @Test + public void testClear() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.clear(); + assertEquals(0, table.size()); + } - System.out.println(hashTable.put("10", "200") + " - changed square of 10 (must have been 100)"); + @Test + public void testGetFromEmpty() { + assertEquals(null, table.get("Deadline")); + } - System.out.println(); + @Test + public void testGetElementFromOneElementTable() { + table.put("1", "2"); + assertEquals("2", table.get("1")); + } - System.out.println(hashTable.contains("10") + " - contains '10' (must be true)"); + @Test + public void testGetElementFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals("5", table.get("5")); + } - System.out.println(); + @Test + public void testGetDoesNotChangeSize() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.get("5"); + assertEquals(10, table.size()); + } - System.out.println(hashTable.remove("10") + " - removed '10'"); + @Test + public void testSizeAfterRemovingFromEmptyTable() { + table.remove("123"); + assertEquals(0, table.size()); + } - System.out.println(); + @Test + public void testRemoveFromEmptyTable() { + assertEquals(null, table.remove("123")); + } - hashTable.clear(); + @Test + public void testRemoveFromOneElementTable() { + table.put("123", "321"); + assertEquals("321", table.get("123")); + } + + @Test + public void testSizeAfterRemovingFromOneElementTable() { + table.put("123", "321"); + table.remove("123"); + assertEquals(0, table.size()); + } + + @Test + public void testSizeAfterRemovingNonexistingFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.remove("100"); + assertEquals(10, table.size()); + } + + @Test + public void testSizeAfterRemoveFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + table.remove("5"); + assertEquals(9, table.size()); + } + + @Test + public void testRemoveFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals("4", table.remove("4")); + } - System.out.println(hashTable.size() + " - size of hashtable (must be 0)"); + @Test + public void testContainsFromEmptyTable() { + assertEquals(false, table.contains("some")); + } + + @Test + public void testContainsFromOneElementTable() { + table.put("Wednesday", "27.09"); + assertEquals(true, table.contains("Wednesday")); + } + + @Test + public void testContainsNonexistingElementFromOneElementTable() { + table.put("Wednesday", "27.09"); + assertEquals(false, table.contains("Sunday")); + } + + @Test + public void testContainsManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals(true, table.contains("4")); + } + + @Test + public void testContainsNonexistingElementFromManyElementTable() { + for (int i = 0; i < 10; i++) { + table.put(String.valueOf(i), String.valueOf(i)); + } + assertEquals(false, table.contains("123")); } -} +} \ No newline at end of file diff --git a/hw1/ru/spbau/solikov/hw1/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/test/ListTest.java index 4711288..8d8b694 100644 --- a/hw1/ru/spbau/solikov/hw1/test/ListTest.java +++ b/hw1/ru/spbau/solikov/hw1/test/ListTest.java @@ -1,33 +1,81 @@ package ru.spbau.solikov.hw1.test; +import org.junit.Before; +import org.junit.Test; import ru.spbau.solikov.hw1.src.List; -/** - * Class for testing all the List functions - */ +import static org.junit.Assert.*; public class ListTest { - public static void main(String[] args) { - List l = new List(); + private List l; + + @Before + public void setUp() { + l = new List(); + } + + @Test + public void testGetHeadOfEmptyList() { + assertEquals(null, l.getHead()); + } + + @Test + public void testInsert() { + l.insert("SPbAU", "Java"); + assertEquals("Java", l.getHead().getData()); + } + + @Test + public void testManyInserts() { for (int i = 0; i < 10; i++) { l.insert(String.valueOf(i), String.valueOf(i)); } - for (List.Node node = l.getHead(); node != null; node = node.getNext()) { - System.out.println(node.getKey()); + int number = 0; + for (List.Node n = l.getHead(); n != null; n = n.getNext()) { + assertEquals(Integer.toString(number), n.getData()); + number++; } + } + + @Test + public void testFindNonexistingElement() { + assertEquals(null, l.find("Something")); + } - System.out.println(); + @Test + public void testFindExistingElement() { + l.insert("SPbAU", "Java"); + assertEquals("Java", l.find("SPbAU")); + } - System.out.println(l.find("5") + " - found key '5'."); + @Test + public void testDeleteElementFromEmptyList() { + l.delete("SomeKey"); + assertEquals(null, l.getHead()); + } + + @Test + public void testDeleteNonexistingElement() { + l.insert("1", "2"); l.delete("5"); - System.out.println(l.delete("5") + " - deleted key '5'."); + assertNotEquals(null, l.getHead()); + } - System.out.println(); + @Test + public void testDeleteExistingElement() { + l.insert("SPbAU", "Java"); + l.delete("SPbAU"); + assertEquals(null, l.getHead()); + } + @Test + public void testClear() { + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } l.clear(); - System.out.print(l.getHead() == null); - System.out.println(" - cleared the list."); + assertEquals(null, l.getHead()); } -} +} \ No newline at end of file From 4beb1572a366cfc323cfaf2b843bdf566a793b9d Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Wed, 27 Sep 2017 19:52:33 +0300 Subject: [PATCH 08/15] Added new Home Work --- hw2/.idea/compiler.xml | 9 ++ hw2/.idea/gradle.xml | 17 +++ .../libraries/Gradle__junit_junit_4_12.xml | 11 ++ ...Gradle__org_hamcrest_hamcrest_core_1_3.xml | 11 ++ hw2/.idea/modules.xml | 10 ++ hw2/.idea/modules/hw2.iml | 13 ++ hw2/.idea/modules/hw2_main.iml | 13 ++ hw2/.idea/modules/hw2_test.iml | 17 +++ hw2/build.gradle | 14 +++ hw2/settings.gradle | 2 + .../java/ru/spbau/solikov/hw2/src/Spiral.java | 112 ++++++++++++++++++ .../solikov/hw2/src/test/SpiralTest.java | 49 ++++++++ 12 files changed, 278 insertions(+) create mode 100644 hw2/.idea/compiler.xml create mode 100644 hw2/.idea/gradle.xml create mode 100644 hw2/.idea/libraries/Gradle__junit_junit_4_12.xml create mode 100644 hw2/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml create mode 100644 hw2/.idea/modules.xml create mode 100644 hw2/.idea/modules/hw2.iml create mode 100644 hw2/.idea/modules/hw2_main.iml create mode 100644 hw2/.idea/modules/hw2_test.iml create mode 100644 hw2/build.gradle create mode 100644 hw2/settings.gradle create mode 100644 hw2/src/main/java/ru/spbau/solikov/hw2/src/Spiral.java create mode 100644 hw2/src/test/java/ru/spbau/solikov/hw2/src/test/SpiralTest.java diff --git a/hw2/.idea/compiler.xml b/hw2/.idea/compiler.xml new file mode 100644 index 0000000..f68cfe7 --- /dev/null +++ b/hw2/.idea/compiler.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/hw2/.idea/gradle.xml b/hw2/.idea/gradle.xml new file mode 100644 index 0000000..a5c3ae1 --- /dev/null +++ b/hw2/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/hw2/.idea/libraries/Gradle__junit_junit_4_12.xml b/hw2/.idea/libraries/Gradle__junit_junit_4_12.xml new file mode 100644 index 0000000..04c10dd --- /dev/null +++ b/hw2/.idea/libraries/Gradle__junit_junit_4_12.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/hw2/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml b/hw2/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml new file mode 100644 index 0000000..8262f72 --- /dev/null +++ b/hw2/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/hw2/.idea/modules.xml b/hw2/.idea/modules.xml new file mode 100644 index 0000000..0a515eb --- /dev/null +++ b/hw2/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/hw2/.idea/modules/hw2.iml b/hw2/.idea/modules/hw2.iml new file mode 100644 index 0000000..df6b05c --- /dev/null +++ b/hw2/.idea/modules/hw2.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw2/.idea/modules/hw2_main.iml b/hw2/.idea/modules/hw2_main.iml new file mode 100644 index 0000000..47d7e3c --- /dev/null +++ b/hw2/.idea/modules/hw2_main.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw2/.idea/modules/hw2_test.iml b/hw2/.idea/modules/hw2_test.iml new file mode 100644 index 0000000..1975b36 --- /dev/null +++ b/hw2/.idea/modules/hw2_test.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw2/build.gradle b/hw2/build.gradle new file mode 100644 index 0000000..4d6a793 --- /dev/null +++ b/hw2/build.gradle @@ -0,0 +1,14 @@ +group '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/hw2/settings.gradle b/hw2/settings.gradle new file mode 100644 index 0000000..c182db6 --- /dev/null +++ b/hw2/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'hw2' + diff --git a/hw2/src/main/java/ru/spbau/solikov/hw2/src/Spiral.java b/hw2/src/main/java/ru/spbau/solikov/hw2/src/Spiral.java new file mode 100644 index 0000000..9c1ce9f --- /dev/null +++ b/hw2/src/main/java/ru/spbau/solikov/hw2/src/Spiral.java @@ -0,0 +1,112 @@ +package ru.spbau.solikov.hw2.src; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Scanner; + +/** + * Matrix of size N * N that could be printed as spiral and sorted by first elements in column + */ +public class Spiral { + + private final int size; + private int matrix[][]; + + /** + * Generates matrix of given size + * Transposes it in order to easily sort by columns + * + * @param N odd integer + * @param other matrix + */ + public Spiral(int N, int other[][]) { + size = N; + matrix = new int[N][N]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + matrix[j][i] = other[i][j]; + } + } + } + + /** + * Method for initializing matrix's fields from standard input + * Transposes it in order to easily sort by columns + */ + public void fill() { + Scanner in = new Scanner(System.in); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + matrix[j][i] = in.nextInt(); + } + } + } + + /** + * Sorting the matrix by first element in column using custom comparator + */ + public void sort() { + Arrays.sort(matrix, new Comparator() { + @Override + public int compare(final int[] first, final int[] second) { + Integer f = first[0]; + Integer s = second[0]; + return f.compareTo(s); + } + }); + } + + /** + * Prints matrix's fields into standard output + * Transposes matrix back to the original view + */ + public void print() { + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + System.out.print(matrix[j][i] + " "); + } + System.out.println(); + } + } + + public int[][] getMatrix() { + return matrix; + } + + /** + * Transposes matrix back to the original view + * Prints matrix's fields into standard output by spiral order. Integer 'c' is the center of matrix + * External 'for' loop implements the layer of matrix + * 4 internal 'for' loops implement round way on sides of current rectangle + */ + public void printSpiral() { + final int c = size / 2; + System.out.print(matrix[c][c] + " "); + if (size > 1) { + int x = c; + int y = c; + int layer = 0; + for (int side = 2; side < size - layer; side++) { + y++; + System.out.print(matrix[y][x] + " "); + for (int i = 1; i < side + layer; i++) { + System.out.print(matrix[y][x - i] + " "); + } + x -= side - 1 + layer; + for (int i = 1; i < side + layer + 1; i++) { + System.out.print(matrix[y - i][x] + " "); + } + y -= side + layer; + for (int i = 1; i < side + layer + 1; i++) { + System.out.print(matrix[y][x + i] + " "); + } + x += side + layer; + for (int i = 1; i < side + layer + 1; i++) { + System.out.print(matrix[y + i][x] + " "); + } + y += side + layer; + layer++; + } + } + } +} \ No newline at end of file diff --git a/hw2/src/test/java/ru/spbau/solikov/hw2/src/test/SpiralTest.java b/hw2/src/test/java/ru/spbau/solikov/hw2/src/test/SpiralTest.java new file mode 100644 index 0000000..497577a --- /dev/null +++ b/hw2/src/test/java/ru/spbau/solikov/hw2/src/test/SpiralTest.java @@ -0,0 +1,49 @@ +package ru.spbau.solikov.hw2.src.test; + +import org.junit.Before; +import org.junit.Test; +import ru.spbau.solikov.hw2.src.Spiral; + +import java.util.Arrays; +import java.util.Comparator; + +import static org.junit.Assert.*; + + +public class SpiralTest { + + private int[][] elements = { + {4, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + private Spiral s = new Spiral(3, elements); + + /** + * Tests if matrix is equal to transposed input matrix + */ + @Test + public void testConstructor() { + int[][] matrix = s.getMatrix(); + for (int i = 0; i < matrix.length; i++){ + for (int j = 0; j < matrix.length; j++){ + assertEquals(true, matrix[j][i] == elements[i][j]); + } + } + } + + /** + * Tests if matrix is sorted from left to right + */ + @Test + public void testSort() { + s.sort(); + int[][] matrix = s.getMatrix(); + int prev = matrix[0][0]; + for (int i = 1; i < matrix.length; i++){ + assertEquals(true, prev <= matrix[i][0]); + } + } + +} \ No newline at end of file From 36facc9f1331415738dfaa3652b2eadd9a4642b3 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Wed, 27 Sep 2017 20:30:13 +0300 Subject: [PATCH 09/15] Added JavaDoc --- hw1/ru/spbau/solikov/hw1/src/HashTable.java | 4 +- .../spbau/solikov/hw1/test/HashTableTest.java | 83 ++++++++++++++++++- hw1/ru/spbau/solikov/hw1/test/ListTest.java | 45 ++++++++++ 3 files changed, 126 insertions(+), 6 deletions(-) diff --git a/hw1/ru/spbau/solikov/hw1/src/HashTable.java b/hw1/ru/spbau/solikov/hw1/src/HashTable.java index dc8722d..fe9b463 100644 --- a/hw1/ru/spbau/solikov/hw1/src/HashTable.java +++ b/hw1/ru/spbau/solikov/hw1/src/HashTable.java @@ -1,12 +1,12 @@ package ru.spbau.solikov.hw1.src; /** - * Implementation of hash table structure using separate chaining + * Implementation of hash table structure using separate chaining */ public class HashTable { /** - * Number used in hash function + * Number used in hash function */ private static final int DEFAULT_HASH_PRIME_NUMBER = 31; diff --git a/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java b/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java index e66599b..4e01672 100644 --- a/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java +++ b/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java @@ -6,26 +6,41 @@ import static org.junit.Assert.*; +/** + * Test class for HashTable with separate chaining + */ public class HashTableTest { private HashTable table; + /** + * Constructs new hash table for every test + */ @Before public void setUp() { table = new HashTable(); } + /** + * Tests if the size of empty table is 0 + */ @Test public void testSizeOfEmptyHashTable() { assertEquals(0, table.size()); } + /** + * Tests if 'put' puts the right key + */ @Test public void testPut() { table.put("27", "09"); assertEquals(true, table.contains("27")); } + /** + * Tests if the size after many 'puts' is correct + */ @Test public void testSizeAfterPut() { for (int i = 0; i < 10; i++) { @@ -34,6 +49,9 @@ public void testSizeAfterPut() { assertEquals(10, table.size()); } + /** + * Tests if the size after 'put' in the same key is correct + */ @Test public void testSizeAfterPutExistingKey() { table.put("123", "321"); @@ -41,6 +59,9 @@ public void testSizeAfterPutExistingKey() { assertEquals(1, table.size()); } + /** + * Tests if 'put' inserts new value instead of previous + */ @Test public void testPutExistingKey() { for (int i = 0; i < 10; i++) { @@ -50,12 +71,18 @@ public void testPutExistingKey() { assertEquals("99", table.get("4")); } + /** + * Tests if 'size' on empty table is 0 + */ @Test public void testClearEmpty() { table.clear(); assertEquals(0, table.size()); } + /** + * Tests if 'clear' works right on table with many keys + */ @Test public void testClear() { for (int i = 0; i < 10; i++) { @@ -65,17 +92,26 @@ public void testClear() { assertEquals(0, table.size()); } + /** + * Tests if 'get' from empty table returns null + */ @Test public void testGetFromEmpty() { assertEquals(null, table.get("Deadline")); } + /** + * Tests if 'get' from one-element table returns the right value + */ @Test public void testGetElementFromOneElementTable() { table.put("1", "2"); assertEquals("2", table.get("1")); } + /** + * Tests if 'get' on many-element table returns the right value + */ @Test public void testGetElementFromManyElementTable() { for (int i = 0; i < 10; i++) { @@ -84,6 +120,9 @@ public void testGetElementFromManyElementTable() { assertEquals("5", table.get("5")); } + /** + * Tests if 'get' does not change the size of hash table + */ @Test public void testGetDoesNotChangeSize() { for (int i = 0; i < 10; i++) { @@ -93,39 +132,57 @@ public void testGetDoesNotChangeSize() { assertEquals(10, table.size()); } + /** + * Tests if 'remove' does nothing with table after removing from empty table + */ @Test public void testSizeAfterRemovingFromEmptyTable() { table.remove("123"); assertEquals(0, table.size()); } + /** + * Tests if 'remove' on empty table returns null + */ @Test public void testRemoveFromEmptyTable() { assertEquals(null, table.remove("123")); } + /** + * Tests if 'remove' on one-element table returns the right value + */ @Test - public void testRemoveFromOneElementTable() { + public void testRemoveFromOneElementTable() { table.put("123", "321"); assertEquals("321", table.get("123")); } + /** + * Tests if the size of table becomes zero after removing from one-element table + */ @Test - public void testSizeAfterRemovingFromOneElementTable() { + public void testSizeAfterRemovingFromOneElementTable() { table.put("123", "321"); table.remove("123"); assertEquals(0, table.size()); } + /** + * Tests if the size of hash table is not affected by removing element that does not exist + */ @Test - public void testSizeAfterRemovingNonexistingFromManyElementTable() { + public void testSizeAfterRemovingNonexistingFromManyElementTable() { for (int i = 0; i < 10; i++) { table.put(String.valueOf(i), String.valueOf(i)); } table.remove("100"); assertEquals(10, table.size()); } - + + /** + * Tests if size after removing existing element decreases + */ @Test public void testSizeAfterRemoveFromManyElementTable() { for (int i = 0; i < 10; i++) { @@ -135,6 +192,9 @@ public void testSizeAfterRemoveFromManyElementTable() { assertEquals(9, table.size()); } + /** + * Tests if 'remove' from many-element table returns the right value + */ @Test public void testRemoveFromManyElementTable() { for (int i = 0; i < 10; i++) { @@ -143,23 +203,35 @@ public void testRemoveFromManyElementTable() { assertEquals("4", table.remove("4")); } + /** + * Tests if 'contains' returns false on empty table + */ @Test public void testContainsFromEmptyTable() { assertEquals(false, table.contains("some")); } + /** + * Tests if 'contains' returns true on element that exists in one-element table + */ @Test public void testContainsFromOneElementTable() { table.put("Wednesday", "27.09"); assertEquals(true, table.contains("Wednesday")); } + /** + * Tests if 'contains' returns false on element that does not exist in one-element table + */ @Test public void testContainsNonexistingElementFromOneElementTable() { table.put("Wednesday", "27.09"); assertEquals(false, table.contains("Sunday")); } + /** + * Tests if 'contains' returns true on many-element table + */ @Test public void testContainsManyElementTable() { for (int i = 0; i < 10; i++) { @@ -168,6 +240,9 @@ public void testContainsManyElementTable() { assertEquals(true, table.contains("4")); } + /** + * Tests if 'contains' returns false on elements that don't exist in many-element table + */ @Test public void testContainsNonexistingElementFromManyElementTable() { for (int i = 0; i < 10; i++) { diff --git a/hw1/ru/spbau/solikov/hw1/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/test/ListTest.java index 8d8b694..11d094e 100644 --- a/hw1/ru/spbau/solikov/hw1/test/ListTest.java +++ b/hw1/ru/spbau/solikov/hw1/test/ListTest.java @@ -6,26 +6,41 @@ import static org.junit.Assert.*; +/** + * Test class for class List that implements single-linked list + */ public class ListTest { private List l; + /** + * Constructs new list for every test + */ @Before public void setUp() { l = new List(); } + /** + * Tests if 'getHead' on empty list returns null + */ @Test public void testGetHeadOfEmptyList() { assertEquals(null, l.getHead()); } + /** + * Tests if 'insert' on empty list inserts the right data + */ @Test public void testInsert() { l.insert("SPbAU", "Java"); assertEquals("Java", l.getHead().getData()); } + /** + * Tests if many 'insert's insert the right data + */ @Test public void testManyInserts() { for (int i = 0; i < 10; i++) { @@ -39,23 +54,47 @@ public void testManyInserts() { } } + /** + * Tests if 'find' on empty list returns null + */ @Test public void testFindNonexistingElement() { assertEquals(null, l.find("Something")); } + /** + * Tests if 'find' on one-element list returns right value + */ @Test public void testFindExistingElement() { l.insert("SPbAU", "Java"); assertEquals("Java", l.find("SPbAU")); } + /** + * Tests if 'find' on many-element list returns right value + */ + @Test + public void testFindExistingElementManyElements() { + for (int i = 0; i < 10; i++) { + l.insert(String.valueOf(i), String.valueOf(i)); + } + l.insert("SPbAU", "Java"); + assertEquals("Java", l.find("SPbAU")); + } + + /** + * Tests if 'delete' on empty list does nothing + */ @Test public void testDeleteElementFromEmptyList() { l.delete("SomeKey"); assertEquals(null, l.getHead()); } + /** + * Tests if 'delete' nonexisting element on one-element list does nothing + */ @Test public void testDeleteNonexistingElement() { l.insert("1", "2"); @@ -63,6 +102,9 @@ public void testDeleteNonexistingElement() { assertNotEquals(null, l.getHead()); } + /** + * Tests if 'delete' from one-element list truly deletes element + */ @Test public void testDeleteExistingElement() { l.insert("SPbAU", "Java"); @@ -70,6 +112,9 @@ public void testDeleteExistingElement() { assertEquals(null, l.getHead()); } + /** + * Tests if 'clear' deletes all elements from many-element list + */ @Test public void testClear() { for (int i = 0; i < 10; i++) { From dc6efaeb11e89f3b46fc117e8e349b254c9614e0 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Sat, 30 Sep 2017 03:24:11 +0300 Subject: [PATCH 10/15] Fixed remarks from to 1st branch --- hw1/ru/spbau/solikov/hw1/src/HashTable.java | 23 +++----- hw1/ru/spbau/solikov/hw1/src/List.java | 58 ++++++++++--------- .../hw1/src/primitive/test/ListTest.java | 4 -- hw1/ru/spbau/solikov/hw1/test/ListTest.java | 21 +++---- 4 files changed, 52 insertions(+), 54 deletions(-) diff --git a/hw1/ru/spbau/solikov/hw1/src/HashTable.java b/hw1/ru/spbau/solikov/hw1/src/HashTable.java index fe9b463..f71fafd 100644 --- a/hw1/ru/spbau/solikov/hw1/src/HashTable.java +++ b/hw1/ru/spbau/solikov/hw1/src/HashTable.java @@ -11,14 +11,13 @@ public class HashTable { private static final int DEFAULT_HASH_PRIME_NUMBER = 31; private List[] table; - private int capacity; + private int capacity = 2; private int size; /** - * Creates a HashTable of capacity 2 + * Creates a HashTable of default capacity */ public HashTable() { - capacity = 2; table = new List[2]; size = 0; } @@ -45,7 +44,6 @@ public void clear() { size = 0; } - /** * Hash function for obtaining a code from key * @@ -60,7 +58,6 @@ private int getHash(String key) { return hash; } - /** * Returns data stored by key or null if not found * @@ -69,11 +66,12 @@ private int getHash(String key) { */ public String get(String key) { int hash = getHash(key); - if (table[hash] == null) return null; + if (table[hash] == null){ + return null; + } return table[hash].find(key); } - /** * Increases the capacity of HashTable by 2 times */ @@ -84,15 +82,14 @@ private void increase() { for (List list : oldTable) { if (list != null) { - List.Node node = list.getHead(); - while (node != null) { - String key = node.getKey(); + while (!list.isEmpty()) { + String key = list.getHeadsKey(); int hash = getHash(key); if (table[hash] == null) { table[hash] = new List(); } - table[hash].insert(key, node.getData()); - node = node.getNext(); + table[hash].insert(key, list.getHeadsData()); + list.delete(key); } } } @@ -126,7 +123,6 @@ public String put(String key, String data) { return deleted; } - /** * Allows to remove data stored by key * @@ -149,7 +145,6 @@ public String remove(String key) { return null; } - /** * Checks whether HashTable contains some data by passed key * diff --git a/hw1/ru/spbau/solikov/hw1/src/List.java b/hw1/ru/spbau/solikov/hw1/src/List.java index 82fbfea..04f8d48 100644 --- a/hw1/ru/spbau/solikov/hw1/src/List.java +++ b/hw1/ru/spbau/solikov/hw1/src/List.java @@ -8,10 +8,12 @@ public class List { /** * Class that stores keys and data and has link for next Node */ - public class Node { + private class Node { private Node next; + private String key; + private String data; public Node(Node n, String k, String d) { @@ -19,21 +21,10 @@ public Node(Node n, String k, String d) { key = k; data = d; } - - public Node getNext() { - return next; - } - - public String getKey() { - return key; - } - - public String getData() { - return data; - } } private Node head; + private Node tail; public Node getHead() { @@ -47,16 +38,23 @@ public Node getHead() { * @param data */ public void insert(String key, String data) { - if (head != null){ + if (head != null) { tail.next = new Node(null, key, data); tail = tail.next; - } - - else{ + } else { tail = head = new Node(null, key, data); } } + /** + * Checks if the list does not contain any elements + * + * @return boolean - true if the list is empty + */ + public boolean isEmpty() { + return head == null; + } + /** * Searches for data by key in List * @@ -65,16 +63,24 @@ public void insert(String key, String data) { */ public String find(String key) { Node current = head; - while (current != null){ - if (current.getKey().equals(key)){ - return current.getData(); + while (current != null) { + if (current.key.equals(key)) { + return current.data; } - current = current.getNext(); + current = current.next; } return null; } + public String getHeadsKey() { + return head.key; + } + + public String getHeadsData() { + return head.data; + } + /** * Deletes a data stored by key in List * @@ -82,19 +88,19 @@ public String find(String key) { * @return data that was deleted */ public String delete(String key) { - if (head == null){ + if (head == null) { return null; } - if (head.getKey().equals(key)){ + if (head.key.equals(key)) { String deleted = head.data; head = head.next; return deleted; } Node current = head; - while (current.next != null){ - if (current.next.getKey().equals(key)){ + while (current.next != null) { + if (current.next.key.equals(key)) { String deleted = current.next.data; current.next = current.next.next; return deleted; @@ -108,7 +114,7 @@ public String delete(String key) { /** * Clears the List */ - public void clear(){ + public void clear() { head = null; tail = null; } diff --git a/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java index 4572b49..be54d90 100644 --- a/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java +++ b/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java @@ -14,10 +14,6 @@ public static void main(String[] args) { l.insert(String.valueOf(i), String.valueOf(i)); } - for (List.Node node = l.getHead(); node != null; node = node.getNext()) { - System.out.println(node.getKey()); - } - System.out.println(); System.out.println(l.find("5") + " - found key '5'."); diff --git a/hw1/ru/spbau/solikov/hw1/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/test/ListTest.java index 11d094e..7100040 100644 --- a/hw1/ru/spbau/solikov/hw1/test/ListTest.java +++ b/hw1/ru/spbau/solikov/hw1/test/ListTest.java @@ -22,11 +22,11 @@ public void setUp() { } /** - * Tests if 'getHead' on empty list returns null + * Tests if 'isEmpty' on empty list returns true */ @Test - public void testGetHeadOfEmptyList() { - assertEquals(null, l.getHead()); + public void testIsEmptyOfEmptyList() { + assertEquals(true, l.isEmpty()); } /** @@ -35,7 +35,7 @@ public void testGetHeadOfEmptyList() { @Test public void testInsert() { l.insert("SPbAU", "Java"); - assertEquals("Java", l.getHead().getData()); + assertEquals("Java", l.getHeadsData()); } /** @@ -48,9 +48,10 @@ public void testManyInserts() { } int number = 0; - for (List.Node n = l.getHead(); n != null; n = n.getNext()) { - assertEquals(Integer.toString(number), n.getData()); + while (!l.isEmpty()) { + assertEquals(Integer.toString(number), l.getHeadsData()); number++; + l.delete(l.getHeadsKey()); } } @@ -89,7 +90,7 @@ public void testFindExistingElementManyElements() { @Test public void testDeleteElementFromEmptyList() { l.delete("SomeKey"); - assertEquals(null, l.getHead()); + assertEquals(true, l.isEmpty()); } /** @@ -99,7 +100,7 @@ public void testDeleteElementFromEmptyList() { public void testDeleteNonexistingElement() { l.insert("1", "2"); l.delete("5"); - assertNotEquals(null, l.getHead()); + assertNotEquals(true, l.isEmpty()); } /** @@ -109,7 +110,7 @@ public void testDeleteNonexistingElement() { public void testDeleteExistingElement() { l.insert("SPbAU", "Java"); l.delete("SPbAU"); - assertEquals(null, l.getHead()); + assertEquals(true, l.isEmpty()); } /** @@ -121,6 +122,6 @@ public void testClear() { l.insert(String.valueOf(i), String.valueOf(i)); } l.clear(); - assertEquals(null, l.getHead()); + assertEquals(true, l.isEmpty()); } } \ No newline at end of file From a8187f8cfc01c1f6621f4a2356cadb437076ff62 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Wed, 4 Oct 2017 16:22:34 +0300 Subject: [PATCH 11/15] =?UTF-8?q?Added=20homework=20=E2=84=963?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hw3_Trie/.idea/compiler.xml | 9 + hw3_Trie/.idea/gradle.xml | 17 ++ hw3_Trie/.idea/misc.xml | 6 + hw3_Trie/.idea/modules.xml | 10 + hw3_Trie/.idea/modules/bor.iml | 13 + hw3_Trie/.idea/modules/bor_main.iml | 13 + hw3_Trie/.idea/modules/bor_test.iml | 17 ++ hw3_Trie/build.gradle | 14 + hw3_Trie/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54788 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + hw3_Trie/settings.gradle | 2 + .../main/java/ru/spbau/solikov/src/Trie.java | 245 ++++++++++++++++++ .../test/java/ru/spbau/solikov/test/Main.java | 238 +++++++++++++++++ 13 files changed, 590 insertions(+) create mode 100644 hw3_Trie/.idea/compiler.xml create mode 100644 hw3_Trie/.idea/gradle.xml create mode 100644 hw3_Trie/.idea/misc.xml create mode 100644 hw3_Trie/.idea/modules.xml create mode 100644 hw3_Trie/.idea/modules/bor.iml create mode 100644 hw3_Trie/.idea/modules/bor_main.iml create mode 100644 hw3_Trie/.idea/modules/bor_test.iml create mode 100644 hw3_Trie/build.gradle create mode 100644 hw3_Trie/gradle/wrapper/gradle-wrapper.jar create mode 100644 hw3_Trie/gradle/wrapper/gradle-wrapper.properties create mode 100644 hw3_Trie/settings.gradle create mode 100644 hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java create mode 100644 hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java 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 0000000000000000000000000000000000000000..5d7d24614cac38960edd6e79e20a9a23d8edea91 GIT binary patch literal 54788 zcmafaW0WS*vSoGIwr!)!wr%4p+g6utqszAKsxI5MZBNhK_h#nax$n)7$jp^1Vx1G2 zC(qu2RFDP%MFj$agaiTt68tMbK*0a&2m}Q6_be-_B1k7GC&mB*r0`FQu26lR{C^cx z{>oqT|Dz}?C?_cuFbIhy@Hlls4PVE#kL z%+b)q8t~t$qWrU}o1>w6dSEU{WQ11MaYRHV`^W006GEHNkKbo3<`>slS- z^Iau?J5(A*RcG;?9caykA`<#qy1~O zV;;PYMn6SI$q}ds#zKhlt{2DkLyA|tPj@5nHw|TfoB{R9AOtjRH|~!gjc7>@`h6hQ zNQ|Ch4lR}rT_GI4eQoy|sMheUuhTnv@_rRPV^^6SNCY zJt~}LH52Y+RK{G^aZh@qG*^+5XM={Yu0CS=<}foB$I}fd5f&atxdLYMbAT-oGoKoE zEX@l(|ILgqD&rTwS4@T(du@BzN3(}du%3WCtJ*e1WJ5HWPNihA7O65R=Zp&IHPQn{ zTJ{$GYURp`Lr$UQ$ZDoj)1f(fN-I+C0)PVej&x_8WZUodh~2t5 z^<=jtVQnpoH>x5ncT0H=^`9-~oCmK=MD#4qnx+7-E-_n^0{2wjL2YV;WK(U;%aCN} zTPh334F$MTbxR7|7mEtX3alSAz|G)I+eFvQnY}XldO7I7$ z2-ZeSVckL<)N1tQ)M6@8uW;`pybJ4+Zf4&;=27ShUds^TB8DN4y^x=7xslL*1%HX_ zT(iSMx?g}!7jTEjX@&lI{{ifXnD}tWA8x4A3#o?GX9GMQHc-%WBBl|UlS|HYNH}JU z?I48Qizg+VWgSZ#zW<;tMruWI@~tW~X_GT(Me0(X0+ag8b-P6vA(1q165LJLl%zIl z?Ef?_&y7e?U@PK^nTSGu!90^0wjPY}`1@cng< z8p@n!$bcZvs3dwYo!t+cpq=9n`6Gi|V&v32g3zJV>ELG|eijj@>UQ8n)?`HPYai20W!}g}CSvAyisSPm0W|p?*Zq_r(%nCY8@}OXs2pS4# zI*)S^UFi`&zltazAxB2B_Gt7iX?Y25?B#w+-*y#dJIH(fIA<(GUhfiupc!IVAu&vF zg3#yzI2SrRpMSxpF*`0Ngul=!@E0Li|35w|ING^;2)a0%18kiwj18Ub{sSbEm38fq z1yOlHl7;{l4yv_FQZ`n><+LwoaKk|cGBRNnN;XDstie!~t5 z#ZWz9*3qvR2XkNZYI0db?t^(lG-Q8*4Jd6Q44rT71}NCQ2nryz(Btr|?2oa(J1`cn z`=-|7k;Q^9=GaCmyu(!&8QJRv=P5M#yLAL|6t%0+)fBn2AnNJg%86562VaB+9869& zfKkJa)8)BQb}^_r0pA1u)W$O`Y~Lenzyv>;CQ_qcG5Z_x^0&CP8G*;*CSy7tBVt|X zt}4Ub&av;8$mQk7?-2%zmOI4Ih72_?WgCq|eKgY~1$)6q+??Qk1DCXcQ)yCix5h#g z4+z7=Vn%$srNO52mlyjlwxO^ThKBz@(B8WGT`@!?Jhu^-9P1-ptx_hfbCseTj{&h}=7o5m0k)+Xx7D&2Vh zXAY*n|A~oM|4%rftd%$BM_6Pd7YVSA4iSzp_^N|raz6ODulPeY4tHN5j$0K9Y4=_~ z)5Wy%A)jp0c+415T7Q#6TZsvYF`adD%0w9Bl2Ip`4nc7h{42YCdZn};GMG+abcIR0 z+z0qSe?+~R5xbD^KtQ;-KtM$Q{Q~>PCzP!TWq`Wu@s-oq!GawPuO?AzaAVX9nLRvg z0P`z82q=Iw2tAw@bDiW;LQ7-vPeX(M#!~eD43{j*F<;h#Tvp?i?nMY1l-xxzoyGi8 zS7x(hY@=*uvu#GsX*~Jo*1B-TqL>Tx$t3sJ`RDiZ_cibBtDVmo3y^DgBsg-bp#dht zV(qiVs<+rrhVdh`wl^3qKC2y!TWM_HRsVoYaK2D|rkjeFPHSJ;xsP^h-+^8{chvzq z%NIHj*%uoS!;hGN?V;<@!|l{bf|HlP0RBOO(W6+vy(ox&e=g>W@<+P$S7%6hcjZ0< z><8JG)PTD4M^ix6OD5q$ZhUD>4fc!nhc4Y0eht6>Y@bU zmLTGy0vLkAK|#eZx+rXpV>6;v^fGXE^CH-tJc zmRq+7xG6o>(>s}bX=vW3D52ec1U(ZUk;BEp2^+#cz4vt zSe}XptaaZGghCACN5JJ^?JUHI1t^SVr`J&d_T$bcou}Q^hyiZ;ca^Um>*x4Nk?)|a zG2)e+ndGq9E%aKORO9KVF|T@a>AUrPhfwR%6uRQS9k!gzc(}9irHXyl5kc_2QtGAV7-T z+}cdnDY2687mXFd$5-(sHg|1daU)2Bdor`|(jh6iG{-)1q_;6?uj!3+&2fLlT~53- zMCtxe{wjPX}Ob$h2R9#lbdl0*UM_FN^C4C-sf3ZMoOAuq>-k+&K%!%EYYHMOTN~TB z8h5Ldln5sx_H3FoHrsaR`sGaGoanU7+hXf<*&v4>1G-8v;nMChKkZnVV#Q_LB{FXS ziG89d+p+9(ZVlc1+iVQy{*5{)+_JMF$Dr+MWjyO@Irs}CYizTI5puId;kL>fM6T(3 zat^8C6u0Ck1cUR%D|A<;uT&cM%DAXq87C~FJsgGMKa_FN#bq2+u%B!_dKbw7csI=V z-PtpPOv<q}F zS)14&NI3JzYKX?>aIs;lf)TfO3W;n+He)p5YGpQ;XxtY_ixQr7%nFT0Cs28c3~^`d zgzu42up|`IaAnkM;*)A~jUI%XMnD_u4rZwwdyb0VKbq@u?!7aQCP@t|O!1uJ8QmAS zPoX9{rYaK~LTk%3|5mPHhXV<}HSt4SG`E!2jk0-C6%B4IoZlIrbf92btI zCaKuXl=W0C`esGOP@Mv~A!Bm6HYEMqjC`?l1DeW&(2&E%R>yTykCk*2B`IcI{@l^| z8E%@IJt&TIDxfFhN_3ja(PmnPFEwpn{b`A z`m$!H=ek)46OXllp+}w6g&TscifgnxN^T{~JEn{A*rv$G9KmEqWt&Ab%5bQ*wbLJ+ zr==4do+}I6a37u_wA#L~9+K6jL)lya!;eMg5;r6U>@lHmLb(dOah&UuPIjc?nCMZ)6b+b4Oel?vcE5Q4$Jt71WOM$^`oPpzo_u; zu{j5ys?ENRG`ZE}RaQpN;4M`j@wA|C?oOYYa;Jja?j2?V@ zM97=sn3AoB_>P&lR zWdSgBJUvibzUJhyU2YE<2Q8t=rC`DslFOn^MQvCquhN~bFj?HMNn!4*F?dMkmM)## z^$AL9OuCUDmnhk4ZG~g@t}Im2okt9RDY9Q4dlt~Tzvhtbmp8aE8;@tupgh-_O-__) zuYH^YFO8-5eG_DE2!~ZSE1lLu9x-$?i*oBP!}0jlk4cy5^Q;{3E#^`3b~Su_bugsj zlernD@6h~-SUxz4fO+VEwbq+_`W{#bG{UOrU;H)z%W0r-mny1sm#O@gvwE72c^im)UrJnQgcB_HxILh!9fPQ);whe*(eIUjA(t{8iI(?NY<5^SGOr;vrcKpedfTu zWCTHMK16<@(tI%`NxN3xW6nKX{JW=77{~yR$t1$xwKUm7UJmOrnI4Z zajmwO&zZ8PhJ6FNRjID+@QZ8fz%%f2c{Xh*BWDIK zXrFxswPdd;(i}fLsNVb(sx-hMJ>IQ0QvH^z3= zc;TX|YE>HpO6-C5=g{+l3U6fF`AXJM6@kcoWLQXxiNiXab#!P8ozeR^oy#PfdS#aj zUDKKNx>5&v%k*OBF;-)X5Afpd60K{FTH@1|)>M!!F)jb))f&{UY-rcR>h z`~9|W#a`Yw7fD~{3`rktJC|L46-(sRaa~hM-d#KSG6@_*&+pnNYQ2JSy@BNg_Tx7< zB-vhG+{d^*zIH!;2M7O`_S{?EKffQ02;N>=2!3JqQX(M_Aj#}dCfdb?yGH%tk^_Zf zAtZ5!rnq4(WSd!_GfuPp4uDd2(8%>)Iu6z=XjRQLi2_RBg97~ zr$zf>FNkUG3~bp6#hl^3HSA2*SS-DT_QkX#QNcG2?8&Cm6Sj#}yaqEhjq1GabS)ZwBhcKc;52~Qc*Z@=jRjfqZO1%y?*D(iB&EE z-Aln~CD}?DqVGGB``Q@F-TY|Fj7)4D28@Z-@a-A4(KC*}W4*2l?E>!wviGFcB*Dc3z50hH^i0Y`j zip{Em#(a42NnOEvkU+6SfAkEzO$ z*j*3sOP4y2W@t7)nbi9Dcj|9Bw}z)VzKuAx4<&3`!gMhuW5&4%F@_!ZKBoaBHYwcn3WcL^0l zkdkY#l8~$5UazRWOJo32=kA|tKs!Y_vX=+xrA3Mwd45^vZe02+dI_r|rmO-`>l0$i zEB%YFf8ecv=Q@YPntwR)df$>p+zI@!1-aj13HMYz5$QWWp$U&Z(I?C5rYl8S=m|d!*(Y&`gzl zu00=P^fRg?$GE2+$)wr(ohep`G%yKT(qdGmR!M45W`~K4bC@YwX{J;T@dq=$9o>;L zz%NIUoFhZxHIjtR1kdw5V7u=4{!3oQc;za?0UQVj5f%uD<=^`&>TYc9;$-0p5VNob z2pSvzby?QX*3j%fJx*5BcET~k^5xT{iQin-qP*nWQ9THOA69^wDN5utzTj#~upjf}CtShX9;wdXE35EVlzWqIGJ z)io1?vG_sea+iQjU%m@q)4(=eS5zC1h|!bCE~d9gvl{7)!IScau*OTR`)!Mhr`mdX zlhmcf-Ms-t;DYx9o2z=q68Nm{ zOF;j&-eqWvD}_5X8`^t48wcrR%*&RycEe!J5nJguNo~cP6)1|!4@Jb2YL6IYdyrH8 zI$W1D+$LRa4*EC=4Cr)=0Qap5g}M^+jyvlDE}G8-wsVQYX&UXR#=~{XZLTPY`=3=N zkvaUS+4ofuBn|356>5pTPX|r)^QG(R2d$TX>Krwf&QVgVCM9zP64l%Z8B=2RYP%{E zaKc@qdtK`R({$|K`t5>0?KorZI1)6`9@|#O>v1WK@3bbLFtGM4gd98X0(-9{W{NiN zIuG0D%0l5WhXSRNbfROzH6w*YO&2Xpx5amm%+T4$qtvPDK+eUjfs$g@<`DBwNH1(33NhDKwO*I9E z$bW{D7h4@U~&K4klFtk`+Smzy>$vNph6hQsYQ1QF(- zHK>f)>|MT%=q)(U-3br5R4KIE!FeeTP`{-^wpgKJzcOqD?!&-6Yf7fd<^40T$r z{@91>s^KAH@mw(72{v#n4rzh?z_qh-AL;FAt==sT(BFv)(FXSoKd)RMA40`^)3^+Z zwdPe9j*t}}%!Fk@58lX}s`NX-7M;>k)w7j1`*~g_dAMDLsOq`@C>D(lreX%!c_OjX zTP$xDO*C|S27Hd)6?;6;Y`P3$%YFG)9y2H0Yuw;6Z2{^y2YvKP`V&OVi;L`j{L;jL zvz-omEQby(t)f?-HssRfTDYnS`=UG{>1Y)Dh(Xb>WU++>XOoF@TR;-#<1E+1AqPdk=H6)VQ32z zLdHM3uv~8{(>v|*O>k2VTW}=fw~%fuNfyf6FMaEXzdHB?tnHs6%)R(k_^``|IN|L# zV&QQG*x~n}a?;|la|TQD383!6WOfCv9V@-(g`ab3{CgpIjQ zGyCjpiIaK${m-Zd;m*k+7;?~M6)Wqb>yI*k`=@zOr%NjIs(C?BUqCq8^ zsi_)Bk)kyU`NL<6nholj+3Xs*E%vZ2H<};VoFCvMFLYwFg-gi8C%2@0gH#_lU>~8E z?>!v9-YFw6r=Z{xMI59a3J6_y8&}4UeEr?9w($B){={R9reR;r4Jgl?G)eMv=EOsc zckWsS;fuDu;l?Dgzgyhj^H>RMJs^*kzUfB#Ax}fqmj?Eb#G1W$J(4a)qfI(k=2*_Y zqr3?H*#`c8owZQ>48MUl@A(yQxuXBM2|bdy`x=bcfHc~8b9#odFy|NGMC(oMC%C+$ zi;L=xaJ%=;6Qf)kX-netDG|g#BZrnfdTm79e(Px7oy)wLHNB^EUMI7snGBJIuq*RP z@Xv@1TIRW_^S82~__wm~U(}t&|5uS))d}DzVP^x7v9q&svHy>{v$D24wjk=4SiJ7i zqf#YhQ?sQusP?MXrRx0PczL)ABq5Z%NibA3eTRvr^@n;Fsio!I2;YM^8}EP;&7WT# zqivIJ-A+dn6W9FwzQ7v&<$;P5qwe`TR5_AiRFDRGVmdG3h+?&byKRASKwXHQiegIU zvi;If(y)ozZ%=Q6)cR|q)pkV>bAocyDX#Om&LQ?^D;#XBhNC;^+80{v1k1(4X1RWKo4Onb+)A zp&OGpq39Ss9Do68%xbC+SH>N@bhr?aF^3ARMK)^mWxfuvt|?ucl0$sf){gT9_b~^# z3>QnE)-@zE%xH=ax{R1+8?7wHJFQhqx1xirV(lZN0HU=>7ODhQ5k^5BK973IumdDP z(oUtiC^Ya#Q@9^~vNuH)*L|F$!0eySLZ_2FYGn%S71MQAFrHK4i#UwxjM0gxL;pC#^nGA?B0S zjI>+f^}Ik10y+Dkm{%iS3&XUVZ;GCHpJ5Re31~x@7X68v;(n<6>>q?g=^VldiKw#@ zEOQ_*7zX;nDQmDM597=8yqlznk7 z+#rTK!TN>LKK0vPkO?^!tGYfh{PQwx2{$;;hXw+o#{4V)o@o7JnX3Pzzv6$kNc=~k zLIc7ZWf|+6KhEdwl_w5PEQknl2TTo9GE7ziZ{5ESq%({Nit}IqJ>FT2iz#C<-kH>9 zZ7#i0)@|N7p)q-r1L{;J^UC?UYp(10rKh8TRyy>yhJWXD>$&^W=lZ>SB=Othg$XEg z5FL%%z9nMPJzPhRIyIGwqaa@*F!II`tmbAv*|$^bO0Q~(jj|aJj5BP6N%o zi>Fh52P_qg$2UE^&NabtBe|(p{jB`_nxYv`c#kx>LN*OSN+N zU4?c;6AYnTgQjgGHWamUI~Jj|bO=J#gpsI+{P2#bjpt${i6FN0W?!+*Po|F(Ep~r^ znlCW6`~{P*dJn~2sE-28TWaVhPubr5OB6wFGHdSr{ylUzA%71gLT*B+enM2v-TrvO ztop}Gd0>sC_EpOG@@K2?m+wHVUHJ=ochwHJueUm~pZw7CElAsk!cgpuF&clLJlcoM z5RfmuLPJGOQ&+|Qje(!|_U>laCSIu5Go16&6C`MR%qhi#y^MTR$a|FuE7KaW!jdVu zQc6y3$b-fjA|zT|iyLgCtE)?+*{ez$14G@qDry0u%fYe=m_L9 zcpCG?q=Z0|3N5rQ75C6%&qtH`V%gd}#f)a{GqGaN!;vg5_;5m_q=-%TK(QnPrSGBM zJR)n3VvZ+adg)`v(iogiMOEgsJRqsAT%F)$7q%>N z+>ypdC#5P+#5I)8tD%Jz_C$CkQ4(v+;XO+*-@Vqfr%y4;NXBbf)IKJp+YrDNXQtxD zPjcXDE`uD{H50-$)3Jxd>X|xN$u3~#ft_j`y+MY-5bs>?@)We6Dr$y%FUB(3ui3I# z7^>}aXe=hA%0I;(8>2ca-1`OXuRv5Kv8h?&2rUu>D9D7L@V+srE z;`vC7L`JG;GbZ`e$0uDdeHVMFNI+5qBQG04|Ejy-g zBlav6v%&NUA^JNO?bO@ZQP|(AT!lFEgBu*fg)=wOA5wiaY#-n~WK#|S`TM7(g1I)Y z{MElhws)Vgzx?^BUlK$3_Zei$(_xyl<)dBB_p!esdMsYJzw(HJx!JOYS=cmMrTh5V zK48AlHI8<>h)vH(Dt}CkO2SPKUCu>*r(ZT(MEJC`EoDeyIjAiZ z4!$#Bv;#Ha|50x!E~2$H@qVM*{HX?6=U`;C_*DY9J?+_ zE_1(oZky$GE>%urwl$tN$r2Q;P6h=-(#J>KqL@4-5)GJp?Lnl!QHTV56UmG?h?t2t z8N0+xSbWmtk1G4%6cSek>wX?&<^~ckAjopL$THKk$l^NQSZr`^P^wN!3f97?2^9l& zo!!HDu5GNryHQMMV&*B02#4$-Kd86@R8@jPjIwC0qR`5yN~0wFF<)(m`Oe--meLR- zQ^9g0Oe9t;I$nX*0sl)jqI6z_x7yg_iIO2oCo`RV(;7kceK2{MG}=Z%q=5WqSafGh zp!GmTD`*RiQDP@S%N*1(9eILhgEc~3nujB!gK^;UZ?|@f%BqT7`F*;dx;_lgxCloE zv)sDk$CT1t^!Ia2yo(vQvLn$!E<}s<-iI>wtXvs#cScn-lpVpte^S&<NYtNP%9=Z+{&Er+rD=2JmitU_vutwn0S4Po2dU$b)6jiBdJ_5VEwz9fT28%;c zk9W8e_B3!WT3Yoz&l)@3uIZ7)GxE z4Xl;;y6~Y|bC|KGj+Bzc?zL66dWH|!>z2pjQuj2bzisLrIDXD?MOOKv{oZumqO&Tt z(~hW<7OR@y^~R0RadKcc}NKI%CiV=eeh%``Vo-RnrvWK(sOydLoK zU$2g-d)ye45;H0P3=L^>a&{%W>(CZNGqYdWEauKGS;tJg%qiCob8E(^&Ltqv)pJgJ z&&ALyxTw~=UZJ1wWa6FTSiq|!=(n^Uh6myUWeNhp4XN3+{UOy#Ftu8-K`^nJ>flFd zrY{FgM8K$1LqQ75sR1Gihk}T(Mj6_MzTTVM8c=aWC@_Nbl|mSZWE8KFmDj4&kDogj zSUoIBdvUaPo-Qjs?4qPLIBoTo}E0mu%O#i zjm2g)0K=|B!>PrQU6C)*{U!S_iH;eR(+_BcTepYExFxn8!O{tLGH>!>zj_IE7r)%$ z?Kj)U{L~DD5_u&9xkDs~GuDvcMA#7<3~M4F-;4 zX{_?jDjL0nedG#Aj2fZRjuBw*dG&M}z$K~y`=~0SC{f_vKrGD^_#{2q!p2xg1IciZ z;6wviQw)Z0Hz~1MKn_K-%}1{7iCGmZyCb`R?p&CxP^!0b{>qsgub#@fpls6(4F0Qt6oWd-ZU(qRseeZ6RRT3Iw%y-mKV?})8V^t>+XKZ0#Gsb%{m&C+Up z{YiPA(cio~45i}`!<+#^hh^P^Ax*|;Uv#Z_fvLAL!yjHjeiP+X&0K}j`c_F-kh6dt(*W7~Cd0 z!!{rP?PE89LfP-8j=XH)`|5V2_sAlez76p+Ax{`9SgVx3_Iv1IRK>q9QHADt#*Y!6r?w zJ5bTiaP7*l{|Znqg@Z$x7oV~vxDJT69J;^p?pH^8117H{G^OIb5#ko3+BjY7nwHaj zt0PiK=(W2l&_CZ%!Nyr& zk;xb^^2gea?J8Y4B6V6KpAUV5{4>)%zR++g|I2XK{|fQHXS$OA+0XV5hAa9vXWGvQ z8}dDIdW4G939a{NblX`04I-%Upx46uQ;Pe{nJ*K9pf?nmI~fadH1*^4-g}b(2>rzC z#1j(IH=l-#O&&7wl>AtIDv5H{5F=QBj8)rADX4*jNMqATF)3Zm41sst%ZI71^f^ed z@k4X+T)1B&GpQ(qLaBD_CLb|`4ZHuwn4wK-^(iT`l{D(B;7B=Cz+M5OEeKs_+(z2v za^=DLy4UYtJk74ad|CLLJpGCAUwdln3G6T`G}oWeH@cHs@7q zZ;{{rJ#XqSrPu5YnVZ%rkVhU*S)AM6sn6cq+}oTU@7p!q;08Ef&9K@xt*``1yTZ(v z%rc{K^2CvW;4I;wa+Z|j@gjog^LHj>_EJal#C3qQ_`di)StH~kQa)IQfO-k@l#<%^?z_se2)nkaRm+p zPBWe7uN31~FEskXR3)9XAlHgFJv&e3NX2J-cgVY#7?_b>+!ly6f_$nIfQU#xA z)62KU z9-k;5Ns8x>h4*lKw`SPB)%zGPMKSuj^&x*-(Xe}F9l#p6%3I3~#%Xiyjwj*-4 z0~Yjnt=EbfR5^w@kvUvtQg^rxvBzS5v7#6s+?%HBy3@SdU!}ZTW!kVhx|rdZMRylS zPGddO{_KC~f7)30WFCU)mud)b&HQbnKg_k(OrbtShyJUPo>I6flvXul0WOo zW2?G$1Uv2>>~5z@7{AQS`WcR|NK6bR_;sX1TdBR4HIPQ|DWOhW7ypB95P59D(C&M? zRyztK7nufK3Uj?YTb74wuIqBT@@h!Q(R7V6Hskn&_zYAT@5l$Z;abhWF*eh-9wum8 z_WpLonUYWAz1wt9i7`t!CUb`e%cm&*bV4YBo( z58L?ql-giN`#~)zhh5Di5A(0|5>v+e9az(x%FcH27o0(St?R>iBxiyBPNoJAbZVz- zS}tavhAJ0kgd+tZjT;&?Bc%%F3vsl#+)G2N?I|@T%6`h|7*kwkGqLte^qR*n0c>>{# z-gTbvExPb@9s2(0T|wq12+Oma8+`3o#BvN+W|Q7o0p`?NLu*jCe4%a&DjmuyCl!0} z)T$0ghCzsXXT$P*~yojBLuRMs-L)E+45g0MNcMtTz>~WZ3Eud|o zf=UioWFpEiNfFa|W_xpfdNm#~s<&6v75(lXw}-{(>=qfJ=7WlEcCAs3Z&jRxGctHA zZmsbixM5%p#!f2}I@{dw5xVdzM2kMSR-8{HvT~QixsE1tq#i1Sp~a*5#|QXg@VbV{ z+l52hbp+qNh+n~mP52NCG@b03k5R zC8cEEGUo2RP-wCS{xX60P~KP3;tdynQ8QG+Bh3&#P#3%$p-jg&JZP~`lZjy-ruMup zxin_e3%MS~+@&N_lp5}Miq9Jn3IW%TuVqgu%fG%ueu!E8J<+ktfppS?F!Jjabc>)f za}Xj8`o>RnXqxrq{a^B2;5Gyqcz=Hxx}X9ABK$AV{~wt6zuR!VRSui@DOl3E({%_z zg)oTn`%0kcqqzPOFmvo_sGCzBbx)~6PT^gT9~qPTAUb1!ALaXwua$Ad zN*U$e)koOD$L}5i{V;&xe4xqwp}C&HY3ai@nL%FV;VEbZrsX$}HXikZ+tp6y-s79L zADxR-ozw#3y)ed)bF32cl&ESj!S^4XVxAeOeEPf7FKw&SRz(G50>^h;7E2H>z+1oV zt^Aj6-1+U2j>#>`fjiS%D82LgZI~_o-o9-HYPu1HwnI>;xUt!d{OlCwqmM6^GNco* z*{HS`_iuLS$Q|%q`rM$pb3Jrm$H`wT^4+4E4ueEd7&{N2QcSYVU3V?;)u*R002cF3_eFPTkdWg8D0NlE3DW8Y&l zLU9lkf8tPHl}rp2GpuEgek$~~Vhi=KV?dlcPe|`3yW84AG4T| z?>>1gRzk%lb(s>@r8GOn<9X419ydKlrh;BfB~LXh?nQvf+c3Fs1c{h-jV`hlKR9C= zznFgMZ)QnZBBWp&3nQiCAWj4!wVxAN0zAT4Wfrklj?4Xq)D?F9+M^wdt}{`YHnBOp zbKaxDALj*|g~Ged`KrVnRM9=l$lNG$tOd97ux9ljHfr-X)pox68%w2U=(bcoe7TO5 zQI^7v~qkOC9lph+Umgo3Oo#A}sib7A3lAmsx47{b#ifMtPr{^E3FN@Dnx2o=3 zK0K0Zj(MT|1o^s4@8G-(#`O1a>UatC%i3UqR#H{Jp#9LOO{~JqZFQB^gNa3VYsxxP zdtyqba^lb`2!*C;yc5UR@9C(w$6Cs~x&IQ)Jv|mm?~<|Y9lLUGjBDjr+ivj;FV${& z)>i#Ph!dL&;DJbXQsWe)MV8f!(}a8LV4>AuA#*)RBRxvoWt2RP4d}d&MphE^Iit@s zQ=^7xY2XTYwqn<gekKI^&oubIG!&M(Ua%z=;PCjAK8WP*cFqgoJZzsP4M z8~$oUsx7G6u+aQmIpAc1J-dp=*ekVHLO=1t>wfADn^aA)&}=8++o`xr*lcWERK6-w zHDoIgG2LU4rZ0t-W@&_`b5B|mi&^~DTH&scMO|Iw1{g;c?D}>#m}vZrV=dchn8!2+ z+Qv8GTIZe{$2hfQAuSh6T+7fxb2uz0%n?+)-LzU-C<}5CX#k7CplPZW{u%53Y#e(1 zgo)6_A*#Y+z6NE-9Bf{3Ib1TSl+kG;W`d(aNY+)<5Vum3Zq+4a9Ms|}*jn0;WCC64Pc1Az`CY0=-k z$5a8Mp&njQt{&nuwl|_^xS}rh< z(#wu{IlD&m3s~${!pJ`S3NM_=xyK-}pyn&Oh^$|V(F+2YB!gTUyrPQIL|pi2e$ECE65#dDJO6vV9H15{cjs1lOB zC^?*8U0M?f<}yYxI}B({nHh1AN$&YvA!~An1b64q-x7xe_c+wwLED2GHOk=SAL!pI zhb^yo3%{$IVx@YHbE!U@lDE;EKLWR4BEXg&hQdUmZ;zv#9@HatIge>B;(iwog{ZTBnlla=sVbuf&Zl_nR7(b-rg z9Cs#mA_^>qksL|9ffWG?>_CfSGLl?|b9Bx;%i*&nSc>sV96|2Ns!^cD!)+3LFN#k#g)ns{t5+U&%Ms}^M73|+A zbWC=7VIOTijqqmt0>=9~FF@Ie5_RS<=8*6W`wp5_0kSict0+sfRDLtNy$cv};X8D6 zi8u-2BrJ(O(rI=>%dq+>sL4Ou_9jF3rBWAdMgne-xyMf(JuN<0Uen)`$M(<9es0W={!<7Cdyoqp$s1~=0VWo7)M2Q_`Crm z`oa}e<}MB-F0%@=Pim~>2T3HQQ{A!KB%cbH{Rwzii0h}n&xs~)G+h&<*(YX6^pV=s z=iXu02VzEU0VUl$ZK+5C>&y56V|tytXc6IdgI|zZm{UBTgU`AKia^r1B=hbN*uCZr%c0{KFd=ZsujjZ?ux22_|-_1O^t2p9#E6B~q%zEOKL{Mp4_~2@Bhs2G?54*u@?wnOT4m3FhA`7miQhSWp_ECr)&nUh}!LD^_-DaYi;4 z7EIO+2I&@VZMks~2k)A9dz3Nt13U1+_DqiN>UIGoMR685eoV{4@BJDUod46Rv~* z;2Yc>fggVa2`16!1Q-I6)rc(qUG(9A9h(~7wDsG~AKJ?4kg04b^vgkT8&TGl2H`ER zEg4PqmkO(Za!%2nxY(#BINrEm8*;tctaEwD!MzRVGRFq9V|8K8te!-YwAt+PDY*jF zj8Qw*)1!e6=cZ7LaKq`$J$yS#!_f@v8~B#@gKXuK(V?!!ulw=>1ok`z|M+w068yZK zHKL3qH71F9Z64_^6qpk#KO5V4b~A#>Qs^W2nW&;I;%nWJFD0yrM^wSl^!HdF4Nidu z%e=#jWYSo4V!xT^i7r+@Vmz3)h>yr>E}@deBd~jL^O$GbF$8L`dx(<K}aSo)AW*O~MMc&DIKo;eE; zmpQTpQE-=efHT$a5)gC6^`LBp8|2FF|H0Thz}D7p>%-kOcWv9YZQHhOW7oEA+vcuq z+jhI#em(cR7w5g_|K%pD$x2q!q-%~j#~9D=0hq{G!M!=ersQ*+ZsJtxBS$-~h`^xU zBG3a~VJcsT885b&cEJYYLzv_T_6nUStVtHnd@F+}-P9+DrI zIsn5g30?!p%oU)QM;Q(a8mNb)$UF)rnpF>WfUrZY0}QuBjQ`gDiLy1N*tGtG(fRjK zK%SKy3=(8%xCo`BtHUnF+_Xi(|M7>@3?86PPjXja2&F5(X)+>OxXQXsxyrgbS5>KO z(mN3aDm&RNW@c_THOr9mP=c;A{SH1R0X~jjXg>|^Q!8{E;9}cs#1Gb+!r)c{JU&Lu ztzQSkpTUA`h&%2M7&u+mLFZTjP)i_tpYROxc4p%VZ(G&CgP^ly3E6* zY`KA{1$@?y_E&kh1M1RSK=%&~AI`EQ{%yoYf{<@n14#UK4c5~nRmP6A+_}li5eh|- zCj3$h|BmJfR%p`C8-?5tA5Jk+MG$U5(K;UryU)s~_S2iw=bL28eq*Fc$=6v}i@mPQ z$mh)Lfs@y6>owe+Yj%$<@sd9{tp|Bugm`CG2jPN(N*gNjtq!qM>f_XcPBt0W=H-_6 zNYw%7kmtK>FEx42u^3r@nlWBssyVNJa$rNqpyxBwsVMHg0zIJHGvNR&aPe6_&!6F2 zm}BNUTQm56;Azu|VG=1e8uSfo2v4+>RV{r1B7-IMPySp8{9O96RuAGXjL`p!`rSNy zz=cxhK5IEb1E8bc>S$e*F{Q6R;?@DY9Th(x7BA-aJ^cYZm=&rb{aT0qho@fMd+q5) z3_9!_fsi-#QH{Vv3t_(}{P8kgw=JL4wcsF^9~m0}2W;O~%+3eB+8dpLA-EkEBwjbz z&d1MMgzYDQ%&yR3)DvN~4-6|_+S&1)))139O22&E4JnT#oxl`JbJCAkosbmV{tevO zm|52qAJ2i{CsFiiUm@N)Zr-r1!RxH%VA~l@mPW?|2FfOTo1v6mAC28;LZ{J!LKrzu zM`8UDfM1SRC0f_~(|uAW$ZK5DfV|UlNV(P&a)cOC_GE=_6-?P%bpsTlHsgw3IDUx% zlg7v{TuS?SHIJ2<>S5A5jSiSPNsOp~x`78tFb6-!94&v2_bf=+x%Y91J)J5m?ut{#oW zReUZ~yW+En!(CwK%dB3vV;MP1daw|2W4g5^>PKe%+#qaGtTR&}$CW=};G@rdn8g29 z|8ZLr4uhW7^E1c;0C&wLfxm%{BD9h|&$EHOjOIExebr?Iozk2>tlRQ`%?i$#ak9|O z%bX>DK;z*`XghIR63)B<4V~ihpTd?7 ze1dD>7F547l6gmZy~(B#F`=$sf<0iaxNtVFZW}ZezI35;UV&6*MH$kTLS8_|X86LE zC8NH}wIN|LF<}j+YK!2W){|D@^5YfV<|oZsj@h1VA$MFzv!K z8LGBZ(&N`oXh3-6cB3>#S)2D7A_<=(ZPz|YcOaGLD^0I-vaP@(kC$&%oYn<0_$Bcb z2N{RKWvo(7MB+ME&e(?^HS`6cJwo%8wXxUJ$2YaNri5^_dKmIT7me(L@LKT&(Tz%H}F0D{FH@c0}ar2*hV4 zOnWnJf9fb<)7>=>BkrEzaFd= zxzn|){KI|-1ONc{-$QFswx<8Z%m0<|ZaXK3G}4nYLQz9MY$uh9m<1`U8f;5X5^Mwk zj|*W!@?MpgQ7vhnhZOY{?)wX4Xb|@g(4T_H<7OBHwT9U2Z?6RQoO=r2&(AlQ9XQzp zu^kh@6gx`)^->b~Kq?{aP)>o3Bs)C*xEa0Bm=aJ|^c9GKHO2vkjbrG#Gx5t*9c#~C z^m^@qy_%8%9@nih?*ti^j^^U@k#a+DPPWLllHs7dg(ht6S!`!Lhr@z`Xps&1_U3BG zk|8)|>#RJv%j_~-r6DD1?bEhs{Zr~VIgGnep~Ws}%AZO(e(FHM!vK zW>FnpNBi>3Bdx_#2<0gu57L7;pt3awsigs|8nPhvnQ6GTC8kz9l&jU4gS@vpG_M;* zJ|)`a^b6Aa17arkbQNj8&{rh$0eVT?WRyc7$cIni6M`hg2k$Pa5}ZY>no#17!C-|% z0-k;Pt}`qdj7wV1JZnV&U#}ZFRsEHdASdomu$g!83PUR}gz;PrjbDSKU9wCww;ep^ zj~8Wtsn?xE*yx^=9;!Ubpl%ubcc_yMtgHcKiK~L~9~uQTh7VKkCy{(9uBK|5zf>V~ z2*ox7$9-0?vSD`w*1xBi>}FAo1xYvR&XhUmISY_8-CYp8D}^sSh2FgI{^GPnJUb!<{nOTy(0iZ)#rCY;+H`JYU<>l;lSM#&7(Eg6l;l6^}2|z6z5d9q}d6CwG&_ z+l#Br#TYzS3g@+w=J-zIxH8^@>I=|0RKY%>R|O6$EB!EmHSOK`AW!mQ&HOt?DTi+R zBs_;eMZL2I;nioOoKpJc&XBqE0*(bE?P?I4dMzx{*L?O`65AL4^>#}S&vR19V%Qy5 zsr)V`sO#+ER(y8U>OOX7slJ(rib;ur7sgY%tOo)Vp|j6NG7OJDQc=(jo^(+)aX^u~k!yL=7&U^A=1Sb_7jZ|ng7f{+RXEp(CNnyzZbP2U=s8g) z+$u{efG`(0oE~>CmI=^H>SG#)GwEVS*U*y+5!Ky5)59kW)|0SPBvUNBQQkwe(&xWitYBBIS^b07@gud1z97M}3~EN1OCDCHGwWvvJhnKk;r)R z0T}dbRr$nAX>~OU3Hm|3-!kfjsQI51$Sw)lCcVzI=8L~#!4c&{NC%REU(nUC=9lt@Qe^8F=Mj2W*{uDvl zj@;9v_rlzUKc*GE-6ZQKCDm2A^+x8Ev$JY%tVSi39%-6v3b#zA0?}BihxW`b<&54X zV{>-*v2yURa5mSs@Od1wvaxX1x98z>ROk143-(c*Mslu*RnPrVL07(WBQ)xuwds)Z zXfPyaXJq5^6jl~C^j1a)qB)HkMLbellgJ`Gz-pMx5R)MsNJ0>ko_wmKFq4g?r2>~u zc39@(wAL7zHg=S*PkUx5EcgfN#dwp&7~3j%116#Ly+qOlf4^gFqyEuhwU*Jby@P(Z zl%>pkezxwwXL;|^tk3TGzAoL$_?+C=q;YvtU}#C$)#--1>t|<}-L92)4KfJzWTR6l zUVAa;a3qb8$UW0}1hz}rAf1(O(HO24$eeORr5?-c(M4Avo2HRY)yfcMdjo$M*4vyQ zb!Q`&m)pD@R+pYsI>>-M^24h{be&F}v@2)A`aA36faQ9%lIePrJqV;BSKY|j!cx2Z z&zCT^Y$%c?78Xg?s50v1TCA9(*u%PlSQui-sep<1%tx@_)B}@LlcuoX>L*(D5sw7j zHPZXW#oGLlA|q+|F(03St7b~RVhCe_P(|TgHor+Iy>(%tenY?%xG4>Q*~<@6Vvu|v za4+992A9xP;76G29CRf!{{eSp;sVQ3ZATw+8=^Xb(Hw{oJ|=x3M;|qNNvjmOb%g1G zJ56aV*!ja*V^?=eiQKb97pT5R^4WP@!H^;uS9-?s4^;TRZE9htX$m+(ZeJ% z_*4;@+P{6{3gdd49$YTurMltF!paB3ykU43I5ixhs?Ufyn$aBYYv!hnKo_pPlx_5B z5KxpvmnAghu|=^-kUFR-FP0OfXR>UAcHRjO+cP;nIxyOIWWlwyusGa>aW2tZd1i9R zUK3BaH#SCz=A-G#K}LQmXJd}v8fcnN4}%yH;R1vb zHGEEmee)pe6{_Cc3{C9^Xg1?hW+S=+V>tFlF*O^Ohm0cZ#76N;>Roy)v!zTl-;;1~ zk%DgpglRdXpZ?TiV|TXa1XzzSvv}(qUm!Fb+u#Bip_{%aJ7w$YU7idRwgP}$AD6?3 zSM%1IX6?mz$2uf>T18;t?w@sKB2Voq!HiX8pAkpXPx0XjxWVD(7rsio&<(Ri_}}*S z?k^y1rlN@z=?ZENjKTK<@)ijMxr2XX7bSGN=!p~g6XTK4p|AX*gy%_)RU$-XgoDq{D&edOtM`1#ah zPHtb$2z5kNVRQFN3`U#t(ar;IH`RzNkWE5F7GHWsaHYQ%bqyKUiMw$D|6Ods{>lYhrVQ6hvI3jaqrn%5w zAnsG&H52g-7NYCcK=PgSLLH178pM`8t?Qf2Osue+_7E@!rxk8S zAzSVawk`yM{4I<(4zO}JJJObjL5V-mjEi5vrmxV7pVi(QQTAA(V1`#l_3x*zRNheC z&-9<*9`qqGH$q^qX(NDjnMIwU#I)&g9B=Sco+s-E#IUhElGfxc)lPq`kbzwJ85HLmGYR(_vcH0So3HYqa38r!7u5QcYkt3;!oAd&QM-8j9uaKA z7w_vW;^DwrLqCJ!Rvj9Ei6KQtN0UsoH;XJxSlMsf`Yj>5X$hOHk7Z@g=C531z@$TP zORK)?D!%hYoQ)_#GJk7?99V;w-X77M<-~PZ#Zh#!f9k166YNSv&EGXBsz$0aYjpL^ z+(IKJl!+G{Qb5S_*)!^gO?o#h^X=35ml0Z&il(BbGSVlDI2%6JSQnF+ zW?@s1rUI=PaU%s15i%e#c#+N-ekMssu;bpS_z&C1Hw|4Z)3ZR^pHpm83n_HJBfXzR z%eG|*4wlA@>Yvsuy*)3RdYYDHKHuJBcz<+;+IpW16$X&wp3$8SI7?Bc-u4kj*}mrL zsmKs0bmZ+=gE&GSd7JeYqRO+=h}Dq|N#iO}iMv(8kGqw?Q>rEHC2t%QqgwK840kAW zk`BEiyzvuW?FfRT2RQpTuV`4gdwfpq&Gi!uJxCp(L^)=xc~d9OO$d=4tpulmLorFK zn+(rNnF>o9JNv&u3@~L{0#^6-hWmMrt>rekPtiS^xmaqqq%=Jy(gdp8Q#a+W24|v1 z*^rtW0S6ybal%Witcgg#TCZzxRITT&*bL9MpjbyBj?6GNq>HyqBCR2|E1n{=;gS_v zs^y^*7KMO8&Q}^13fya?pLYh28lJ2r`}II$($A}x><~!N)lCul8tHqGR+nH8Fq}GW z&by+EH6X51Z#s>!Yp886?EjQ^9v1eGj{hKxwy}&RPT)=A8B@2B7Ia?&j1nHCX-Jk* z!5K)QVShYDc&5kHKPB7uWc|QBE;#%_`YrdiZX5Q4p(oV0kXbT`JT-On-b?LHO={Zr z@DI%{QQ{&?DQ^u$1=fgpPFrLUzbeA3HUQGvmXCn&uP#y25b3NS@GpcE9JZ;EcksX3 zA55t)Hnch=o~j;Gls1W42)2RJN^Q0tzuJ^JGqD|;V>vnJuGYNPK5|eVBDoTeQ>X(` zBrz%z+b0BR4u{49QAd8xt5_NSNh@*`nwuM-jf}gGh@7*>h@7+UA5MEy6i}n&6=e$y zD!ZisNS&0T#z$QgWo?60L%IHktVIHHuuKCMl(Deejkv+%ZL74`U4qL{r{dw|jLBWqd_=(ISPa+|r4rV*cEnvn&Z41dC{lx_5rd0XXAh}QQU&gmD+)aH+@`xny&p}cjE28nLTL3@)+j! zfo;l}VLy02&^A5g?qx?+dH!Ta^MFQuJrRu!1G8u6eWMSyXPP5~#TDi}RClxgIeAc* z1pPLui>rQqY#Q1K%pNU|NlLAc&=3y4(#V5X0E_+z_No60QnRBPc_gl7(8%M2fP6rs z{{ZKjwkGI=xGL&l-5H*8!$7`h7f303O5D^KZU3-ms?}#n^$T~~ahXn%PM%7p&oybS z$?J!1$&-kV=l$PI6eeJFMB=`Iir4Rb;Qt}X{7dB~Xlr9)ZtCoy|KF=%RD!iEB0t>7 z*ZT2NAWwi_em=n^erE0tBLu86y)rbin3rI+T{7We^oBO`t)e*r{p~N@URdMIF3sG^ z^+8s~2FClGk4vrh_vvX}fTJ6-5Xsb0J(dWpNa!nj-jPWz*5@|&-bn$B2y-r@nI~)B zn+p}zTI~@1T6;4e2AC1Z$g0W566jxBZ{eq!&_$&sh8)%f;>;z~&s~gxK*4!iO832) zx@uM~F=%tT7yD)iG5K2yjO%rQ#KCS&&6BZe&d+7pwky$(&7KSOozEr}h+CIeX<63u z4X^4%h<*N-j0+gm%PeczZQFH`)7kD`R_?O1Lt-qEpx0 zLP=(=rJ;iJmmZ!=P#M=gN=-ZJpBOO6(6c(aHZ(QNXC0c8Z%0=ZQLN4|fxj7{Gkx$s zDQ}sPVwdIiiYKCif4~TDu|4MKCRKCj?unewtU=NJ_zVG12)zwM8hW|RqXpMR>L&7H ze*n_U%(ZMZhB>f8B0dX= z*hXjt)qs<4JOjF3CVknPZw%0gV`1Y1>REss_liH3y}dbw<3SuYUGcQ?pQmh~NA+^Y+;VUat~1>!z=hJ}812t|fL%&6Fw4k_vaLl%5P zaF}0KrvAe`GL@YpmT|#qECE!XTQ;nsjIkQ`z{$2-uKwZ@2%kzWw}ffj5=~v0Q(2V? zAO79<4!;m$do&EO4zVRU4p)ITMVaP!{G0(g;zAMXgTk{gJ=r826SDLO>2>v>ATV;q zS`5P4Re?-@C7y1y<2Hw%LDpk z6&-~5NU<3R7l-(;5UVYfO|%IN!F@3D;*`RvRZ)7G9*m5gAmlD5WOu}MUH`S>dfWJ! z{0&B@N*{cuMxXoxgB}fx{3zJ^< z9z}XHhNqMGvg?N2zH&FBf5?M)DPN#Sg;5Og|0wru-#o*8=I!LXqyz~9i6{|yJw)0_ zi{j3jT#nPCG)D52S+165KRchAq|514-eM$YPimg2%X+16RCArIZtlDbDJO9=_XyMD zoC^b@fUv711vit4&lIo~XncD2uCrfuKH8E``e;Wk&{8k);EWqCUZY4dFLKdmDl2_o zMP+GW-dzpwsUA(^%gsgRdYf#-3OCJUsgmJ`fGQap4~PuIKu)ZT(CxOSpRyUl=$|t1 z@@9CcP9_@rSKUF|;BN%KHC+N7d4VZ(4JNDI)}~sZv2!hs#<)>M(?2^H1`Nah~_taU^n*CbZH+v)kdrHiM?!|KO#%*anDcA zed#~O%=w^jdIN>J!b>@<2;X8ubcCH!LUaV3T0*)*P6lv1xM#U>JO~Lka?P=Kai~qs z)|hDVH@#0tM}OqE%ga*c8vmF(0X!4gj}tZqMuEekF6fS&$@If4oJH9PLW&Ca2CqS! zfkAWlfh!<(6MyR-lrwS$!W1cT&?~9N)lQb(4OtXPysW0aAuCFVGK)qU3A{G5JDcRR z0l*vGOmm7i3SwqTqa#ANOHJHqtXj*J-5DUpWe*|^!LSE7MH;VKN8ppjX3R8gSfnPR za?2F6Xxunau(+jZc-<7%)%3K*{j}AElzPIow3=~#ISC_ByScS)c5RK|nL(TH%;(lK z^u*J*<(dfJ;}Uiev!~7#lDhATnmpSY)w#;Y`=iAW#6`}@HGaXSeT;jsEvDL&Rwu?g zwa+JW;0MPS06x|r$VLq6$(ka8!;gGb1K<%MqGP+vDZWZJpLjKUgN0dK?p3C{D&tcv z?8!@{Tp?UxYWG0JfVo|U^rKmRPEB&^qgnQp(hU_Mp`Hw%ZX8fw*h*4tt04)@@mcJ_ zE;fJG*eg~9`F2+PL4%?p8fN*l|`>hNJhPR@f<$JH}SDGe|xPodBc@ z>*Gnzv5JtD8GN(Z%CmDFt?t%9F3^cpug_(Pj_XoBpS6RydL6+wWw4E%2-C%D)4a@G z7Mm4d{CY9S+M^0d1mLZT+oHVm5%c>in{0}!k>iT1C7#O+0_1Gclk$8$rnAyl`57^B zo9|71ttYuJ?CCDp$oK~e9lPh*aS!gBLQ1$o0w|uluKHCle;NYURgv7Cg;E*M8+;83~Kx>BJqZ=o*mJS9Hxp=bp~uQ+Q%iUB!>h> zOs3rb^x>b}>%7ncd=$S7FEv%w)~kN!oh)w>XYRbU2#{7MtEP=KR`!!n z@c6cm$`qZ86iAb-P2zW?ffg_?Xz?EWLv+Pnv)j_^g>gIsDw>%z=48xXs ztXy*AgZ}XryXSSAq;ZyAo)P&1<{h#o+VX1pS&x;c*LB2ys@g^|Ne^e&u(F($VQFzr2N;Uxpn0XHISA zuG$StIAZ#%^;gdx$;F0uJ&fE3FfcOV5yV(?_06FH)#7uOG>hC+zoVY1>30J3Ep>V)`nJL7 zk-AP2lh7;4f1R`YHyo;x@iS6P1L=R_8g$rKjBniGG z7Wy?lA+#98cwsLqlOX_;2mj}QgJ00aae3PBZO))?g054Gt?|`89P}ud8M2P~c zY2m?A{f&}{PvB%59$#`Yk6F9}LtTVLr4`_vUk1t5EDB5ygR+ri}TnuVxHj)IP*)IkApp`A~+v|BqN+W)Eh{|~%!crx)V;Kr^+pMkH z-VRyWpnOF)zmUX=sW=EW7Sdz15#ID+-r^V11Ir+;p$0yW;Ox4TAr-xrzn_b`k?bky zeItAr-#I&+|GRSkvlRau-}`?TWtEDiE56bAOSC zXcKZ(B?@}6N2NN5qNO?(71~?1N_iSEI}#5>GtgSGfksdS;%*IxVesnmc|!B7!#As( zgkcT^N*WT)relVUBm%nwL7Ks$StYuLd{O9NFq1)*nGAwTTHGTa$A)1vhix>~^ zwI|7g-%^M18t{Wp1E^%KnR)wZ~8RVWvNJrwz|vlMs7BF=)# z!#!W^ejQa>_i{U|rv{Nps!~_x?0z#}RB!+F_*)hdG!fagq+6O|;|V>DK|}OwLHM{7 zc|Q4JDqZH(nqF#j77OTDd%tU=1^eF_*XUDD zLzIL8?i~Il6q-m+m~@v*S2Gf6MH<43mrr3PsXp3Gc@CI9CsQ(oIsNyL`y-30TZ)y2 zYC@-4t+WFJjTIFKG{Ik_q1EU8u@@uFmb&W$L!V4#wKElaN{V~n%%E8S=L#i)yK!!&}msL1A@L^Cvs!?xT_*E3Wy+?&!bM>&BX0zj}N zWsjWwc*VWfRRw=egZ{i2*C%@Q6@@{UL*b;Ww9X^`b!$qP0Sy zC~!r#ku$&SkWCvn zA%wXT{U&rse)rLT(?kEqV~XFw)Y(gt1=pD3_FfE4BEggPx@1S6tDZ0ZScD8*)IFipTitfM{x-f+_9Ia~$WY){ z?tP3Z{DseC&$!T-VRNexl=}yi$sykaFt&Eqqf_>L$NZHPzs|)+crni^~2>p+%^0$d5N?uxWfDg`lerb52rkr$|fC*BhMw(nq9tjW< zVyoq}-AbIbelzit1@;rbH?dVZ4>&;pH95<@;rcru?D+W{vzL1c+X*`pA(KcEsv0J5 z8>+;r?@uE6ZVy`ZD%&AHgeSJFy8&PgBs@pVc#tnfT3K5lV*sXjUg{__>Bb@itc03T zqY?ocs6Ce36GFD9e(^6_ri{W3S%uRcdhX){d6o=%W{9G-wuW=;LYD68tlaYm5QL(>p!s%^L(DaS;O>oUeRK;kuUa~kLY$|&( zd(+mnhx-oK_v;PQFXh%6i<6GnkRzH!%2|(d>!cUjnvoBDg#=J!3L2v*2pgtSQ*Gu z=RCC%>XTs;O!aDy!=X%QiK8w96-@&t*Yed=2*U&LS z0^$6&T~hZC?1Fp>6%{d~fV|qvj(ms2(Ua!9Dg4-@-?flR%5sI9p(hOK^Qdv5}Xb=$>(jo4>I*u7NUC zyw$-D1RDY8JH4QF@IEYTf;JSon$LXTqQLj_Eo^HoZr>5s!0W2;3#ol30_UhcLoGP$ zkgJGZqf;mXnmRac=Q{0!EA1#l)h_iV6jGE9xOGkji}=nk5xH7<(w?_Ql{_mq#X^Ps zDrl19$7P*mtYZXO;`>IfGU<6IfHEoJLRWA?c7mlA2snEJa+2G{F|z9-5Lc$X_M_6I zS7rTj8iq>V>2qDS!$9X$3AkeoqYUrRvZZlu5AXhe&-qj7DINRpJ=$nbm&yJUL zcJ@H|>CqgW{xwFY`cv)wN}Xp%GW9wd!vU)01INOK@s$_sz16F3W2^K@64nUUezH@@ zQJiU(N4T!2=C0~dhUNu;Y&_yVmEn~^nk$dh5N)a%9~XmIbR7Nc8u%miPwioLEmHR* zySN?!T9C0CcZeao2$y3m!0*@y+9t(59hZ=ALbQ%d^GQ)E#qI^ctA?{nKcx$+W2A#j zcLQb5NUIbd)gvB~QWr^1ng{>h?Ow+v4w|%dqIcC-N&%ap_Fz6b`6n}Ti zlkcCu9o78psV=AQ@NEwJpC&!OBKiLjt|$Cu)}#UDa@ZbfDL5^M1T5T#IOtMJZ4M~@ zXh*~47lNRu)o#ag&x>oab^hT7_!}++Tu>Kp?ES&$NgZ=ft z@|%3a9wO!rj!ufs27i70Pfq5L%DKY49NedjCV1fw36Mcf1LIukMiBT~H*#ef1u`|^ zS>3!r3^IrW&|73LfNdaCC%H8HKgW?VdxC6N;*dy^8U1woISrmJ&t9gk4IS(~pI+}j z@q&fnCqtR$5RhjBLdEL&X@l(~du#pHwHPS`dQ<&40f&X%>}7*O-vM#J#po6?Y!?LZ z#%8kSqO^!ie^^+#kQpbo(yAwf6w+F9{5 zxr2E+g=yfXY^^*w^#T)dy*>{ssx02%=D=Iv@JdTqIii;(pCh3`y+{r`Qlv~G#KJ6+ zr-QLYiWxU8f%SEPjUe~u6gi2Y>}jl6O(nUyc^qx33sm-56?`f42*06OBLegREfmbNUvvR#>{W&4DL|NPV+As&($WF)rTOnFv3La3jr4-Hn6zUC4{4}gS4p|j| zXte{N$&J}b9RjH;Wk(fQ8MEm5MeheCL`nuU`LK6JG^(7x%thc4+P}<4YJm2`*J22c zv@7LA`$kj)8W9K8B&?Wg?{7p1U09yEf`82HVE-#!;om=j{^PFv=Zxw2&%3cI$y#>) zTgCC!f_Z)dib)na4Hdu#m6(?wN-ysPJ}QLh6xK=aYKgsA&Fm_COZcMgg&!u7ANCJQ z1XoK%L48~Ry|l+P`}4*&`|+0JdQMOG2Y}pgI4JTwMt$ljskkbA1%8w}3<-)-qB0f3 z!I@9PD0ju48_R&(5GqUqe(T|y$)@uJsaB(vrSrDwFMP-G+sqx7fdi-dcc~=&t}{(w zTCssQmj;uFlFp-e(*|_9ORZHD~t<;{*$w zNUR8S5`2=qbMkY8gr1sJ%pa)y>%Zw3wB3ic9p(>p1~$Nh_L)^oSkM);n2a2>6QF^* zQ3Xp|`{@>v*X7L_axqvuV?75YX!0YdpSNS~reC+(uRqF2o>f6zJr|R)XmP}cltJk# zzZLEYqldM~iCG}86pT_>#t?zcyS5SSAH8u^^lOKVv=I}8A)Q{@;{~|s;l#m*LT`-M zO~*a=9+_J!`icz0&d98HYQxgOZHA9{0~hwqIr_IRoBXV7?yBg;?J^Iw_Y}mh^j;^6 z=U;jHdsQzrr{AWZm=o0JpE7uENgeA?__+QQ5)VTY0?l8w7v%A8xxaY`#{tY?#TCsa zPOV_WZM^s`Qj|afA8>@iRhDK(&Sp}70j`RyUyQ$kuX_#J_V>n2b8p4{#gt6qsS?m=-0u0 zD_Y*Q2(x9pg_p3%c8P^UFocmhWpeovzNNK;JPHra?NwY%WX^09ckLz+dUvRC>Zu(= zE0Rq{;x~uY#ED&tU6>T)#7Tw%8ai&-9Amoh5O$^)1VfT3Kefm=*Pq?2=Wn~J;4I3~ z*>@-M`i4Ha{(pDXzdDhCv5Bq2ceu#EZAI3Kh^k0FHuZM)4Q666NzE%_fqXjP{1tp~ zQ1Gz`Vb+N(D=pG$^NU8yt5)T{dAxaF{ZoyB$z@NPrf)@G1-$w5j;@B_B(;6^#kyDH zZPVPxZPVGFPoIz1wzL3+_PWFB6IuBtIwEL}Sm@{oD8^Jf8UT{5Q@3HMRF0M4D=_E` zD(p+3wNv(r!=OA#^r6zxnUQeKY+Tj~-6J`c$SGNlHTst`!>PT8oP64JwLJ zo0&FdEy@+u>gWQrXTdhK^p&z61G=JYN1H5KCKeg|W9c0j1L*oI77G&T&Z5-HqX=VZ z#!c;28ttj9QSrIsa5}SB8OhDXn$8_FWX#?SWSGHu>Z|1%HI~2`_eAKIXQ46}WVn1C zq4Vx2!Tj@NE9J(=xU22vc3x9-2hp2qjb;foS)&_3k6_Ho%25*KdYbL>qfQ#don@{s zBtLx?%fU}M{>-*8VsnKZ{M-OZKZ2E3>;ko6$FWGD*p9T!CSb=4~c)rOoo5E`K0Ic^_ULF141!8WqUJpg$IH=MuWY`+G@#?Hu#}$j zDKKwbn1(V+u}fexB}_7WjyMn97x-r)1;@-dW1ka*LV~~`ZMXb5jwOa|#_kzpH|1;~ ziM0Z(3(i51hF699k}j_R#YEPp?^MUV~lprsYT9X z&C;nR9aPs;069~kp*WuEUfXSpQ>RR&>8I-|<=)3VsPW4F^3DhBOV6Nm<{%}(LoVbz zXCz2qe&_se*qqX*hi8u%6IS!95}mLi-(R#SvKM_{jFaAOIcxIBVb0D z#mxPNiCzQf@=e5;1EQ@f4{xlXGooG1uw`hnwcHQZLq7i3=x>PAecmrXKu~j`52SO| zuM4u^mx46I<`|*yI_~W;eFi6u51dm-AEW(@z|V9K4!C*wD{)wHI{4e}Yx$lynI|S; zXE2fV%8_->;1VDQXej!4Ogi*7WK5aj-uw@PdJ{y%P__4KNhoh}7HN zTe+&l792&XU2;`=>;_P>=;%@BAP49r&lpXeMrS1>Y4#0|J+jcu^7t0z?)9^Ups(Gfh^lT~da7_I!7SQqo`ayuRhc*HoBNP@sr{-|^8? zZO2pGuK$RS-u}UK!vzE+%OG}2?9bhm2&3fGYLRQRQ|9j-Y$VA}!DbMeL`e#L+sv5= zjj4V3+jU-C*JC8#R*`7i8LXcNK6~z+3=NitB4?Lh^QC_OW$sovcgmRdCXvymBY|-@ ztoIRZB6?q}#u{onCGn>H+{4iFA}o)(%D;-LUnYogL75kPIz`7E<~wT?Er_#ySf|aC zV(OPMl&RHZ+~lEHks$k(dahPU-n%*=RWxi_LmoyHn%Xhs`}=1Z7VzX@sL658PZ~r~ z)3-wXUIRX{mgZLx#p(P9TE1W>*(hvysV0P~9&Kj~vh_DYUCXw2!u+v^jWX6)+e922 z{j!a28HTt%W<)TvR5oDpvGZ2HbW+w{5yIjn=VP345an~xUsRw6M+E0>Yj z%L(l~15e>#g<$DAx#;2NC*lZ!Jgj5+uyjAGo%6HAIU}fGaKp}2Z)gwfjLfCa@MQNm zUXQT+U=H$fAjHv#W5BUVGinxT;W*b`BL}CX-fvd}$ZO!aei6wM4lvTSq1US%r@>b| zHOqrj9@-~x$+*(lL$$zA$oA?3M4-C&!c#q~H_=hl2;2n*%pNDN!M=<)zCx^9IzRus{1_>%iAM{3Q?s zIu~?m^B-?+TrwsWeuO-)?BonmXlc;AmRzV&e%-Hz{5S3_UfzCZXlx032W zT&r`5@e2?Q5v0)Z)gs03?%Z{(bg*=^ie<&oU=0QO;nA0ON})kq=^uX4b*uT)?v6`2 zwMgyt^sjpoc_|NjcyUL18e0u`Gj#jg-i@{xeM{f;`>%s*lDfN-MdsW+>!Zi)m`c6hL;eALmV6u+0aZrzWGeL zICYR@_=fPc)$s3}jn}?$32DP;h@$A-Dh)QEg%wTMGpnZ9g|~Vmf}-KiC~PcId9XNZ zNfy2&CwYf7*;g?iVuUU64A`Gq4f)XA$s!mbc;a*a8f(A3e`wySVO-;*M7dXh*>sRtw$iRxXe?7VPx z)^wzvs)QWJUcB_?N2d^{Z9KKssXr9v`3(mV1I4$q{RMlfp4q-Bxf@St-Pw3Q*Ef!$ z!{NR<=B)=|K&A(zG8TQxik5kFerKk^W(N6`tJ(+C8ka{3yfhI~zuw$buwnXgvJB~x zC)%fCrD})mLbehXLw+LA62K1)!9-)D$dTZJ8+OY7(gHj(3BjTIp;EQ9$l+|UF^9d_ zsI|CwwV*tyG>^V5@L|uh|BTI1`Tte+)lqpQ>DL6;;O+!>cXuZQ*Wm8%Zo%E%-GT=Q z?(V@gK=9!Hz1i9QWroSl=Bso1(0|bP)>~a&UHw!&_x2CeuB}V3o=||vZDIOmtQ3|; zk*wrlvN{Ud&*WQ1VB7LkuIhdpL^7vi;l=0K!xQj@qNGoNv7h!K@d`!pz>*WGS zUQ6jZ%R^w&JQ!>KEM_Fud|U(Go2;H$BO*7DDsdNuP7Ue@%Lk>dHP9Kogwl1SRm7$% zkSjCaNRoy~oWfZ!o6+HK0>CoErUVy-=yaaGEt_qOCd@O7rZhzs7}Lem)^w+$xQ805 zju#fFE^ejJZPwJ>IcaZ>i;K#Vw3C)GgC^9u+kLnyg0wRrc|=z}1hB-oM(x!k!Wy%o z-x?x!e=h3iBw>H^e5PFrLRF_K?VO%^HO6Z8g-2>G0TT$?#creEyEZNs%%JIh(M1Dr zB;8ZpP6SvOPlsZAq%HdXaw{`9W27D{MtEJ!UC=|0lRjzjK5qi*ay4Q&!iC8Wy>SFu zj0d%0Z}HdDWg+miRbxv}A+L9~1Dj{J8-<}3&AcW829ME3Y1&#}8IASgK3pqDUSE;G zlK5hDo2|$(E)%Am^!qm^N`E6Q@Urjhw23il(SP-ri^?H~?^NONQ4L_lZKoOQ423r} zfXTL~Ovzzj(_1-q_UtpZs*&PPfTn@}v5%>ysx4h?s)P+P!7J8jN^aFo*d?EUyh|bQ zx}dY`e#&CQ)ATs|_QcIks`^uHY%prn#{gq=&RgOmJYfo5pF)!@6vfFR?y ztbyN6rcv@u&QZE1zfGVh3ztDrWt|bP3LhjyoAhwMQsWM#Ji}lOjcbxj7p!o>iP(g? zK$IaHQsuqU!(SJ$aQ*;Mvr~ZA(-6!ZQbG6T;A%?&6PqNeosTmjG`QOI^^lE$;ht+b z7HvdkAhXSDm67c4y?v(TviM@(qo8Q5(|c2qU}LiDi~*#f)a15U%_O8;u$1D8jXXc9lF@%iuvg_98C$X8 zRJo*VZ`Ub3f7@%H$=QpJQjE+^0xrqPU65^ZBbhleKw;eKLJ`K7zVVsFGT+4qM?x0O z@Nht4#!zj~y`m+1UitJ1hxJaK?ef+FKX=j*3;)VzJWw{@+RKm=SOqn*gL(zoJ0(UT{WdEIbH*+qvC00ZXDZY`QU!g!N z%~QK0nxz^vYd&h-^|?$)<<`voGx6I@_%25j@DLc)H`;~eZQ?cFsEuLs^n}{|wrAj^ zy=gA0t$}fymYPUOrchB!R4V!#b_XFWNL|D>($kiG;=Cyv4Yqd2_)m6)g7PhGpd!WBg{6Q zW~;u{h29hhq?quBR>qOkz)Jg{CI}e` zT5{7mfPm0AYfHs}K{i1^rbdu*w`MA9P;x$)bK`MQ6pdt?WoqB3kN^~i_BF_X-eQ6eQL8jDbj z3Nv8$vViw4I>Jc_GxXD6EW~BmEKMH4C4J)bzv72n(PnDi+I!ut`K7k3w{(=MP`yKr2H^(skQ@E}M?2&|}yx$wN;7ZjGGeyMYC`pvItQ#GtEatt%w!a5Nxcmjn*KNa4~`M+o!7#-O?m9rje^v{vhdVCwgf-eRi)r{UG}$ zp;ER}Erldqqgo!i@Ne~cRfRA~ge#+%rguKQges=0vi`(igdBvNm_$dsri5;!-w%Ou zJT}O>?(>5Na18KB$DJ{BPI7AD*(Hqg+BsxnK;>dpMdwY!!6piTO1EJgh1*$Npts+7 zTWpfUMfx$ZAK02m0gnlV%3%_uJp0<Gr+VYAu{0+Ep< z4p*;LgH%5@7-+L8Ei6|LYi|`efW>KxsEsp;v4CI-o3N9ZAl@QV>4JVoSMCy-V!9Bf zyn_Gh9J!&R+CCZZ1e5}vfZv)U|GVou>)ILqZH`=_bR>%`kHFKY)pF!igPP;D4xxwG zf&$GlPy~&{Kn#~U!`$iJc%+Wr`04BMT$I=u)Wa6MjBo@ouMZ$mOe0Z!Dph1NYiw*J z#lFz_>+#dW%)_I%ix-_%=ZBA5M7KE%A+%tRvr5ydGh-%JFK$i zB3OA^tlEuC;)otcC(Ydu0@v~{_m6vBT)eA=%1#=&MpkOyT^M=x)Jn471lC16Jgv=(LlX%yQ9n^&IEf6BUR4@%S5)t&5e(hym}=0 zda=G&VJw>Pna;Rm6AuJ~v|ELXYfXElX$Ke1iP~Zw6Wq1!X+46@C2)!6oNicgzu=pE zQOddc=tb*c7mn8Q2V_l==6t%R;RK%jFBaFu8JXtXI7Q);*zby*jX}HZdVL+#X?a9) z-T!k2dvy+di-gKl_?iE9Vk1nTQmH14Y;NPj24m&h%niyu;7lIaI(d;Trd(kb{zOlq zLtI9Px6TD*Of#+zJntaH55X(1YVt}Xz#Br?HNH*JI5~v*T7k|lv1~Q*&k^hpd%ho| zLgXCAsigQ$6(^L5096aN*(QRve`EdEE{|i5Rx=9d@=Jg&&-Oc?g@1JUmr;uZrGG5| zcv;O)%5!2^E1ZG}!(v+-`Vhb(rt6`h)29%g>0^#k@2gKa^<-_pZ-l+?5ZAjoj3UZh zVzsZ9+z@gH1U)&%o3C5zyeqvP!QXa7hBJRPxcIID|CNM#0HKClA8Hs$TT(S9X7e6J zTS9f~)DcPq3L8nA$-xpMal?|4*zVR7yv6|k8>}a4_mp#51jx#5Ic{=3X7K{c=<+;{ z|A|n+o+pcD(8y|y@q+T86^?o2*DtUA-!)LLP^6?Dd<#%5U69qP;9ATnDPx&_3$-*+ zE`;|r?rT#ElWSbw0Kx17F4$f4r$B;J>b^JM4L9pNn>*+cPbU26rnIoZud#}8OvzHs z%#^h%+#+>n!+awM6q;GLRy$*~&qFh?yr4Ihx*SU<`e?wQ6kp#s)TmLRxXzNE02}O8 zVmV5kr*h{dJmc2yV;0_3!D64OEfSkGo3Ul2w(FlZ3^)a3?an|m?x~!DYalgXDxWMM z2_!D1QDIxIKPVurQj%}rI_``LGFbEmQJYq3HvlA8;Ktb}x%8uY2~fhnEXiD;47C^nKf{+nBjMFC0+_PZRT2fQ}T^O)I0*d4o^=L0|b_ z9B)cG1ro+40Qu;0gJ$tl%I`g748+z|j-(UXzB+^968lcpLQ8lw=2Se_3zL7-?rtT_ z?eDP|Iu{0t&Oknq0oobWf4|At89^E;x3#o z$OHE`rXx28)OZt|0qFIUM!ELTWF3K0k*Xj{#`xl z*UMx7C1#TFPV0wy6wgPsk4`c&b*Y=q;S{12Rw(a@iA?xW{GemFZ&)RQjs}dBjmSuz z^FHUx1@hj2+~tKjv%W%vF?GTl%lNdLIn3ky^ziryyN>YQ!=QS!LkO3e-0yQsHR<3ou|Xy7KP4mGJfd5^v!7>w zD++pZ1KCu^N}b;nB1b{1%h8)VicW2BNbM!K7vB5jb8pz2E^+P%<(kCAilPTNGx#CH zJqz8j%NR0h1TRuy-7B!a4v%7!Mu)M0;V~T$<7N8&;qi~q?jNzT1O>o60C3-@;Tz)X zwT6<&Q~i_{X$&bg$wKQ*ss%Io9lU=Vl-Ymr_CAdEm_&8=ysR~H|)lK)cfSrG(@j)$TOctVaY&jrY%Ho zFmIt!e$wa^@SJ$UF6i|A+wzyqcA72n6iDYIAAz;Ea9oDu9y={vRUF)qphxQFnQL{a zyw>bprCbe4=jt@atOj9h%kTm3*(1nar4&NGUl3T@$eMQzy9-B?dJHHOtlBbn82}2J zN1t-#%_>b5Ih^)mRx(AyghuaVfIV~50u{($B zriCS6$G3vGADdtw=P+dA`y{kwWmD$zhax7@unSDma@i}?&M|C1dV~aUI72#RXX`^J zW?ypzfKD?E6q66@q<_DC4U60aPA=D=I}{h8w>@nsY{^@Up~~?2O^g(t?mA4Nm*5hw zsAQ0Tym1{4;Uj9?Gi%V8g$LILGl6-HZm-bEOoR*lElO@CT7?~*DW1RycvKcJ8JCVw z=&0B_T&!4EPRdTRe$VTc^;EyKj5lOV6ZE*F{N3THz86+GK20%QmdpFPqMI!#rpC!K zWm60zlo~zxEwLCY$2^)MSZt<&F?TO=#aqi|7=P#>_yfB5|Hq{F*Q*y9isJxX1e7PE z7DHXjobP!$^?vF(Zw)92#3e)WKS0$WBEx=IEj%iORdX6VPQ0n=7)*n3KLh?i+V{~r z{%q8#LeSid-C;HDy503;$$Isof1GX&2<2>~1K}$ihS_9Iw*I6~5J`P9XQEQ7g?xW# zq*9PC&HjK+8ew7_ z=#=9Xh#Y4`t-A*iH)0c>klws4b(ICoS|enmnr&Oqms8=DhLKbnnJzq-qRP}Zv`lN) z=G6pAST~ww`RQhl9r1MNX*Ahxi#Jj$F}GTrTS2p-p`Pg3aoU844?^=Wko~KVtL2*J zbt*iyW&$N#xmah{!z%8=90`O4^B4$;2luzVu`L11&p?<#SBBk)0tz2$FX>80`4_+9 zlQgyjE)>4&YhSuBn}aE_Vp*BBlE9TD@HGIItEtrY-*9~&X}F>BDbkvw9d^59mIrUz z6QOh~50o_8NL*`owA!}YwB=nn4O+JgT|EZg)n}+wj3qm)PTiXz6D*^~Px{E0Wrs@dqn?RqXU-v^+fKU!7h{t4^fY@Mfy|owlE*#89C~B)yWaFEB z^{V9xQQgA*>|~`Sk;k7QC*#eS#uxjYOv1|gc0u=HT0}Yox9nL{kE|!54l#z2{^*^p z$H=@M8WRcrX{#UnGqqM^QFTr z>~c18jbF)0ft~y`F$=fcizTmRK1V#&XTJFrBDpXqX{WR5CAe=K~bm zYz67LIwwfVop|=~w8QT!@5t|X-6dCa2p*7gxGm+30X*aCMYQ5 zY=;y|g4bB#k4TR}5?XTvZ{KzBJ5wFVsf^xMDw>?wx^HO(#5UHxVhxiB{zB zFlv5E-pH(18Zt2Mu7`OhIU)-hg*?Z{Yd(>8yT=4Xt*Tz%11fq)SI84B{M|9aOl%72 zYzz_o)HXg-fjp|xUqHG*IWO3$eiw~ieSEcrO$Bc8WK)02=1{Yp$J(yhReWcj@VQS6jiKP*j!U(x9 zwaLJ!#HLhYUw%c(_IH%53zjVA%xt70o`|hRnak-a3xFpnGckkHUoa=zpCh zZ0pUEZ2-EJ6<~dh?{~VDl9l;Ctgf{w4Zr&_W8fJi)@9^}L^ul!AsGrN0-LR+x|Jsd8c~qMcH`^n@zQmA zyXW!f_Tx$83DCB!h5+mqG$;L}Kv_C{T-SDQXS|>3h_Ee7s5z|Nm#s{^UL2tZMCaj_ zPo%)G-$0h;Rt&?EhTT$h^?Ge1(l@^67VJVNrf4`xl31auNNZGWihf%^hb275f*njS zegGR+TV}O0&oo~I$L)m)Rt?(78{w6!iOeF10h?xR69MP(Ot0Y(aPKvq!|WQCjR`$K zqbN(5Dm>=>nwChby^YdTKc=N{=&!TjZWb#JB6qmka6aYLw38C~n3PTvZ-bPaxn{Vx z>Zz@57a=Lp$n%aZ<4bn6zCzGJ#kZx^*l2gg4AVxrP<{NVRnu&%rEmuAtv7Z-C*#P&5i$j?%ljf$JHP?}*~Lp3F6mbySnI z((Ui{A)@PQcmnDU@wygo@V0R|qoaw^{G^$l5E<`513g9A?)`YLP>c4Y%aC+{jDfsK zXbqkuH7RbXNJD5^A9O@+HV)cb?|xEl%~FQj|mTZ3QNW~@iB_A>p_LGOqy8~F~OI&`%aigq`Dy2 z^QEdK7D-9@n>ZaUgeG=A!G2gWYa%Wm&=SYHSqOYSh0ziv)b0fST?|o>41Mu?&M>9E zlkfnBESfOc@7*XL^wG>zAN0pInU!2Wa3kqi7}@faKfKtB6>2F zjsKWdXQ;urD9+YvQ=PNN0gQ%Xfc&|M;0N_%fdqX{8HE+&LFplbf?dRAV|@pulT(1? zi*sivFXhW}bv#u{DwIVeLgdRUPV_9xJXd%vPL3{DHJ041-Iv_VHTFMWrKF5Vzb3uf z+B)QMuWFlHJUBb4cV2zCX+{=i4wL&j_4>~H_CbUfe{i=7>yakuNf!TLJ4b=@NN1|# zgW48OhJ&dVC+6YYmu~HpIp!jDRnx?HCtFNA*Pyr3D4`OZTHSG;n$&NM2aQ9+r7zEzO$MhuJsSF$ z9H8mLwvi&F982}CY*XrXzC#U!Lf&7p=~v(Mf`lT4XI&M5KT zq)43OJumv62Vqt8stDHmbg=`Mf~W%)tLS4&#OB3*bKw&yk7e@D^JX3;vMP{Uj+z8* zmz$wJ7rmJu5A?#zX@0j70W9DEoNz1W``1gl;%EdzrOm(PjM3}MYTF&X+SY8lN8 zMTc<@3}bY7ML3u3J{rh6ylW7uI9A=9$5A(LtoBa&sA zSy(C!VOc2$O1b2rr6Ik=mmykB;7l+ha+EJh_{)~{#3Q{u*wr8`nHzK?C=IF^@?~EX z+kH^T;jtHM{bMLu>Ugnw=vA{AWCSTn6Eo4nQ#6FosE@T!U?H}ok~K*R4w9E0W6-2n zVd}A3I2+U_>jfd@sosnlnPgzX4W0C4bFJb9U@7qGS~nOAdq_xD1xOOn@wrD2PE$xF ze@(E!vFM$$kPr2iO69j1Fvq)r>U?bhlrikgrZMQ#gZDKlU%tYJw6=TW1528c#ZOKlYxWLIsDi#aAX9#W>#7OuFMoo%?_{MdLk4vR%ySNre$;K05} zF(_ql@Y`E;u>#@gz}hO|%7kqi!Pq0R7RyG=(9SJF$`~>N_N*2jc6TJ%B&gKDSpKR# zjFT0Uq57R1DR07pg5SFp>5LUHe1wy|C~_}s_=t>XWsHin7Ggkfu_s>F8%i2CfQMQS zWL+_YIvDf7T(1nSpIc)7X%=o_!8E9aU`9W1Oa8WP*(!`N#x)fyQ7NXf2{bz|Xn;Rm z2=^QNfPt--9R~9oruZPcOoVdZxmn#~qtsMOf&SBs#QL1+Xc~vbplOD!Cb#2>{jrTI#D-#GOHVCgl-ksU{tUszSLNL7q&3UM{@RJDd3s0>s}11^nD z^$nqNeQ-#1(xV|w$`tsF25+}OZ=f~e-jSf7b-05_ntV4@ zWE5sk?mG+&2lN%o34xaBY`O_c@D%}P#t6CZ+Ow!9hoRktiC=WXCfKbe;G2fCyIYa* z-QMzE10g`Ly5wM*_mkRga_y1BIGeUEty{HEWe4vw6mI53`U@P!^kKa>JjGk3g5`UY zRhCj3%zcG(pswZ_(RUBqo>(>Q^0_l>=K$^rXALNQIFiQSdK)CfKNQ-ZZ=4MvwnxF- z_6<#qZ40Bgc){g%b94uMtqTISJ>j#?spW%+zx6H`kO_&DegRZyZ-OEC+8{*W9s64A77(w8SpD(0sz^bIkUx`nwP$Rs z*UJz4`KK7cee}U@lKtTLnKY{(&dcv}=CU#HO!rbnqN2?hHtG4HRC=e}cLhw1k_gdJ zD-K3xFDzd~a@M`13o8Gp&{tU-#&EoSa;D4r6LQV->sxBW3PmBEo=CRG`!)L;;T<0t z7T0%g!2R!UT_IB{TQ7itDU>y-VPJU)P1*Y}BUrrT_dfd)kyMC+EHvD>^DMz(C;;Zgq)btTJ|F%u&7rIMWg$W^4avXkr>g!76+Y*h#fC((R8h8t@#u^J|{i?fyRQJG#f#{m9;mNC9}LE8A9^?DBEW zVkI>`w+R|=|CX=DIcP&XRhYn+s|HYt2WAT1sIs1NJRmH8JA1$ocRfn|Hl zbGLm_DM#Jp0YUAO0RN%Pf_&81bHJC1^tOf&bw(C+N0jf`T~L~qt@^OaS8Ok{{aYq+ zmH9-I;yF>*ZgGvSm7Ckdwg#6BC;+IAIIdZR>T!O2coHisQaDQZ zUyOR?FJX(TmQWQ2keJVd%55}SwE`(%qtT(*gu5glzETZsvnGalRkD_hj5&q!6m`gg zz$i^M+ho2;Ud)ZD9J>^V(MWy`_kEktmQ8*K$?pzd>ACOl zlPfScddrpjMzgZ)8>3OMvie!pnR6gYB|tC2(?=ecvQKoq4ArWE(ZYbPsu7*WVO=w8 zn~gFe?O_x$c}lO>Pri)A5gr+IuPb0K+(xPKTu$6A_;culTAhDt$bi&Vfr}`enAJ(o zg~;q@+-KVul}Gfs?BTiiOt2xlcZB~hUUp`6E!~9)X3Pzq&n^IJQWzr zVO5cdCKM6*_WQgSuxaVXMGzq3ZWJdN%@ZuCLo02}n;2(6 zTY}=G>Om*K)n$254w*>weMYee1Z|)82tyXc;HQ%qjLkFhitUDnqNWG%ur3utD^&Iy zDI=7uLX~KF1f%qxAn$6As@9*oFEE+|N)8Av#zC;1`F7YY6$BK%eBAz)Cs?S>nU^Fw zf2|;|pyuOlDlO!SAJIG8f8=~U$zCYr@y^Yw(0bwqOD=G2TF4l0lk6e03yO#N3}NSb zI-gXHvv~t@Eo@^GkMjT_0-|6IWRrr2xxVk<`f7q1;qXutK@oR4K~tcHl> zMvxU>=O1o%+660UI&)#Fixp`&r6yZ=px#wqy0=oa42qQ;(xdve;LHS5RAm95D)xq{ z0_S2?SuC9#)<$cQU0PJV(~Wl7DQL5jbpyeokYH$ofxmh(lB`%~~(jFVZ! z_{l*IM{x1PiIf$3>BK9{%%$~`F`6ONI3+&e^BSs$SkKYoNhY;#P>F7#JIg_U)vxWD zVKEa5hd~JyHU{s2LimCtg#97IbF4@Y?vJ^_Um=JyH7PSA-vO;fFh{aZD)zY0Xvv~a zqNz)%M1SyJGNp1z^(T12Q9be>HzX?8{-27QtUDjG5 z_6=V*Gk9f6}LAT1j`OT_C+`g?FaGO}P1!JKAQ+H+{ zEo%n2slwjD1@S(P&=_AJYV(9yS?Z;Ll~t~aWYzR^_H?#?+gxzQ(y1=*cIe^9K9Zz?eadMLs*&-- zZmY{~Z_U{hu3u6*qWF%|j4vpO=4v$W0y4Nqz?0(RmWd*rs#>gnJCZ@ATQ3D+S! zS0T(ZnY#u{#Cgh7kks!Qk9Bnbht@GLk2zrFB$iiT2X6bVL7^z^SCe+hxmjbu`?STj zD&*!fK;1}J>=bPQ0 zZ`bfL-CKn?V3V1a2%b7bY;^?jV`Joocc2qXnl8<46msCMaa^5~+5kEJfQ`f=1wt1R zU@3l5bf`ly=p?~UU&PmEAz_eBu|-pl1ydyxSKupT2`-+%UR~J-Ox{B#tq}(B3Ql-P zlc^Oo0)1H9@Ni4pop8R@yu+KHyl#$I-O#$AU6bV7R@v)+;Cu{_^OHhaeVwbvPN5?* z50p$|U{83@;0DvmBK|p}UC8zUBmiA(aX6)6@2p?HW|I500P zxp$_vuoDa5P0ze-VKpr4#eKxZai+ej@O#0Kx0+rlUc!8$NH@1?cTmhWlNRj|i>snm zhlgNyC6Y`MsT?MjJl=^@=es~k8gq2?M&~YXdbfD;3ux(vKiusmndCrd&B&>Aq!_ii zOWc}o(`bIIEsts_L?>nDkx!m+A;l|P1{!<#dijduP(6Paxb^`uvmU&o;N6t+g)b?Q zJ#jwTMAa+2=hxY;`26Qt2Z>=7w923fgh?Ljc%w^an?~U zHlX`HFZE^O0%JPIIS7=S{H^Q!P({j53EIc}NUv65U~%YXnSs~%CQa^`2p)w}<-C0@ zd2@&NtjUR%PrRw>E|!@I-R z4e5QB`s}QFI7B;@f&SbnZ#Q;I{EYuNsmlN_#CUjFG*eNmK8g^*=kIj!7De@#SI}yn zNl_VtOZLo|{GzUu5Ii)%YG+Ah;&vj=IQW za_!e|JfU6j(ByyB?AU^KR<6GgMa6#|B&wc_X@De7jJA8)F;uUfhpk{rT)kj zQl)A3L_>}s;t7|Muq{#MwfGf@u9Q_8h7Hz0f40&AU)NCfTXU1uhUz!A+Dqc~p61lG&s6NFJ^CkNfn99Ln zxW)IWfx0+B9pL=VYJM@9HU~Ca);w)h6hnZA&6a3R_Nmqpj7v9BaKyy7<1{fc*0Tbu08BQ3W#o`80kIHht7t|bEsU-Jk@MXTPSpsNjMzB+W zJ1?*Jkg?|`xT2tOxjI1iX}mV4RIS$V?;NXKf=oK|YzY6(<3#ZKihRZv^~ zoee!yIg4v<5^*1ujFn$QHfx z2V!BrjDzva25_O{@o-BxY&dgek_h(cdz%K#R#&nK{{^sVb;S=1C=(5GUi1TZqq&L0 zsq(7$9ufW)=Vc_k)>sXtVSCP?Jp_;z@TvK*t>k+P=nmxMBZ^xKTduOy8!kEY+LZD( zQuy$vDrRKf!eY^AxbRT^nt`W;m0$Lr?g-|CS<8Q%5E9?=h7%5T`!M^^8yvUBegdO# z#?EQhfL!Ab(2LhQ1mAXKkgW;S+XRn&G=EDhy*pnm)1{Q2A02zDVv*Gxq5Q25P7K_N zs4d8y7*_04Zl<=Vc%?&-s{s%x<6HoaN{V6{ml^0;l&UwskZ&oJ#TOU%!-!w zNE@$Z#ria#g5UV@1b-0{{GJ?f><00{0?9050>yUYukQ#`l0$m(59F!5nQRojJX@)%-W+G{BPTtg$?_I zuBg}vG1!E>yUMQ zWeVln`N`06$e3t#G5}f36b*wBEE7FqATQh zm$k)^2%<5DmzrzQ9gI@<@3eqX*95>s`UU8LR)m;aL65E04MpR%R#QwonHj2&t%so0 zrPC>kred>bh;E#mxTeMJ@}c^7QPgoId%lF-lpEi}jbFX>wsg9?jH@WaZ(*zs(hOOm zkZ;ty2<`!W+;!WtV&Lf}yro`ojcn{VPrs+TIX>DX_gtVT1a<$cEG^VNEEJhXBt!yX zf9Czy1>CrdR@7F&0xkhy4-EC+7jXafUjJi@-yd)H2nCIQZFy;Eq&Xrg&_od+N6(=d z3Po>yTL#KNXxftx?r$x`r55yKe-{m+H}p7Z`%U%-$!KBEA0EJmv;`;<9w`|d_ZcT1 zYaC3UpFN&m=^#>37`%NeFHPtt2!BVPmAexZnkGS=AMKObM?+0&tKoH0+(h;Hdb>7% zvpp078p(ac!d69~uy*(=dG&ihiAul$4b@%=bhn=N@CLL|i&v80$3beLD!0h$@Eyhi zV#zKfZ8ZVr_X~;$8ubV9%PNRy-jik)_PeM{tQ4^o3oJ%fjA8@!7~!s5e(~E>4f=aQ z-QP&(%?l^qGxqOXDt(&NQPz5A$;_jxp-5|LW37PomOhy-JxLf(7C|_j$JZe>od>!U+>g+tvSpQNq-@D*m&yI}t8-1`A|XD^Dvix)A1&w_yRTd# z)$Tc-8L0;Z6)5q{TtH*FvAQH&D<>IwCYfD*9H7*@^jo-BWLe_Rgu4|eOs<~$T!Ret z-IL~vgOkQ0gN{R}R>9gdiV}jT#A;SK?g$bb#7uRx{Gp!*+snGN%$eIfrKi#cC;W4L z2Wh9AePj_~iDcc)I4Y7T-igLL@fW&47Py2D%n|0kN4!7GtD2x(BP4$#%JHUd8koCM zZ)O+2yFR)M(+=RWL+ItRs!!Zd`;9P`FYG-6mmoZ*Cw`Cu*~T8?6yk&5Rf(2uGP9pq ziDF*XO@E0X9y0E1(&B7C1>RZNfkW)`X6$7=^#(){SL~Lq-9$7_FDV16x{D~HsY)F2 zx!7LBx}!7I*Jx6XH|=lnvA++lFdKPbIv5M}y(6c>zF3d-11YY7H+axb>brd%@ui`Y z13%&U#ZIs=0Tv4nz)n{fz)n}rUpxhN)@FwK4!y3OkRW32cwGdY#gJm!*2-LHr3MuWwt0(d;lv0KT;VtUp{dA7C3#UTs6S^v( zs~Qb{G;CLkuQdr`6v0P0PLN-a5urUr#Z}Cm1EdvN(yNz|2tVV2YgJmQ&9jZEOL2~T z)|V7MUl`fT#6XBtf9Kjzlzd>nbQZXx{N0ypQ9O%^<|doM-zU(j&RikrjlP|uwCd%J zv5Cj@ykJm3gjvO9hv>+a+TIu33gNw!y|Ji0l6mQyWs-R0Iq*oNv&g_m9LnJLABuO{ z_%7!{ILV2ExqTM{^t>f!Bd(y(aVskpLLI&v9cWWZT{q3*La)^q!l^2)o?GZnIgj<_RN4&Q$(nsif^6CN-kfd zw8Q~%rTn<34}j5)lYj7&N$xGJgQ2ZP@cj6`ONP$JNymdygr zp7Qi+pAPvfn58-}TrLy!*Gv$)1e0yZ%VLC>;9AEmGuUEbPR(ozM4`yQEZBy6(AJ)V zO=8)TbN5jWqB6m54II&at_`&fUaIco6!tdKI&6lt)u2+!)NnV7sxE`Mp_iZIjfBAz zvw=i_^To1pdfxV{p!jaRlC-Qe@v5!p!)N9YI5KmosGqEctC+U$HUXqL8qcKUS|PAM z^s|&KX=T%j`l8IlezvcM;J93u9|ry~mb+Ptl|qS}V1G}?5BThblBE2qU-Q}!mCD|K zh>D>ddKUDU*ru@kqRxl)b507K0}a?HbuL$l3E(ent~zunulb?+Gw5GmR`Ac=Dky+k z3D`36FNf`a>)8V~qyMv({mx4Tdq_w~An9g7DFDv@6%4?co};OS0gauZzM-j&!=F|0 zrD!O}M#j&nMr9;vYFXx(W@#knSbzcF$Pkb=x83VMu0;bJZ>3%VqW}S_2*3vd5&#@O z74iYfZ!e0Bh@t?Egsdn)7w@l^IYD*0{n$;V2snQH-k;@%y6pd5CL8|ROI`Og&qX`nxqcEI_MEB-C*|4&o^g@r$r{l8xL zZ`*;tF`u}pC!GK)ISXi^A9iFv3l8A%{S)(l00gbA9e#KP*vRObS^-ioe>w!btec6S zfl(d+Zx(R8`H2fSQwC)H`~q4Sp!{HAt!wZft-+UoL)M)3@PKCG2h^AOFMynY;K)A# z0$v_2t^$q@CIC%lQ~jT+CodT~(n2>N0Vd5j0MiD-zc8c$+UFk_{+N@!gqxA2Tgd^y z3;_;?zrbyx|05irzQ%Tj_V&^MGjKzz|5z}*gx6mr zqqcCg2WY^EnpzkN=<5R*WOS``jsF_~Xo=g3CZNIP0S*4w&JmCQO9C-FU4Rp(5AQu`4h!Rj!zy_>86)vKGfK~x?Jb>%xkG}V7+}%S}`%(bf z65s#;{i%=ue!(x=MB+ca?$>x3Wf(UzfHr0Ycx?O?51#hdcvkifx)v7ytq+4+;-}&Q zp43CYU_$Vx+5sLBmVd(gb?pjV>06WmHwXyu0Rgxpe=1($zeJO^HvX@7`=wv~Pc%fp zSpAEp`z`nSm!0;d7y3^YY?=Sf^6O@J=^6VIQw%VQ|DxtE=O2G@kbPO>myV4;(aF?) ziT>|S`V0TYm(VW_^L|2uX#NxQU+wc=qP}#V`H6~P2+&FY*E9N$J~S@@e*paGWk1Rf zubH348UXmG_WhBW_VVJF&NDwR&iwnu|1tmg?-Rn8@Gsp&e!^3j{H<>Pf&ZP4iI+q# z)&GAIZCd<|=uh?kFJ1sI;a|$w|Acq3`X~4o^W~SYFV)+B!Y)|<6YQTu4KFcYY6t(s ztaSV*%s=+g{hEUy)Uc(Qh4+y5xv{*68+IU|CS+rN$^tT@h1VX z=Wh`FgXZH)rk7f9KbcH?e}n0_l;K`-zEt%3$%z*58=U{7@AZ=Er8LM-D)#W-p!x@) zke5s^B^Z7(aYp?H(;wYI;Fp37FR5OpzW=16iT!OV!1!YGXZgODBrmgvf0D>0{5HuS z&+DJ$RbH~ZOjG^IBAxWxEPqZ~eM#^#N$@8D9pF>y#f#@pWA494nm=yKuTutJQoYR4 z`bmYI@f%eCv#nkx>-@yG%lZxce@@+b`D0$@HvA;3&i&tHzn)~hT!j9KsZswo%zrh< z-k`%VA= literal 0 HcmV?d00001 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..cb2eab2 --- /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 + * @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 + * @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 == true) { + 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 + * @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 == true; + } + + /** + * 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 + * @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 (i != element.length() && current.isTerminal == true) { + lastElement = current; + indexLastElement = i; + } + + if (current.nodes[index] != null) { + current = current.nodes[index]; + } else { + return false; + } + } + + if (current.isTerminal == true) { + 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 + * @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 + * @throws IOException + */ + 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 + * @throws IOException + * @throws ClassNotFoundException + */ + 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..d5fdac3 --- /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 { + + 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 x) { + } catch (DirectoryNotEmptyException x) { + System.err.format("%s not empty%n", "test.trie"); + } catch (IOException x) { + System.err.println(x); + } + } + +} From b5dd1c55ff32e12d3b22efcef6620dfb0f04ab7c Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Wed, 4 Oct 2017 17:09:32 +0300 Subject: [PATCH 12/15] Added missing comments for hw3 --- .../main/java/ru/spbau/solikov/src/Trie.java | 38 +++++++++---------- .../test/java/ru/spbau/solikov/test/Main.java | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) 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 index cb2eab2..3bf4cf9 100644 --- a/hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java +++ b/hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java @@ -14,8 +14,8 @@ public class Trie implements Serializable { /** * Function to obtain the letter in String from integer * - * @param i - * @return string + * @param i number of letter + * @return string */ private static String getCharForNumber(int i) { return i > 0 && i < 27 ? String.valueOf((char) (i + 64)) : null; @@ -51,8 +51,8 @@ public Trie() { * False otherwise. * Supports size. * - * @param element - * @return true if element already in trie, false otherwise + * @param element string to be added + * @return true if element already in trie, false otherwise */ public boolean add(String element) { @@ -72,7 +72,7 @@ public boolean add(String element) { } } - if (current.isTerminal == true) { + if (current.isTerminal) { return false; } @@ -94,8 +94,8 @@ public boolean add(String element) { /** * Checks if element is in trie. * - * @param element - * @return true if element is in trie, false otherwise + * @param element to be checked + * @return true if element is in trie, false otherwise */ public boolean contains(String element) { @@ -111,15 +111,15 @@ public boolean contains(String element) { } } - return current.isTerminal == true; + 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 - * @return true if element was in trie, false otherwise + * @param element to be removed + * @return true if element was in trie, false otherwise */ public boolean remove(String element) { @@ -132,7 +132,7 @@ public boolean remove(String element) { char c = element.charAt(i); index = alphabet.get(Character.toString(c)); - if (i != element.length() && current.isTerminal == true) { + if (current.isTerminal) { lastElement = current; indexLastElement = i; } @@ -144,7 +144,7 @@ public boolean remove(String element) { } } - if (current.isTerminal == true) { + if (current.isTerminal) { for (int i = 0; i < ALPHABET_SIZE; i++) { if (current.nodes[i] != null) { current.isTerminal = false; @@ -197,8 +197,8 @@ public int size() { * 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 - * @return size + * @param prefix to be checked for size + * @return size */ public int howManyStartsWithPrefix(String prefix) { @@ -220,8 +220,8 @@ public int howManyStartsWithPrefix(String prefix) { /** * Takes output stream and serializes class Trie to that stream with standard Java method * - * @param out - * @throws IOException + * @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); @@ -233,9 +233,9 @@ public void serialize(OutputStream out) throws IOException { /** * Takes input stream and reads class from that stream with standard Java method * - * @param in - * @throws IOException - * @throws ClassNotFoundException + * @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); 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 index d5fdac3..cf00f5e 100644 --- a/hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java +++ b/hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java @@ -15,7 +15,7 @@ */ public class Main { - Trie trie; + private Trie trie; @Before public void setUp() { @@ -227,7 +227,7 @@ public void rmDir() { try { Path path = FileSystems.getDefault().getPath("test.trie"); Files.delete(path); - } catch (NoSuchFileException x) { + } catch (NoSuchFileException ignored) { } catch (DirectoryNotEmptyException x) { System.err.format("%s not empty%n", "test.trie"); } catch (IOException x) { From cf926bfcf5bfb4812c4d812c3ae7cd9a267a352c Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Wed, 4 Oct 2017 16:22:34 +0300 Subject: [PATCH 13/15] =?UTF-8?q?Added=20homework=20=E2=84=963?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hw3_Trie/.idea/compiler.xml | 9 + hw3_Trie/.idea/gradle.xml | 17 ++ hw3_Trie/.idea/misc.xml | 6 + hw3_Trie/.idea/modules.xml | 10 + hw3_Trie/.idea/modules/bor.iml | 13 + hw3_Trie/.idea/modules/bor_main.iml | 13 + hw3_Trie/.idea/modules/bor_test.iml | 17 ++ hw3_Trie/build.gradle | 14 + hw3_Trie/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54788 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + hw3_Trie/settings.gradle | 2 + .../main/java/ru/spbau/solikov/src/Trie.java | 245 ++++++++++++++++++ .../test/java/ru/spbau/solikov/test/Main.java | 238 +++++++++++++++++ 13 files changed, 590 insertions(+) create mode 100644 hw3_Trie/.idea/compiler.xml create mode 100644 hw3_Trie/.idea/gradle.xml create mode 100644 hw3_Trie/.idea/misc.xml create mode 100644 hw3_Trie/.idea/modules.xml create mode 100644 hw3_Trie/.idea/modules/bor.iml create mode 100644 hw3_Trie/.idea/modules/bor_main.iml create mode 100644 hw3_Trie/.idea/modules/bor_test.iml create mode 100644 hw3_Trie/build.gradle create mode 100644 hw3_Trie/gradle/wrapper/gradle-wrapper.jar create mode 100644 hw3_Trie/gradle/wrapper/gradle-wrapper.properties create mode 100644 hw3_Trie/settings.gradle create mode 100644 hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java create mode 100644 hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java 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 0000000000000000000000000000000000000000..5d7d24614cac38960edd6e79e20a9a23d8edea91 GIT binary patch literal 54788 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 literal 0 HcmV?d00001 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..cb2eab2 --- /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 + * @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 + * @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 == true) { + 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 + * @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 == true; + } + + /** + * 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 + * @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 (i != element.length() && current.isTerminal == true) { + lastElement = current; + indexLastElement = i; + } + + if (current.nodes[index] != null) { + current = current.nodes[index]; + } else { + return false; + } + } + + if (current.isTerminal == true) { + 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 + * @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 + * @throws IOException + */ + 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 + * @throws IOException + * @throws ClassNotFoundException + */ + 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..d5fdac3 --- /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 { + + 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 x) { + } catch (DirectoryNotEmptyException x) { + System.err.format("%s not empty%n", "test.trie"); + } catch (IOException x) { + System.err.println(x); + } + } + +} From 7af3e589e6f44a54b2a62945eb6fb1603abc0894 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Wed, 4 Oct 2017 17:09:32 +0300 Subject: [PATCH 14/15] Added missing comments for hw3 --- .../main/java/ru/spbau/solikov/src/Trie.java | 38 +++++++++---------- .../test/java/ru/spbau/solikov/test/Main.java | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) 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 index cb2eab2..3bf4cf9 100644 --- a/hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java +++ b/hw3_Trie/src/main/java/ru/spbau/solikov/src/Trie.java @@ -14,8 +14,8 @@ public class Trie implements Serializable { /** * Function to obtain the letter in String from integer * - * @param i - * @return string + * @param i number of letter + * @return string */ private static String getCharForNumber(int i) { return i > 0 && i < 27 ? String.valueOf((char) (i + 64)) : null; @@ -51,8 +51,8 @@ public Trie() { * False otherwise. * Supports size. * - * @param element - * @return true if element already in trie, false otherwise + * @param element string to be added + * @return true if element already in trie, false otherwise */ public boolean add(String element) { @@ -72,7 +72,7 @@ public boolean add(String element) { } } - if (current.isTerminal == true) { + if (current.isTerminal) { return false; } @@ -94,8 +94,8 @@ public boolean add(String element) { /** * Checks if element is in trie. * - * @param element - * @return true if element is in trie, false otherwise + * @param element to be checked + * @return true if element is in trie, false otherwise */ public boolean contains(String element) { @@ -111,15 +111,15 @@ public boolean contains(String element) { } } - return current.isTerminal == true; + 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 - * @return true if element was in trie, false otherwise + * @param element to be removed + * @return true if element was in trie, false otherwise */ public boolean remove(String element) { @@ -132,7 +132,7 @@ public boolean remove(String element) { char c = element.charAt(i); index = alphabet.get(Character.toString(c)); - if (i != element.length() && current.isTerminal == true) { + if (current.isTerminal) { lastElement = current; indexLastElement = i; } @@ -144,7 +144,7 @@ public boolean remove(String element) { } } - if (current.isTerminal == true) { + if (current.isTerminal) { for (int i = 0; i < ALPHABET_SIZE; i++) { if (current.nodes[i] != null) { current.isTerminal = false; @@ -197,8 +197,8 @@ public int size() { * 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 - * @return size + * @param prefix to be checked for size + * @return size */ public int howManyStartsWithPrefix(String prefix) { @@ -220,8 +220,8 @@ public int howManyStartsWithPrefix(String prefix) { /** * Takes output stream and serializes class Trie to that stream with standard Java method * - * @param out - * @throws IOException + * @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); @@ -233,9 +233,9 @@ public void serialize(OutputStream out) throws IOException { /** * Takes input stream and reads class from that stream with standard Java method * - * @param in - * @throws IOException - * @throws ClassNotFoundException + * @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); 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 index d5fdac3..cf00f5e 100644 --- a/hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java +++ b/hw3_Trie/src/test/java/ru/spbau/solikov/test/Main.java @@ -15,7 +15,7 @@ */ public class Main { - Trie trie; + private Trie trie; @Before public void setUp() { @@ -227,7 +227,7 @@ public void rmDir() { try { Path path = FileSystems.getDefault().getPath("test.trie"); Files.delete(path); - } catch (NoSuchFileException x) { + } catch (NoSuchFileException ignored) { } catch (DirectoryNotEmptyException x) { System.err.format("%s not empty%n", "test.trie"); } catch (IOException x) { From 53347dfa0f71aada877d22a2eb873b227f7b51a4 Mon Sep 17 00:00:00 2001 From: Pavel Solikov Date: Wed, 4 Oct 2017 22:49:42 +0300 Subject: [PATCH 15/15] Deleted folders from other hws --- hw1/.idea/misc.xml | 6 - hw1/.idea/modules.xml | 8 - hw1/hw1.iml | 31 --- hw1/ru/spbau/solikov/hw1/src/HashTable.java | 157 ----------- hw1/ru/spbau/solikov/hw1/src/List.java | 121 --------- .../hw1/src/primitive/test/HashTableTest.java | 41 --- .../hw1/src/primitive/test/ListTest.java | 29 -- .../spbau/solikov/hw1/test/HashTableTest.java | 253 ------------------ hw1/ru/spbau/solikov/hw1/test/ListTest.java | 127 --------- hw2/.idea/compiler.xml | 9 - hw2/.idea/gradle.xml | 17 -- .../libraries/Gradle__junit_junit_4_12.xml | 11 - ...Gradle__org_hamcrest_hamcrest_core_1_3.xml | 11 - hw2/.idea/modules.xml | 10 - hw2/.idea/modules/hw2.iml | 13 - hw2/.idea/modules/hw2_main.iml | 13 - hw2/.idea/modules/hw2_test.iml | 17 -- hw2/build.gradle | 14 - hw2/settings.gradle | 2 - .../java/ru/spbau/solikov/hw2/src/Spiral.java | 112 -------- .../solikov/hw2/src/test/SpiralTest.java | 49 ---- 21 files changed, 1051 deletions(-) delete mode 100644 hw1/.idea/misc.xml delete mode 100644 hw1/.idea/modules.xml delete mode 100644 hw1/hw1.iml delete mode 100644 hw1/ru/spbau/solikov/hw1/src/HashTable.java delete mode 100644 hw1/ru/spbau/solikov/hw1/src/List.java delete mode 100644 hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java delete mode 100644 hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java delete mode 100644 hw1/ru/spbau/solikov/hw1/test/HashTableTest.java delete mode 100644 hw1/ru/spbau/solikov/hw1/test/ListTest.java delete mode 100644 hw2/.idea/compiler.xml delete mode 100644 hw2/.idea/gradle.xml delete mode 100644 hw2/.idea/libraries/Gradle__junit_junit_4_12.xml delete mode 100644 hw2/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml delete mode 100644 hw2/.idea/modules.xml delete mode 100644 hw2/.idea/modules/hw2.iml delete mode 100644 hw2/.idea/modules/hw2_main.iml delete mode 100644 hw2/.idea/modules/hw2_test.iml delete mode 100644 hw2/build.gradle delete mode 100644 hw2/settings.gradle delete mode 100644 hw2/src/main/java/ru/spbau/solikov/hw2/src/Spiral.java delete mode 100644 hw2/src/test/java/ru/spbau/solikov/hw2/src/test/SpiralTest.java diff --git a/hw1/.idea/misc.xml b/hw1/.idea/misc.xml deleted file mode 100644 index 0548357..0000000 --- a/hw1/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/hw1/.idea/modules.xml b/hw1/.idea/modules.xml deleted file mode 100644 index 9b27cd1..0000000 --- a/hw1/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/hw1/hw1.iml b/hw1/hw1.iml deleted file mode 100644 index bf5ed58..0000000 --- a/hw1/hw1.iml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw1/ru/spbau/solikov/hw1/src/HashTable.java b/hw1/ru/spbau/solikov/hw1/src/HashTable.java deleted file mode 100644 index f71fafd..0000000 --- a/hw1/ru/spbau/solikov/hw1/src/HashTable.java +++ /dev/null @@ -1,157 +0,0 @@ -package ru.spbau.solikov.hw1.src; - -/** - * Implementation of hash table structure using separate chaining - */ -public class HashTable { - - /** - * Number used in hash function - */ - private static final int DEFAULT_HASH_PRIME_NUMBER = 31; - - private List[] table; - private int capacity = 2; - private int size; - - /** - * Creates a HashTable of default capacity - */ - public HashTable() { - table = new List[2]; - size = 0; - } - - /** - * Returns number of keys - * - * @return size - */ - public int size() { - return size; - } - - - /** - * Iterates on each list in HashTable and clears it, capacity is the same - */ - public void clear() { - for (List list : table) { - if (list != null) { - list.clear(); - } - } - size = 0; - } - - /** - * Hash function for obtaining a code from key - * - * @param key - * @return hash code - */ - private int getHash(String key) { - int hash = 0; - for (int i = 0; i < key.length(); i++) { - hash = (DEFAULT_HASH_PRIME_NUMBER * hash + key.charAt(i)) % capacity; - } - return hash; - } - - /** - * Returns data stored by key or null if not found - * - * @param key - * @return data or null if not found - */ - public String get(String key) { - int hash = getHash(key); - if (table[hash] == null){ - return null; - } - return table[hash].find(key); - } - - /** - * Increases the capacity of HashTable by 2 times - */ - private void increase() { - List[] oldTable = table; - table = new List[2 * capacity]; - capacity *= 2; - - for (List list : oldTable) { - if (list != null) { - while (!list.isEmpty()) { - String key = list.getHeadsKey(); - int hash = getHash(key); - if (table[hash] == null) { - table[hash] = new List(); - } - table[hash].insert(key, list.getHeadsData()); - list.delete(key); - } - } - } - } - - /** - * Allows to add the pair of key and data into HashTable, if there's not enough space calls increase() - * - * @param key - * @param data - * @return data that was stored before by the same key or null if this space was empty - */ - public String put(String key, String data) { - int hash = getHash(key); - if (table[hash] == null) { - table[hash] = new List(); - } - - List current = table[hash]; - String deleted = current.delete(key); - current.insert(key, data); - - if (deleted == null) { - size++; - } - - if (size >= capacity) { - increase(); - } - - return deleted; - } - - /** - * Allows to remove data stored by key - * - * @param key - * @return data stored by key - */ - public String remove(String key) { - int hash = getHash(key); - - if (table[hash] == null) { - return null; - } - - String deleted = table[hash].delete(key); - if (deleted != null) { - size--; - return deleted; - } - - return null; - } - - /** - * Checks whether HashTable contains some data by passed key - * - * @param key - * @return data or null if the HashTable doesn't contain key - */ - public boolean contains(String key) { - return get(key) != null; - } -} diff --git a/hw1/ru/spbau/solikov/hw1/src/List.java b/hw1/ru/spbau/solikov/hw1/src/List.java deleted file mode 100644 index 04f8d48..0000000 --- a/hw1/ru/spbau/solikov/hw1/src/List.java +++ /dev/null @@ -1,121 +0,0 @@ -package ru.spbau.solikov.hw1.src; - -/** - * Implementation of single-linked list that stores Nodes of keys and data - */ -public class List { - - /** - * Class that stores keys and data and has link for next Node - */ - private class Node { - - private Node next; - - private String key; - - private String data; - - public Node(Node n, String k, String d) { - next = n; - key = k; - data = d; - } - } - - private Node head; - - private Node tail; - - public Node getHead() { - return head; - } - - /** - * Adds a pair of key and data in List - * - * @param key - * @param data - */ - public void insert(String key, String data) { - if (head != null) { - tail.next = new Node(null, key, data); - tail = tail.next; - } else { - tail = head = new Node(null, key, data); - } - } - - /** - * Checks if the list does not contain any elements - * - * @return boolean - true if the list is empty - */ - public boolean isEmpty() { - return head == null; - } - - /** - * Searches for data by key in List - * - * @param key - * @return data or null if not found - */ - public String find(String key) { - Node current = head; - while (current != null) { - if (current.key.equals(key)) { - return current.data; - } - current = current.next; - } - - return null; - } - - public String getHeadsKey() { - return head.key; - } - - public String getHeadsData() { - return head.data; - } - - /** - * Deletes a data stored by key in List - * - * @param key - * @return data that was deleted - */ - public String delete(String key) { - if (head == null) { - return null; - } - - if (head.key.equals(key)) { - String deleted = head.data; - head = head.next; - return deleted; - } - - Node current = head; - while (current.next != null) { - if (current.next.key.equals(key)) { - String deleted = current.next.data; - current.next = current.next.next; - return deleted; - } - current = current.next; - } - - return null; - } - - /** - * Clears the List - */ - public void clear() { - head = null; - tail = null; - } -} diff --git a/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java b/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java deleted file mode 100644 index 6a881ca..0000000 --- a/hw1/ru/spbau/solikov/hw1/src/primitive/test/HashTableTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package ru.spbau.solikov.hw1.src.primitive.test; - -import ru.spbau.solikov.hw1.src.HashTable; - -/** - * Class for testing all the HashTable functions - */ - -public class HashTableTest { - public static void main(String[] args) { - HashTable hashTable = new HashTable(); - - for (int i = 0; i < 100; i++){ - hashTable.put(String.valueOf(i), String.valueOf(i * i)); - } - - System.out.println(hashTable.size() + " - size of hashtable (must be 100)"); - - System.out.println(); - - System.out.println(hashTable.get("10") + " - square of 10 (must be 100)"); - - System.out.println(); - - System.out.println(hashTable.put("10", "200") + " - changed square of 10 (must have been 100)"); - - System.out.println(); - - System.out.println(hashTable.contains("10") + " - contains '10' (must be true)"); - - System.out.println(); - - System.out.println(hashTable.remove("10") + " - removed '10'"); - - System.out.println(); - - hashTable.clear(); - - System.out.println(hashTable.size() + " - size of hashtable (must be 0)"); - } -} diff --git a/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java deleted file mode 100644 index be54d90..0000000 --- a/hw1/ru/spbau/solikov/hw1/src/primitive/test/ListTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.spbau.solikov.hw1.src.primitive.test; - -import ru.spbau.solikov.hw1.src.List; - -/** - * Class for testing all the List functions - */ - -public class ListTest { - public static void main(String[] args) { - List l = new List(); - - for (int i = 0; i < 10; i++) { - l.insert(String.valueOf(i), String.valueOf(i)); - } - - System.out.println(); - - System.out.println(l.find("5") + " - found key '5'."); - l.delete("5"); - System.out.println(l.delete("5") + " - deleted key '5'."); - - System.out.println(); - - l.clear(); - System.out.print(l.getHead() == null); - System.out.println(" - cleared the list."); - } -} diff --git a/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java b/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java deleted file mode 100644 index 4e01672..0000000 --- a/hw1/ru/spbau/solikov/hw1/test/HashTableTest.java +++ /dev/null @@ -1,253 +0,0 @@ -package ru.spbau.solikov.hw1.test; - -import org.junit.Before; -import org.junit.Test; -import ru.spbau.solikov.hw1.src.HashTable; - -import static org.junit.Assert.*; - -/** - * Test class for HashTable with separate chaining - */ -public class HashTableTest { - - private HashTable table; - - /** - * Constructs new hash table for every test - */ - @Before - public void setUp() { - table = new HashTable(); - } - - /** - * Tests if the size of empty table is 0 - */ - @Test - public void testSizeOfEmptyHashTable() { - assertEquals(0, table.size()); - } - - /** - * Tests if 'put' puts the right key - */ - @Test - public void testPut() { - table.put("27", "09"); - assertEquals(true, table.contains("27")); - } - - /** - * Tests if the size after many 'puts' is correct - */ - @Test - public void testSizeAfterPut() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - assertEquals(10, table.size()); - } - - /** - * Tests if the size after 'put' in the same key is correct - */ - @Test - public void testSizeAfterPutExistingKey() { - table.put("123", "321"); - table.put("123", "333"); - assertEquals(1, table.size()); - } - - /** - * Tests if 'put' inserts new value instead of previous - */ - @Test - public void testPutExistingKey() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - table.put("4", "99"); - assertEquals("99", table.get("4")); - } - - /** - * Tests if 'size' on empty table is 0 - */ - @Test - public void testClearEmpty() { - table.clear(); - assertEquals(0, table.size()); - } - - /** - * Tests if 'clear' works right on table with many keys - */ - @Test - public void testClear() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - table.clear(); - assertEquals(0, table.size()); - } - - /** - * Tests if 'get' from empty table returns null - */ - @Test - public void testGetFromEmpty() { - assertEquals(null, table.get("Deadline")); - } - - /** - * Tests if 'get' from one-element table returns the right value - */ - @Test - public void testGetElementFromOneElementTable() { - table.put("1", "2"); - assertEquals("2", table.get("1")); - } - - /** - * Tests if 'get' on many-element table returns the right value - */ - @Test - public void testGetElementFromManyElementTable() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - assertEquals("5", table.get("5")); - } - - /** - * Tests if 'get' does not change the size of hash table - */ - @Test - public void testGetDoesNotChangeSize() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - table.get("5"); - assertEquals(10, table.size()); - } - - /** - * Tests if 'remove' does nothing with table after removing from empty table - */ - @Test - public void testSizeAfterRemovingFromEmptyTable() { - table.remove("123"); - assertEquals(0, table.size()); - } - - /** - * Tests if 'remove' on empty table returns null - */ - @Test - public void testRemoveFromEmptyTable() { - assertEquals(null, table.remove("123")); - } - - /** - * Tests if 'remove' on one-element table returns the right value - */ - @Test - public void testRemoveFromOneElementTable() { - table.put("123", "321"); - assertEquals("321", table.get("123")); - } - - /** - * Tests if the size of table becomes zero after removing from one-element table - */ - @Test - public void testSizeAfterRemovingFromOneElementTable() { - table.put("123", "321"); - table.remove("123"); - assertEquals(0, table.size()); - } - - /** - * Tests if the size of hash table is not affected by removing element that does not exist - */ - @Test - public void testSizeAfterRemovingNonexistingFromManyElementTable() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - table.remove("100"); - assertEquals(10, table.size()); - } - - /** - * Tests if size after removing existing element decreases - */ - @Test - public void testSizeAfterRemoveFromManyElementTable() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - table.remove("5"); - assertEquals(9, table.size()); - } - - /** - * Tests if 'remove' from many-element table returns the right value - */ - @Test - public void testRemoveFromManyElementTable() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - assertEquals("4", table.remove("4")); - } - - /** - * Tests if 'contains' returns false on empty table - */ - @Test - public void testContainsFromEmptyTable() { - assertEquals(false, table.contains("some")); - } - - /** - * Tests if 'contains' returns true on element that exists in one-element table - */ - @Test - public void testContainsFromOneElementTable() { - table.put("Wednesday", "27.09"); - assertEquals(true, table.contains("Wednesday")); - } - - /** - * Tests if 'contains' returns false on element that does not exist in one-element table - */ - @Test - public void testContainsNonexistingElementFromOneElementTable() { - table.put("Wednesday", "27.09"); - assertEquals(false, table.contains("Sunday")); - } - - /** - * Tests if 'contains' returns true on many-element table - */ - @Test - public void testContainsManyElementTable() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - assertEquals(true, table.contains("4")); - } - - /** - * Tests if 'contains' returns false on elements that don't exist in many-element table - */ - @Test - public void testContainsNonexistingElementFromManyElementTable() { - for (int i = 0; i < 10; i++) { - table.put(String.valueOf(i), String.valueOf(i)); - } - assertEquals(false, table.contains("123")); - } -} \ No newline at end of file diff --git a/hw1/ru/spbau/solikov/hw1/test/ListTest.java b/hw1/ru/spbau/solikov/hw1/test/ListTest.java deleted file mode 100644 index 7100040..0000000 --- a/hw1/ru/spbau/solikov/hw1/test/ListTest.java +++ /dev/null @@ -1,127 +0,0 @@ -package ru.spbau.solikov.hw1.test; - -import org.junit.Before; -import org.junit.Test; -import ru.spbau.solikov.hw1.src.List; - -import static org.junit.Assert.*; - -/** - * Test class for class List that implements single-linked list - */ -public class ListTest { - - private List l; - - /** - * Constructs new list for every test - */ - @Before - public void setUp() { - l = new List(); - } - - /** - * Tests if 'isEmpty' on empty list returns true - */ - @Test - public void testIsEmptyOfEmptyList() { - assertEquals(true, l.isEmpty()); - } - - /** - * Tests if 'insert' on empty list inserts the right data - */ - @Test - public void testInsert() { - l.insert("SPbAU", "Java"); - assertEquals("Java", l.getHeadsData()); - } - - /** - * Tests if many 'insert's insert the right data - */ - @Test - public void testManyInserts() { - for (int i = 0; i < 10; i++) { - l.insert(String.valueOf(i), String.valueOf(i)); - } - - int number = 0; - while (!l.isEmpty()) { - assertEquals(Integer.toString(number), l.getHeadsData()); - number++; - l.delete(l.getHeadsKey()); - } - } - - /** - * Tests if 'find' on empty list returns null - */ - @Test - public void testFindNonexistingElement() { - assertEquals(null, l.find("Something")); - } - - /** - * Tests if 'find' on one-element list returns right value - */ - @Test - public void testFindExistingElement() { - l.insert("SPbAU", "Java"); - assertEquals("Java", l.find("SPbAU")); - } - - /** - * Tests if 'find' on many-element list returns right value - */ - @Test - public void testFindExistingElementManyElements() { - for (int i = 0; i < 10; i++) { - l.insert(String.valueOf(i), String.valueOf(i)); - } - l.insert("SPbAU", "Java"); - assertEquals("Java", l.find("SPbAU")); - } - - /** - * Tests if 'delete' on empty list does nothing - */ - @Test - public void testDeleteElementFromEmptyList() { - l.delete("SomeKey"); - assertEquals(true, l.isEmpty()); - } - - /** - * Tests if 'delete' nonexisting element on one-element list does nothing - */ - @Test - public void testDeleteNonexistingElement() { - l.insert("1", "2"); - l.delete("5"); - assertNotEquals(true, l.isEmpty()); - } - - /** - * Tests if 'delete' from one-element list truly deletes element - */ - @Test - public void testDeleteExistingElement() { - l.insert("SPbAU", "Java"); - l.delete("SPbAU"); - assertEquals(true, l.isEmpty()); - } - - /** - * Tests if 'clear' deletes all elements from many-element list - */ - @Test - public void testClear() { - for (int i = 0; i < 10; i++) { - l.insert(String.valueOf(i), String.valueOf(i)); - } - l.clear(); - assertEquals(true, l.isEmpty()); - } -} \ No newline at end of file diff --git a/hw2/.idea/compiler.xml b/hw2/.idea/compiler.xml deleted file mode 100644 index f68cfe7..0000000 --- a/hw2/.idea/compiler.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/hw2/.idea/gradle.xml b/hw2/.idea/gradle.xml deleted file mode 100644 index a5c3ae1..0000000 --- a/hw2/.idea/gradle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/hw2/.idea/libraries/Gradle__junit_junit_4_12.xml b/hw2/.idea/libraries/Gradle__junit_junit_4_12.xml deleted file mode 100644 index 04c10dd..0000000 --- a/hw2/.idea/libraries/Gradle__junit_junit_4_12.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/hw2/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml b/hw2/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml deleted file mode 100644 index 8262f72..0000000 --- a/hw2/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/hw2/.idea/modules.xml b/hw2/.idea/modules.xml deleted file mode 100644 index 0a515eb..0000000 --- a/hw2/.idea/modules.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/hw2/.idea/modules/hw2.iml b/hw2/.idea/modules/hw2.iml deleted file mode 100644 index df6b05c..0000000 --- a/hw2/.idea/modules/hw2.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw2/.idea/modules/hw2_main.iml b/hw2/.idea/modules/hw2_main.iml deleted file mode 100644 index 47d7e3c..0000000 --- a/hw2/.idea/modules/hw2_main.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw2/.idea/modules/hw2_test.iml b/hw2/.idea/modules/hw2_test.iml deleted file mode 100644 index 1975b36..0000000 --- a/hw2/.idea/modules/hw2_test.iml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hw2/build.gradle b/hw2/build.gradle deleted file mode 100644 index 4d6a793..0000000 --- a/hw2/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -group '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/hw2/settings.gradle b/hw2/settings.gradle deleted file mode 100644 index c182db6..0000000 --- a/hw2/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'hw2' - diff --git a/hw2/src/main/java/ru/spbau/solikov/hw2/src/Spiral.java b/hw2/src/main/java/ru/spbau/solikov/hw2/src/Spiral.java deleted file mode 100644 index 9c1ce9f..0000000 --- a/hw2/src/main/java/ru/spbau/solikov/hw2/src/Spiral.java +++ /dev/null @@ -1,112 +0,0 @@ -package ru.spbau.solikov.hw2.src; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.Scanner; - -/** - * Matrix of size N * N that could be printed as spiral and sorted by first elements in column - */ -public class Spiral { - - private final int size; - private int matrix[][]; - - /** - * Generates matrix of given size - * Transposes it in order to easily sort by columns - * - * @param N odd integer - * @param other matrix - */ - public Spiral(int N, int other[][]) { - size = N; - matrix = new int[N][N]; - for (int i = 0; i < size; i++) { - for (int j = 0; j < size; j++) { - matrix[j][i] = other[i][j]; - } - } - } - - /** - * Method for initializing matrix's fields from standard input - * Transposes it in order to easily sort by columns - */ - public void fill() { - Scanner in = new Scanner(System.in); - for (int i = 0; i < size; i++) { - for (int j = 0; j < size; j++) { - matrix[j][i] = in.nextInt(); - } - } - } - - /** - * Sorting the matrix by first element in column using custom comparator - */ - public void sort() { - Arrays.sort(matrix, new Comparator() { - @Override - public int compare(final int[] first, final int[] second) { - Integer f = first[0]; - Integer s = second[0]; - return f.compareTo(s); - } - }); - } - - /** - * Prints matrix's fields into standard output - * Transposes matrix back to the original view - */ - public void print() { - for (int i = 0; i < size; i++) { - for (int j = 0; j < size; j++) { - System.out.print(matrix[j][i] + " "); - } - System.out.println(); - } - } - - public int[][] getMatrix() { - return matrix; - } - - /** - * Transposes matrix back to the original view - * Prints matrix's fields into standard output by spiral order. Integer 'c' is the center of matrix - * External 'for' loop implements the layer of matrix - * 4 internal 'for' loops implement round way on sides of current rectangle - */ - public void printSpiral() { - final int c = size / 2; - System.out.print(matrix[c][c] + " "); - if (size > 1) { - int x = c; - int y = c; - int layer = 0; - for (int side = 2; side < size - layer; side++) { - y++; - System.out.print(matrix[y][x] + " "); - for (int i = 1; i < side + layer; i++) { - System.out.print(matrix[y][x - i] + " "); - } - x -= side - 1 + layer; - for (int i = 1; i < side + layer + 1; i++) { - System.out.print(matrix[y - i][x] + " "); - } - y -= side + layer; - for (int i = 1; i < side + layer + 1; i++) { - System.out.print(matrix[y][x + i] + " "); - } - x += side + layer; - for (int i = 1; i < side + layer + 1; i++) { - System.out.print(matrix[y + i][x] + " "); - } - y += side + layer; - layer++; - } - } - } -} \ No newline at end of file diff --git a/hw2/src/test/java/ru/spbau/solikov/hw2/src/test/SpiralTest.java b/hw2/src/test/java/ru/spbau/solikov/hw2/src/test/SpiralTest.java deleted file mode 100644 index 497577a..0000000 --- a/hw2/src/test/java/ru/spbau/solikov/hw2/src/test/SpiralTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package ru.spbau.solikov.hw2.src.test; - -import org.junit.Before; -import org.junit.Test; -import ru.spbau.solikov.hw2.src.Spiral; - -import java.util.Arrays; -import java.util.Comparator; - -import static org.junit.Assert.*; - - -public class SpiralTest { - - private int[][] elements = { - {4, 2, 3}, - {4, 5, 6}, - {7, 8, 9} - }; - - private Spiral s = new Spiral(3, elements); - - /** - * Tests if matrix is equal to transposed input matrix - */ - @Test - public void testConstructor() { - int[][] matrix = s.getMatrix(); - for (int i = 0; i < matrix.length; i++){ - for (int j = 0; j < matrix.length; j++){ - assertEquals(true, matrix[j][i] == elements[i][j]); - } - } - } - - /** - * Tests if matrix is sorted from left to right - */ - @Test - public void testSort() { - s.sort(); - int[][] matrix = s.getMatrix(); - int prev = matrix[0][0]; - for (int i = 1; i < matrix.length; i++){ - assertEquals(true, prev <= matrix[i][0]); - } - } - -} \ No newline at end of file