diff --git a/.gitignore b/.gitignore
index 208a567..82be5ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,66 @@
.idea/
target/**
+
+# build and test folders.
+build
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+#
+# User-specific stuff:
+**/.idea/**/workspace.xml
+**/.idea/**/tasks.xml
+
+# (IDEA's) Gradle:
+**/.idea/**/gradle.xml
+**/.idea/**/libraries
+
+
+# Sensitive or high-churn files:
+**/.idea/**/dataSources/
+**/.idea/**/dataSources.ids
+**/.idea/**/dataSources.xml
+**/.idea/**/dataSources.local.xml
+**/.idea/**/sqlDataSources.xml
+**/.idea/**/dynamic.xml
+**/.idea/**/uiDesigner.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+
+### Gradle ###
+.gradle
+/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
+
+classes/
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..5b6f6d1
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,14 @@
+group 'ru.spbau.mit'
+version '1.0-SNAPSHOT'
+
+apply plugin: 'java'
+
+sourceCompatibility = 1.8
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ testCompile group: 'junit', name: 'junit', version: '4.11'
+}
diff --git a/classes/test/assignments-2017-1/checkstyle.xml b/classes/test/assignments-2017-1/checkstyle.xml
new file mode 100644
index 0000000..abd42af
--- /dev/null
+++ b/classes/test/assignments-2017-1/checkstyle.xml
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..6ffa237
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..12a47ad
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Mar 04 23:01:58 MSK 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..9aa616c
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,169 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..2dfc931
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'assignments-2017-1'
+
diff --git a/src/main/java/ru/spbau/mit/DictionaryImpl.java b/src/main/java/ru/spbau/mit/DictionaryImpl.java
new file mode 100644
index 0000000..f0b04a4
--- /dev/null
+++ b/src/main/java/ru/spbau/mit/DictionaryImpl.java
@@ -0,0 +1,206 @@
+package ru.spbau.mit;
+
+public class DictionaryImpl implements Dictionary {
+
+ // how many elements at most can be stored in a bucket with one hash
+ private static final int MAX_BUCKET_FILL_SIZE = 4;
+ private static final int INITIAL_BUCKETS_NUMBER = 4;
+
+ private Bucket[] buckets;
+ private int size;
+
+ public DictionaryImpl() {
+ buckets = emptyBuckets(INITIAL_BUCKETS_NUMBER);
+ size = 0;
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public boolean contains(String key) {
+ if (key == null) {
+ return false;
+ }
+ int hashIndex = bucketIndex(key);
+ return buckets[hashIndex].contains(key);
+ }
+
+ public String get(String key) {
+ if (key == null) {
+ return null;
+ }
+ int hashIndex = bucketIndex(key);
+ return buckets[hashIndex].get(key);
+ }
+
+ public String put(String key, String value) {
+ if (key == null) {
+ return null;
+ }
+ int hashIndex = bucketIndex(key);
+ Bucket bucket = buckets[hashIndex];
+
+ if (bucket.canPut(key)) {
+ boolean containedBefore = bucket.contains(key);
+ String oldValue = bucket.put(key, value);
+ if (!containedBefore) {
+ size++;
+ }
+ return oldValue;
+ } else {
+ resize();
+ return put(key, value);
+ }
+ }
+
+ public String remove(String key) {
+ if (key == null) {
+ return null;
+ }
+ int hashIndex = bucketIndex(key);
+ Bucket bucket = buckets[hashIndex];
+ boolean containedBefore = bucket.contains(key);
+ String oldValue = bucket.remove(key);
+ if (containedBefore) {
+ size--;
+ }
+ return oldValue;
+ }
+
+ public void clear() {
+ size = 0;
+ buckets = emptyBuckets(buckets.length);
+ }
+
+ private static int bucketIndex(String key, int bucketsNumber) {
+ int initialHash = key.hashCode();
+ final int bitsInInteger = 32;
+ final int minIntegerAbs = 1 << (bitsInInteger - 1);
+ if (initialHash < 0) {
+ initialHash += minIntegerAbs;
+ }
+
+ return initialHash % bucketsNumber;
+ }
+
+ private int bucketIndex(String key) {
+ return bucketIndex(key, buckets.length);
+ }
+
+ private static Bucket[] emptyBuckets(int newBucketsNumber) {
+ Bucket[] newBuckets = new Bucket[newBucketsNumber];
+ for (int i = 0; i < newBucketsNumber; ++i) {
+ newBuckets[i] = new Bucket();
+ }
+ return newBuckets;
+ }
+
+ private Bucket[] newBuckets(int newBucketsNumber) {
+ Bucket[] newBuckets = emptyBuckets(newBucketsNumber);
+ for (Bucket bucket : buckets) {
+ for (int i = 0; i < bucket.size(); ++i) {
+ int newBucketIndex = bucketIndex(bucket.values[i].key, newBucketsNumber);
+ Bucket newBucket = newBuckets[newBucketIndex];
+
+ if (!newBucket.canPut(bucket.values[i].key)) {
+ return null;
+ }
+ newBucket.add(bucket.values[i]);
+ }
+ }
+ return newBuckets;
+ }
+
+ private void resize() {
+ Bucket[] newBs = null;
+ for (int newBucketsNumber = buckets.length * 2; newBs == null; newBucketsNumber *= 2) {
+ newBs = newBuckets(newBucketsNumber);
+ }
+ buckets = newBs;
+ }
+
+ private static class Node {
+ private final String key;
+ private String value;
+
+ Node(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+ private static class Bucket {
+ private final Node[] values;
+
+ Bucket() {
+ values = new Node[MAX_BUCKET_FILL_SIZE];
+ }
+
+ public boolean contains(String key) {
+ return index(key) != -1;
+ }
+
+ public String get(String key) {
+ int i = index(key);
+ return i == -1 ? null : values[i].value;
+ }
+
+ public int size() {
+ for (int i = 0; i < MAX_BUCKET_FILL_SIZE; ++i) {
+ if (values[i] == null) {
+ return i;
+ }
+ }
+ return MAX_BUCKET_FILL_SIZE;
+ }
+
+ public boolean canPut(String key) {
+ return index(key) != -1 || !isFull();
+ }
+
+ public String put(String key, String value) {
+ int i = index(key);
+ assert (canPut(key));
+ if (i != -1) {
+ String oldValue = values[i].value;
+ values[i].value = value;
+ return oldValue;
+ }
+
+ add(new Node(key, value));
+ return null;
+ }
+
+ public void add(Node node) {
+ values[size()] = node;
+ }
+
+ public String remove(String key) {
+ String oldValue = null;
+ int i = index(key);
+ if (i != -1) {
+ oldValue = values[i].value;
+ int size = size();
+ values[i] = values[size - 1];
+ values[size - 1] = null;
+ assert (size() == size - 1);
+ }
+ return oldValue;
+ }
+
+ private int index(String key) {
+ int size = size();
+ for (int i = 0; i < size; i++) {
+ if (values[i].key.equals(key)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private boolean isFull() {
+ return size() == MAX_BUCKET_FILL_SIZE;
+ }
+ }
+}
diff --git a/src/test/java/ru/spbau/mit/DictionaryImplTest.java b/src/test/java/ru/spbau/mit/DictionaryImplTest.java
new file mode 100644
index 0000000..324504c
--- /dev/null
+++ b/src/test/java/ru/spbau/mit/DictionaryImplTest.java
@@ -0,0 +1,294 @@
+package ru.spbau.mit;
+
+import org.junit.Test;
+
+import java.util.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+
+public class DictionaryImplTest {
+ private static final int NTESTS = 10000;
+ private static final int MAX_STRING_SIZE = 1000;
+ private static final int MAX_CHAR = 256;
+ private static final Random RANDOMIZER;
+ private static final int RANDOMIZER_SEED = 1092423045;
+ private static final String RANDOM_SYMBOLS;
+
+ static {
+ RANDOMIZER = new Random(RANDOMIZER_SEED);
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < MAX_CHAR; ++i) {
+ sb.append((char) i);
+ }
+
+ RANDOM_SYMBOLS = sb.toString();
+ }
+
+ private static String randomString() {
+ int size = 1 + RANDOMIZER.nextInt(MAX_STRING_SIZE);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < size; ++i) {
+ int nextCharPosition = RANDOMIZER.nextInt(RANDOM_SYMBOLS.length());
+ char nextSymbol = RANDOM_SYMBOLS.charAt(nextCharPosition);
+ sb.append(nextSymbol);
+ }
+ return sb.toString();
+ }
+
+ @Test
+ public void size() throws Exception {
+ DictionaryImpl d = new DictionaryImpl();
+ int size = 0;
+ ArrayList keys = new ArrayList<>();
+ for (int i = 0; i < NTESTS; i++) {
+ assertEquals(d.size(), size);
+ String key = randomString();
+ if (!keys.contains(key)) {
+ keys.add(key);
+ size++;
+ d.put(key, randomString());
+ assertEquals(d.size(), size);
+ }
+ }
+ assertEquals(d.size(), size);
+ }
+
+ @Test
+ public void contains() throws Exception {
+ ArrayList keys = new ArrayList<>();
+ ArrayList values = new ArrayList<>();
+ DictionaryImpl d = new DictionaryImpl();
+ assertFalse(d.contains(null));
+
+ assertNull(d.put("key", null));
+ assertNull(d.get("key"));
+ assertTrue(d.contains("key"));
+ assertNull(d.remove("key"));
+ assertFalse(d.contains("key"));
+
+ // section "put".
+ for (int i = 0; i < NTESTS; ++i) {
+ String key = randomString();
+ String value = randomString();
+ if (keys.contains(key)) {
+ int index = keys.indexOf(key);
+ assertEquals(d.put(key, value), values.get(index));
+ values.set(index, value);
+ } else {
+ keys.add(key);
+ values.add(value);
+ assertNull(d.put(key, value), null);
+ }
+ assertTrue(d.contains(key));
+ assertEquals(value, d.get(key));
+ }
+
+ // section "contains".
+ for (int i = 0; i < NTESTS; ++i) {
+ int index = RANDOMIZER.nextInt(keys.size());
+ assertTrue(d.contains(keys.get(index)));
+ assertEquals(d.get(keys.get(index)), values.get(index));
+ }
+
+ // section "remove".
+ boolean[] used = new boolean[keys.size()];
+ Arrays.fill(used, false);
+ for (int i = 0; i < NTESTS; ++i) {
+ int index = RANDOMIZER.nextInt(keys.size());
+ String key = keys.get(index);
+ String value = values.get(index);
+ if (used[index]) {
+ assertNull(d.remove(key), null);
+ } else {
+ assertEquals(d.remove(key), value);
+ assertNull(d.remove(key), null);
+ assertNull(d.remove(key), null);
+ used[index] = true;
+ }
+ }
+ }
+
+ private static boolean tossCoin(int truePercentage) {
+ final int fullPercentage = 100;
+ return RANDOMIZER.nextInt(fullPercentage) < truePercentage;
+ }
+
+ @Test
+ public void clear() throws Exception {
+ DictionaryImpl d = new DictionaryImpl();
+ assertEquals(d.size(), 0);
+ d.clear();
+ assertEquals(d.size(), 0);
+
+ ArrayList keys = new ArrayList<>();
+ final int clearFrequency = 3;
+ for (int i = 0; i < NTESTS; ++i) {
+ String random = randomString();
+ assertEquals(keys.contains(random), d.contains(random));
+ String key = randomString();
+ String value = randomString();
+ if (keys.contains(key)) {
+ continue;
+ }
+ assertEquals(keys.contains(key), d.contains(key));
+ d.put(key, value);
+ if (!keys.contains(key)) {
+ keys.add(key);
+ }
+ if (tossCoin(clearFrequency)) {
+ assertEquals(d.size(), keys.size());
+ for (String k : keys) {
+ assertTrue(d.contains(k));
+ }
+ d.clear();
+ assertEquals(d.size(), 0);
+ for (String k : keys) {
+ assertFalse(d.contains(k));
+ }
+ keys.clear();
+ }
+
+ }
+ }
+
+ @Test
+ public void testEmpty() throws Exception {
+ DictionaryImpl d = new DictionaryImpl();
+ String emptyValue = null;
+ ArrayList keys = new ArrayList<>();
+
+ final int putEmptyFrequency = 25;
+ final int removeEmptyFrequency = 50;
+ final int removeAnyFrequency = 30;
+ for (int i = 0; i < NTESTS; ++i) {
+ if (tossCoin(putEmptyFrequency)) {
+ String newEmptyValue = randomString();
+ assertEquals(d.put("", newEmptyValue), emptyValue);
+ keys.add("");
+ emptyValue = newEmptyValue;
+ }
+ if (tossCoin(removeEmptyFrequency)) {
+ assertEquals(d.remove(""), emptyValue);
+ keys.remove("");
+ emptyValue = null;
+ }
+
+ if (tossCoin(removeAnyFrequency) && !keys.isEmpty()) {
+ String key = keys.get(RANDOMIZER.nextInt(keys.size()));
+ if (!key.isEmpty()) {
+ d.remove(key);
+ keys.remove(key);
+ assertEquals(d.size(), keys.size());
+ }
+ }
+ }
+ }
+
+ @Test
+ public void remove() {
+ DictionaryImpl d = new DictionaryImpl();
+ ArrayList keys = new ArrayList<>();
+ for (int i = 0; i < NTESTS; ++i) {
+ String key = randomString();
+ String value = randomString();
+ assertEquals(d.contains(key), keys.contains(key));
+ if (!keys.contains(key)) {
+ d.put(key, value);
+ assertTrue(d.contains(key));
+ d.remove(key);
+ assertFalse(d.contains(key));
+ }
+ }
+
+ for (int i = 0; i < NTESTS; ++i) {
+ String key = randomString();
+ String value = randomString();
+ if (keys.contains(key)) {
+ continue;
+ }
+ assertNull(d.put(key, value));
+ keys.add(key);
+ }
+
+ assertEquals(keys.size(), d.size());
+ for (String key : keys) {
+ assertTrue(d.contains(key));
+ d.put(key, randomString());
+ assertNotNull(d.remove(key));
+ assertNull(d.get(key));
+ assertFalse(d.contains(key));
+ d.put(key, randomString());
+ assertTrue(d.contains(key));
+ assertNotNull(d.get(key));
+ }
+
+ assertEquals(keys.size(), d.size());
+ for (String key : keys) {
+ assertTrue(d.contains(key));
+ assertNotNull(d.remove(key));
+ assertNull(d.get(key));
+ }
+ assertEquals(d.size(), 0);
+ }
+
+ @Test
+ public void hashMapTest() {
+ HashMap hm = new HashMap<>();
+ DictionaryImpl d = new DictionaryImpl();
+
+ final int clearFrequency = 50;
+ final int insertFrequency = 94;
+ final int removeFrequency = 60;
+
+ ArrayList keys = new ArrayList<>();
+ String key1 = randomString();
+
+ for (int i = 0; i < NTESTS; ++i) {
+ assertTrue(i==0 || d.contains(key1));
+// if (i > 0)
+ d.put(key1, randomString());
+ assertNotNull(d.put(key1, randomString()));
+ }
+
+ hm.put(key1, d.get(key1));
+ keys.add(key1);
+
+ for (int i = 0; i < NTESTS; ++i) {
+
+ if (tossCoin(clearFrequency)) {
+ hm.clear();
+ keys.clear();
+ d.clear();
+ }
+
+ if (tossCoin(insertFrequency)) {
+ String key = randomString();
+ String value = randomString();
+ assertEquals(hm.put(key, value), d.put(key, value));
+ if (!keys.contains(key)) {
+ keys.add(key);
+ }
+ }
+
+ if (!keys.isEmpty() && tossCoin(removeFrequency)) {
+ int j = RANDOMIZER.nextInt(keys.size());
+ String key = keys.get(j);
+ assertEquals(hm.remove(key), d.remove(key));
+ keys.remove(key);
+ }
+ assertEquals(keys.size(), hm.size());
+ assertEquals(hm.size(), d.size());
+ }
+
+ for (String key : keys) {
+ assertTrue(d.contains(key));
+ assertEquals(d.get(key), hm.get(key));
+ assertEquals(d.remove(key), hm.remove(key));
+ }
+ }
+}