From b5daed3dfa5a63aaf236fbf4ee9ce9b3ae783733 Mon Sep 17 00:00:00 2001
From: Arno Onken <asnelt@asnelt.org>
Date: Fri, 30 Dec 2016 18:43:38 +0100
Subject: [PATCH] Update to version 2.0. Added support for number type
 detection. Added support for floating point numbers for Mersenne Twister.
 Changed app colors.

---
 Derandom.iml                                  |  17 +-
 README.md                                     |  22 +-
 app/app.iml                                   |  92 ++-
 app/build.gradle                              |  17 +-
 app/src/main/AndroidManifest.xml              |  10 +-
 .../derandom/DisplayParametersActivity.java   |   3 +-
 .../org/asnelt/derandom/HistoryBuffer.java    |  18 +-
 .../java/org/asnelt/derandom/HistoryView.java |  61 +-
 .../derandom/LinearCongruentialGenerator.java | 139 ++--
 .../org/asnelt/derandom/MainActivity.java     |  77 +-
 .../org/asnelt/derandom/MersenneTwister.java  | 507 +++++++++++-
 .../org/asnelt/derandom/NumberSequence.java   | 722 ++++++++++++++++++
 .../asnelt/derandom/NumberSequenceView.java   |  78 ++
 .../asnelt/derandom/ProcessingFragment.java   | 209 ++---
 .../org/asnelt/derandom/RandomManager.java    | 120 +--
 .../derandom/RandomNumberGenerator.java       | 119 ++-
 .../org/asnelt/derandom/SettingsActivity.java |   5 +-
 .../res/drawable-hdpi/ic_action_discard.png   | Bin 450 -> 161 bytes
 .../res/drawable-hdpi/ic_action_refresh.png   | Bin 663 -> 528 bytes
 .../res/drawable-mdpi/ic_action_discard.png   | Bin 324 -> 115 bytes
 .../res/drawable-mdpi/ic_action_refresh.png   | Bin 508 -> 360 bytes
 .../res/drawable-xhdpi/ic_action_discard.png  | Bin 543 -> 151 bytes
 .../res/drawable-xhdpi/ic_action_refresh.png  | Bin 895 -> 665 bytes
 .../res/drawable-xxhdpi/ic_action_discard.png | Bin 765 -> 194 bytes
 .../res/drawable-xxhdpi/ic_action_refresh.png | Bin 1239 -> 982 bytes
 .../drawable-xxxhdpi/ic_action_discard.png    | Bin 0 -> 243 bytes
 .../drawable-xxxhdpi/ic_action_refresh.png    | Bin 0 -> 1326 bytes
 .../main/res/drawable-xxxhdpi/ic_launcher.png | Bin 0 -> 39782 bytes
 .../layout/activity_display_parameters.xml    |   2 +-
 app/src/main/res/layout/activity_main.xml     |  32 +-
 app/src/main/res/layout/activity_settings.xml |   2 +-
 app/src/main/res/layout/dialog_about.xml      |  10 +-
 app/src/main/res/menu/display_parameters.xml  |   2 +-
 app/src/main/res/menu/main.xml                |   7 +-
 app/src/main/res/menu/settings.xml            |   2 +-
 app/src/main/res/values-v11/styles.xml        |   2 +-
 app/src/main/res/values-v14/styles.xml        |   2 +-
 app/src/main/res/values-v23/styles.xml        |  28 +
 app/src/main/res/values-w820dp/dimens.xml     |   2 +-
 app/src/main/res/values/colors.xml            |  29 +
 app/src/main/res/values/dimens.xml            |   2 +-
 app/src/main/res/values/strings.xml           |  11 +-
 app/src/main/res/values/styles.xml            |  28 +-
 app/src/main/res/xml-v14/preferences.xml      |  61 ++
 app/src/main/res/xml/backup.xml               |   2 +-
 app/src/main/res/xml/preferences.xml          |   2 +-
 build.gradle                                  |   4 +-
 gradle/wrapper/gradle-wrapper.properties      |  17 +-
 gradlew.bat                                   | 180 ++---
 settings.gradle                               |   2 +-
 50 files changed, 2086 insertions(+), 559 deletions(-)
 create mode 100644 app/src/main/java/org/asnelt/derandom/NumberSequence.java
 create mode 100644 app/src/main/java/org/asnelt/derandom/NumberSequenceView.java
 create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_action_discard.png
 create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_action_refresh.png
 create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_launcher.png
 create mode 100644 app/src/main/res/values-v23/styles.xml
 create mode 100644 app/src/main/res/values/colors.xml
 create mode 100644 app/src/main/res/xml-v14/preferences.xml

diff --git a/Derandom.iml b/Derandom.iml
index 67ecff1..0ebb4bf 100644
--- a/Derandom.iml
+++ b/Derandom.iml
@@ -1,19 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2015 Arno Onken
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
 <module external.linked.project.id="Derandom" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
   <component name="FacetManager">
     <facet type="java-gradle" name="Java-Gradle">
@@ -31,4 +16,4 @@ limitations under the License.
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>
-</module>
+</module>
\ No newline at end of file
diff --git a/README.md b/README.md
index d439d91..5251dae 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,10 @@ generator like, for instance, the Java standard pseudo random number
 generator or the Mersenne Twister MT19937.  The app will then try to
 predict following numbers from the generator.
 
-The app expects all numbers to be entered as signed integers.  Three
-input modes are supported:
+The app expects all numbers to be entered as integers or floating point
+numbers between zero and one.  Currently, floating point numbers are
+supported for the Mersenne Twister only.  Three input modes are
+supported:
 
 1. *Text field* lets you enter the numbers directly on the device.
 2. *File* lets you choose a file with newline separated number strings.
@@ -64,13 +66,13 @@ s.close()
 ```
 Start the app on the Android device and set the input spinner from
 *Text field* to *Socket*.  Make sure that the device and the Derandom
-socket port (default 6869) is reachable in your network.  Then set `HOST`
-in the Python program to the address of your Android device and run the
-program.  For each number that is sent by the Python program, eight
-predictions are returned by Derandom and displayed by the Python program.
-After the app has received 624 numbers the Python Mersenne Twister should
-be detected and, in the app, numbers in the prediction history should
-appear in green instead of red.
+socket port (default 6869) are reachable in your network.  Then set
+`HOST` in the Python program to the address of your Android device and
+run the program.  For each number that is sent by the Python program,
+eight predictions are returned by Derandom and displayed by the Python
+program.  After the app has received 624 numbers the Python Mersenne
+Twister should be detected and, in the app, numbers in the prediction
+history should appear in green instead of red.
 
 
 Building from source
@@ -88,7 +90,7 @@ License
 -------
 
 ```text
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/app.iml b/app/app.iml
index 227d02d..37f87bb 100644
--- a/app/app.iml
+++ b/app/app.iml
@@ -1,19 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2015 Arno Onken
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
 <module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="Derandom" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
   <component name="FacetManager">
     <facet type="android-gradle" name="Android-Gradle">
@@ -27,10 +12,7 @@ limitations under the License.
         <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
         <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
         <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
-        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
-        <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
         <afterSyncTasks>
-          <task>generateDebugAndroidTestSources</task>
           <task>generateDebugSources</task>
         </afterSyncTasks>
         <option name="ALLOW_USER_CONFIGURATION" value="false" />
@@ -43,19 +25,21 @@ limitations under the License.
   </component>
   <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
     <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
-    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
+    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
       <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
@@ -65,6 +49,15 @@ limitations under the License.
       <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
@@ -72,6 +65,7 @@ limitations under the License.
       <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
@@ -79,34 +73,60 @@ limitations under the License.
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.1/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.0.1/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/25.1.0/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/25.1.0/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-compat/25.1.0/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-ui/25.1.0/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-utils/25.1.0/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-fragment/25.1.0/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-media-compat/25.1.0/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/25.1.0/jars" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/25.1.0/jars" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-resources" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
       <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
       <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
       <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
     </content>
-    <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
+    <orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" exported="" name="appcompat-v7-23.0.1" level="project" />
-    <orderEntry type="library" exported="" name="support-v4-23.0.1" level="project" />
-    <orderEntry type="library" exported="" name="support-annotations-23.0.1" level="project" />
+    <orderEntry type="library" exported="" name="support-compat-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="support-fragment-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="animated-vector-drawable-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="support-annotations-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="support-v4-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="support-core-ui-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="support-media-compat-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="support-vector-drawable-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="appcompat-v7-25.1.0" level="project" />
+    <orderEntry type="library" exported="" name="support-core-utils-25.1.0" level="project" />
   </component>
-</module>
+</module>
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index a8cba3f..2042f1b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,13 +16,14 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 23
-    buildToolsVersion "23.0.1"
+    compileSdkVersion 25
+    buildToolsVersion "25.0.2"
 
     defaultConfig {
         applicationId "org.asnelt.derandom"
-        minSdkVersion 7
-        targetSdkVersion 23
+        minSdkVersion 9
+        targetSdkVersion 25
+        return void
     }
 
     buildTypes {
@@ -31,9 +32,11 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
         }
     }
+
+    return void
 }
 
 dependencies {
-    compile 'com.android.support:appcompat-v7:23.0.1'
-    compile 'com.android.support:support-v4:23.0.1'
+    compile 'com.android.support:appcompat-v7:25.1.0'
+    compile 'com.android.support:support-v4:25.1.0'
 }
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a1bf0f4..b93bb9a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
 limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="org.asnelt.derandom"
-    android:versionCode="9"
-    android:versionName="1.8" >
+    android:versionCode="10"
+    android:versionName="2.0" >
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.INTERNET" />
@@ -27,7 +28,8 @@ limitations under the License.
         android:fullBackupContent="@xml/backup"
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name"
-        android:theme="@style/AppTheme" >
+        android:theme="@style/AppTheme"
+        tools:ignore="GoogleAppIndexingWarning">
         <activity
             android:name=".MainActivity"
             android:label="@string/app_name"
diff --git a/app/src/main/java/org/asnelt/derandom/DisplayParametersActivity.java b/app/src/main/java/org/asnelt/derandom/DisplayParametersActivity.java
index 3c76964..7b6f3e2 100644
--- a/app/src/main/java/org/asnelt/derandom/DisplayParametersActivity.java
+++ b/app/src/main/java/org/asnelt/derandom/DisplayParametersActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -110,6 +110,7 @@ public class DisplayParametersActivity extends AppCompatActivity {
             textParameters[i].setInputType(InputType.TYPE_CLASS_NUMBER);
             // Remove the following line to make fields editable
             textParameters[i].setKeyListener(null);
+            textParameters[i].setEnabled(false);
             layoutParameters.addView(textParameters[i]);
         }
         scrollViewParameters.addView(layoutParameters);
diff --git a/app/src/main/java/org/asnelt/derandom/HistoryBuffer.java b/app/src/main/java/org/asnelt/derandom/HistoryBuffer.java
index 566f019..a0d0db4 100644
--- a/app/src/main/java/org/asnelt/derandom/HistoryBuffer.java
+++ b/app/src/main/java/org/asnelt/derandom/HistoryBuffer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ import java.nio.BufferUnderflowException;
 /**
  * This class implements a ring buffer for storing long numbers.
  */
-public class HistoryBuffer {
+class HistoryBuffer {
     /** The maximum number of elements in the buffer. */
     private int capacity;
     /** The array for storing the elements. */
@@ -35,7 +35,7 @@ public class HistoryBuffer {
      * Constructs an empty buffer with a given capacity.
      * @param capacity the maximum number of elements the buffer can hold
      */
-    public HistoryBuffer(int capacity) {
+    HistoryBuffer(int capacity) {
         clear();
         setCapacity(capacity);
     }
@@ -43,7 +43,7 @@ public class HistoryBuffer {
     /**
      * Removes all elements from the buffer.
      */
-    public void clear() {
+    void clear() {
         head = 0;
         tail = -1;
         numbers = new long[0];
@@ -54,7 +54,7 @@ public class HistoryBuffer {
      * @param capacity the new capacity
      * @throws IllegalArgumentException if the capacity is less than zero
      */
-    public void setCapacity(int capacity) throws IllegalArgumentException {
+    void setCapacity(int capacity) throws IllegalArgumentException {
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity must not be negative");
         }
@@ -77,7 +77,7 @@ public class HistoryBuffer {
      * Puts new elements into the buffer.
      * @param incomingNumbers the numbers to store
      */
-    public void put(long[] incomingNumbers) {
+    void put(long[] incomingNumbers) {
         if (incomingNumbers.length == 0) {
             return;
         }
@@ -116,7 +116,7 @@ public class HistoryBuffer {
      * @return the number that was last put into the buffer
      * @throws BufferUnderflowException if the buffer is empty
      */
-    public long getLast() throws BufferUnderflowException {
+    long getLast() throws BufferUnderflowException {
         if (tail < 0) {
             // Empty buffer
             throw new BufferUnderflowException();
@@ -130,7 +130,7 @@ public class HistoryBuffer {
      * @return the numbers that were last put into the buffer
      * @throws BufferUnderflowException if range is greater than the number of buffer elements
      */
-    public long[] getLast(int range) throws BufferUnderflowException {
+    private long[] getLast(int range) throws BufferUnderflowException {
         if (range > length()) {
             throw new BufferUnderflowException();
         }
@@ -151,7 +151,7 @@ public class HistoryBuffer {
      * Returns all elements that are stored in the buffer
      * @return all elements in the buffer
      */
-    public long[] toArray() {
+    long[] toArray() {
         return getLast(length());
     }
 
diff --git a/app/src/main/java/org/asnelt/derandom/HistoryView.java b/app/src/main/java/org/asnelt/derandom/HistoryView.java
index 76c03ab..2b200b0 100644
--- a/app/src/main/java/org/asnelt/derandom/HistoryView.java
+++ b/app/src/main/java/org/asnelt/derandom/HistoryView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,12 +23,11 @@ import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.style.ForegroundColorSpan;
 import android.util.AttributeSet;
-import android.widget.TextView;
 
 /**
  * A view for displaying a HistoryBuffer.
  */
-public class HistoryView extends TextView {
+public class HistoryView extends NumberSequenceView {
     /**
      * Interface for listening to scroll change events.
      */
@@ -177,41 +176,36 @@ public class HistoryView extends TextView {
         setText(getText().toString());
     }
 
-    /**
-     * Clears the view.
-     */
-    public void clear() {
-        setText("");
-    }
-
     /**
      * Appends numbers to the text that is displayed.
-     * @param numbers numbers to display
+     * @param numberSequence number sequence to display
      */
-    public void appendNumbers(long[] numbers) {
-        appendNumbers(numbers, null);
+    @Override
+    public void append(NumberSequence numberSequence) {
+        append(numberSequence, null);
     }
 
     /**
-     * Appends numbers to the text that is displayed. If correctNumbers is not null and color is
-     * enabled then the numbers are colored. The numbers are colored green if they match the
-     * corresponding correctNumbers or red otherwise.
-     * @param numbers numbers to display
-     * @param correctNumbers numbers to compare to
+     * Appends numbers to the text that is displayed. If correctNumberSequence is not null and color
+     * is enabled then the numbers are colored. The numbers are colored green if they match the
+     * corresponding correctNumberSequence or red otherwise.
+     * @param numberSequence number sequence to display
+     * @param correctNumberSequence numbers to compare to
      */
-    public void appendNumbers(long[] numbers, long[] correctNumbers) {
-        if (numbers == null || numbers.length == 0) {
+    public void append(NumberSequence numberSequence, NumberSequence correctNumberSequence) {
+        if (numberSequence == null || numberSequence.isEmpty()) {
             return;
         }
+        int length = numberSequence.length();
         // Number of lines to remove from beginning of textView
-        int linesToRemove = getLineCount() + numbers.length - capacity;
+        int linesToRemove = getLineCount() + length - capacity;
         removeExcessNumbers(linesToRemove);
         // Offset to first number to append
-        int offset = numbers.length - capacity;
+        int offset = length - capacity;
         if (offset < 0) {
             offset = 0;
         }
-        showNumbers(numbers, correctNumbers, offset);
+        showNumbers(numberSequence, correctNumberSequence, offset);
         Layout layout = getLayout();
         if (layout != null) {
             scrollTo(0, layout.getHeight());
@@ -244,28 +238,31 @@ public class HistoryView extends TextView {
     /**
      * Shows numbers in the view. If correctNumbers is not null and color is enabled then the
      * numbers are colored.
-     * @param numbers the numbers to show
-     * @param correctNumbers the numbers to compare to
+     * @param numberSequence the number sequence to show
+     * @param correctNumberSequence the numbers to compare to
      * @param offset index of the first number to show
      */
-    private void showNumbers(long[] numbers, long[] correctNumbers, int offset) {
-        if (numbers == null || numbers.length == 0) {
+    private void showNumbers(NumberSequence numberSequence, NumberSequence correctNumberSequence,
+                             int offset) {
+        if (numberSequence == null || numberSequence.isEmpty()) {
             return;
         }
+        int length = numberSequence.length();
         // Check whether the numbers should be colored
-        boolean useColor = colored && correctNumbers != null
-                && correctNumbers.length >= numbers.length;
+        boolean useColor = colored && correctNumberSequence != null
+                && correctNumberSequence.length() >= length;
         // Check whether we need a newline at the beginning
         boolean initialNewline = getText().length() > 0;
         // Append colored numbers
-        for (int i = offset; i < numbers.length; i++) {
+        for (int i = offset; i < length; i++) {
             if (i > offset || initialNewline) {
                 append("\n");
             }
-            String numberString = Long.toString(numbers[i]);
+            String numberString = numberSequence.toString(i);
             if (useColor) {
                 Spannable coloredNumberString = new SpannableString(numberString);
-                if (numbers[i] == correctNumbers[i]) {
+                if (numberSequence.getInternalNumber(i)
+                        == correctNumberSequence.getInternalNumber(i)) {
                     ForegroundColorSpan colorGreen = new ForegroundColorSpan(Color.GREEN);
                     coloredNumberString.setSpan(colorGreen, 0, coloredNumberString.length(),
                             Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
diff --git a/app/src/main/java/org/asnelt/derandom/LinearCongruentialGenerator.java b/app/src/main/java/org/asnelt/derandom/LinearCongruentialGenerator.java
index 4c8190b..70a3d3e 100644
--- a/app/src/main/java/org/asnelt/derandom/LinearCongruentialGenerator.java
+++ b/app/src/main/java/org/asnelt/derandom/LinearCongruentialGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,9 +22,7 @@ import java.util.List;
 /**
  * This class implements a linear congruential random number generator.
  */
-public class LinearCongruentialGenerator extends RandomNumberGenerator {
-    /** Human readable name of the generator. */
-    private final String name;
+class LinearCongruentialGenerator extends RandomNumberGenerator {
     /** Multiplier parameter. */
     private final long multiplier;
     /** Human readable name of multiplier parameter. */
@@ -54,8 +52,6 @@ public class LinearCongruentialGenerator extends RandomNumberGenerator {
     };
     /** The parameter names as a list. */
     private static final List PARAMETER_NAMES_LIST = Arrays.asList(PARAMETER_NAMES);
-    /** Two complement bit extension for negative integers. */
-    private static final long COMPLEMENT_INTEGER_EXTENSION = 0xFFFFFFFF00000000L;
     /** Internal state. */
     private volatile long state;
     /** Index of most significant modulus bit. */
@@ -75,9 +71,9 @@ public class LinearCongruentialGenerator extends RandomNumberGenerator {
      * @param bitRangeStart start index of output bits
      * @param bitRangeStop stop index of output bits
      */
-    public LinearCongruentialGenerator(String name, long multiplier, long increment, long modulus,
-                                       long seed, int bitRangeStart, int bitRangeStop) {
-        this.name = name;
+    LinearCongruentialGenerator(String name, long multiplier, long increment, long modulus,
+                                long seed, int bitRangeStart, int bitRangeStop) {
+        super(name);
         this.multiplier = multiplier;
         this.increment = increment;
         if (modulus == 0L) {
@@ -86,7 +82,7 @@ public class LinearCongruentialGenerator extends RandomNumberGenerator {
         this.modulus = modulus;
         modulusBitRangeStop = Long.SIZE - Long.numberOfLeadingZeros(modulus) - 1;
         this.initialSeed = seed;
-        this.state = seed;
+        setState(seed);
         // Check index range
         if (bitRangeStart < 0) {
             throw new IllegalArgumentException("bitRangeStart must not be negative");
@@ -113,7 +109,8 @@ public class LinearCongruentialGenerator extends RandomNumberGenerator {
      * Resets the generator to its initial seed.
      */
     @Override
-    public void reset() {
+    public synchronized void reset() {
+        super.reset();
         setState(initialSeed);
     }
 
@@ -121,19 +118,10 @@ public class LinearCongruentialGenerator extends RandomNumberGenerator {
      * Sets the state of the generator.
      * @param state the complete state of the generator
      */
-    public synchronized void setState(long state) {
+    private synchronized void setState(long state) {
         this.state = state;
     }
 
-    /**
-     * Returns the name of the generator.
-     * @return name of the generator
-     */
-    @Override
-    public String getName() {
-        return name;
-    }
-
     /**
      * Returns human readable names of all parameters.
      * @return a string array of parameter names
@@ -181,34 +169,57 @@ public class LinearCongruentialGenerator extends RandomNumberGenerator {
     }
 
     /**
-     * Find prediction numbers that match the input series and update the state accordingly.
+     * Find prediction numbers that match the input sequence and update the state accordingly.
      * @param incomingNumbers new input numbers
      * @param historyBuffer previous input numbers
-     * @return predicted numbers that best match input series
+     * @return predicted numbers that best match input sequence
      */
     @Override
-    public synchronized long[] findSeries(long[] incomingNumbers, HistoryBuffer historyBuffer) {
-        long[] predicted = new long[incomingNumbers.length];
-        if (incomingNumbers.length == 0) {
-            // Empty input
-            return predicted;
-        }
-        // Make prediction based on current state
-        predicted[0] = next();
-        if (predicted[0] != incomingNumbers[0]) {
-            if (historyBuffer == null || historyBuffer.length() == 0) {
-                // No history present; just guess incoming number as new state
-                setState(incomingNumbers[0]);
-            } else {
-                // We have a pair to work with
-                setState(findState(historyBuffer.getLast(), incomingNumbers[0]));
+    public synchronized NumberSequence findSequence(NumberSequence incomingNumbers,
+                                                    HistoryBuffer historyBuffer) {
+        NumberSequence predicted;
+        if (incomingNumbers == null) {
+            predicted = new NumberSequence();
+        } else if (incomingNumbers.isEmpty()) {
+            predicted = new NumberSequence(incomingNumbers.getNumberType());
+        } else if (incomingNumbers.hasTruncatedOutput()) {
+            // Make prediction based on current state
+            predicted = peekNextOutputs(incomingNumbers.length(), incomingNumbers.getNumberType());
+            // Check whether the current state is compatible with the incoming numbers
+            if (predicted.equals(incomingNumbers)) {
+                return nextOutputs(incomingNumbers.length(), incomingNumbers.getNumberType());
             }
-        }
-        for (int i = 1; i < incomingNumbers.length; i++) {
-            predicted[i] = next();
-            if (predicted[i] != incomingNumbers[i]) {
-                setState(findState(incomingNumbers[i-1], incomingNumbers[i]));
+            // No option found, so disable generator; lattice reduction will solve this problem at
+            // some point
+            setActive(false);
+        } else {
+            int wordSize = getWordSize();
+            long[] incomingWords = incomingNumbers.getSequenceWords(wordSize);
+            NumberSequence.NumberType numberType = incomingNumbers.getNumberType();
+            // Make prediction based on current state
+            predicted = nextOutputs(incomingNumbers.length(), numberType);
+            long[] predictedWords = predicted.getSequenceWords(wordSize);
+            if (predictedWords[0] != incomingWords[0]) {
+                if (historyBuffer == null || historyBuffer.length() == 0) {
+                    // No history present; just guess incoming number as new state
+                    setState(incomingWords[0]);
+                } else {
+                    // We have a pair to work with
+                    NumberSequence lastNumber = new NumberSequence(
+                            new long[]{historyBuffer.getLast()}, numberType);
+                    long[] historyWords = lastNumber.getSequenceWords(wordSize);
+                    long lastHistoryWord = historyWords[historyWords.length - 1];
+                    setState(findState(lastHistoryWord, incomingWords[0]));
+                }
+            }
+            for (int i = 1; i < incomingWords.length && isActive(); i++) {
+                long nextWord = next();
+                predicted.setSequenceWord(i, nextWord, wordSize);
+                if (nextWord != incomingWords[i]) {
+                    setState(findState(incomingWords[i - 1], incomingWords[i]));
+                }
             }
+            predicted.fixNumberFormat();
         }
         return predicted;
     }
@@ -224,12 +235,43 @@ public class LinearCongruentialGenerator extends RandomNumberGenerator {
     }
 
     /**
-     * Calculate the state of the generator based on two consecutive values.
+     * Returns the word size of the generator.
+     * @return the word size
+     */
+    protected int getWordSize() {
+        return bitRangeStop - bitRangeStart + 1;
+    }
+
+    /**
+     * Returns the state of the generator.
+     * @return the current state
+     */
+    @Override
+    protected long[] getState() {
+        long[] state = new long[1];
+        state[0] = this.state;
+        return state;
+    }
+
+    /**
+     * Sets the state of the generator.
+     * @param state the new state
+     * @throws IllegalArgumentException if state is empty
+     */
+    protected synchronized void setState(long[] state) {
+        if (state == null || state.length < 1) {
+            throw new IllegalArgumentException();
+        }
+        this.state = state[0];
+    }
+
+    /**
+     * Calculates the state of the generator based on two consecutive values.
      * @param number one output of the generator
      * @param successor next output of the generator
      * @return the state of the generator after the successor value
      */
-    private long findState(long number, long successor) {
+    private synchronized long findState(long number, long successor) {
         // Undo output shift
         number <<= bitRangeStart;
         // Number of leading bits that are hidden
@@ -258,12 +300,7 @@ public class LinearCongruentialGenerator extends RandomNumberGenerator {
      * @return the output of the generator
      */
     private long calculateOutput(long state) {
-        long output = (state & mask) >> bitRangeStart;
-        // For integers add two complement bit extension for negative numbers
-        if (bitRangeStop - bitRangeStart + 1 == Integer.SIZE && output >> Integer.SIZE-1 > 0) {
-            output |= COMPLEMENT_INTEGER_EXTENSION;
-        }
-        return output;
+        return (state & mask) >> bitRangeStart;
     }
 
     /**
diff --git a/app/src/main/java/org/asnelt/derandom/MainActivity.java b/app/src/main/java/org/asnelt/derandom/MainActivity.java
index 6570756..503aa61 100644
--- a/app/src/main/java/org/asnelt/derandom/MainActivity.java
+++ b/app/src/main/java/org/asnelt/derandom/MainActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,7 +18,8 @@ package org.asnelt.derandom;
 
 import android.Manifest;
 import android.annotation.SuppressLint;
-import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
@@ -31,6 +32,7 @@ import android.support.v4.app.ActivityCompat;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.content.ContextCompat;
 import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.text.InputType;
 import android.text.Layout;
@@ -84,7 +86,7 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
     /** Field for displaying predictions for previous numbers. */
     private HistoryView textHistoryPrediction;
     /** Field for displaying predictions. */
-    private TextView textPrediction;
+    private NumberSequenceView textPrediction;
     /** Field for entering input. */
     private EditText textInput;
     /** Spinner for selecting the input method. */
@@ -110,7 +112,7 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
         textHistoryInput.setHistoryViewListener(this);
         textHistoryPrediction = (HistoryView) findViewById(R.id.text_history_prediction);
         textHistoryPrediction.setHistoryViewListener(this);
-        textPrediction = (TextView) findViewById(R.id.text_prediction);
+        textPrediction = (NumberSequenceView) findViewById(R.id.text_prediction);
         textInput = (EditText) findViewById(R.id.text_input);
         spinnerInput = (Spinner) findViewById(R.id.spinner_input);
         spinnerGenerator = (Spinner) findViewById(R.id.spinner_generator);
@@ -127,7 +129,6 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
         ActionBar actionBar = getSupportActionBar();
         if (actionBar != null) {
             actionBar.setDisplayShowHomeEnabled(true);
-            actionBar.setIcon(R.drawable.ic_launcher);
         }
 
         FragmentManager fragmentManager = getSupportFragmentManager();
@@ -268,22 +269,23 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case R.id.action_refresh:
-                processInput();
+                if (processingFragment.getInputSelection() == INDEX_FILE_INPUT) {
+                    selectTextFile();
+                } else {
+                    processInput();
+                }
                 return true;
             case R.id.action_discard:
                 clearInput();
                 return true;
             case R.id.action_parameters:
-                openParameters();
+                openActivityParameters();
                 return true;
             case R.id.action_settings:
-                openSettings();
+                openActivitySettings();
                 return true;
             case R.id.action_about:
-                openAbout();
-                return true;
-            case R.id.action_exit:
-                finish();
+                openDialogAbout();
                 return true;
             default:
                 return super.onOptionsItemSelected(item);
@@ -393,10 +395,10 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
      * @param historyNumbers previously entered numbers
      * @param historyPredictionNumbers predictions for previous numbers
      */
-    public void onHistoryPredictionReplaced(long[] historyNumbers,
-                                            long[] historyPredictionNumbers) {
+    public void onHistoryPredictionReplaced(NumberSequence historyNumbers,
+                                            NumberSequence historyPredictionNumbers) {
         textHistoryPrediction.clear();
-        textHistoryPrediction.appendNumbers(historyPredictionNumbers, historyNumbers);
+        textHistoryPrediction.append(historyPredictionNumbers, historyNumbers);
     }
 
     /**
@@ -413,28 +415,23 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
      * @param inputNumbers the entered numbers
      * @param predictionNumbers predictions for entered numbers
      */
-    public void onHistoryChanged(long[] inputNumbers, long[] predictionNumbers) {
+    public void onHistoryChanged(NumberSequence inputNumbers, NumberSequence predictionNumbers) {
         // Appends input numbers to history
-        textHistoryInput.appendNumbers(inputNumbers);
-        textHistoryPrediction.appendNumbers(predictionNumbers, inputNumbers);
+        textHistoryInput.append(inputNumbers);
+        textHistoryPrediction.append(predictionNumbers, inputNumbers);
     }
 
     /**
      * Called when the predictions for upcoming numbers changed.
      * @param predictionNumbers predictions of upcoming numbers
      */
-    public void onPredictionChanged(long[] predictionNumbers) {
-        textPrediction.setText("");
+    public void onPredictionChanged(NumberSequence predictionNumbers) {
+        textPrediction.clear();
         if (predictionNumbers == null) {
             return;
         }
         // Append numbers
-        for (int i = 0; i < predictionNumbers.length; i++) {
-            if (i > 0) {
-                textPrediction.append("\n");
-            }
-            textPrediction.append(Long.toString(predictionNumbers[i]));
-        }
+        textPrediction.append(predictionNumbers);
         textPrediction.scrollTo(0, 0);
     }
 
@@ -445,7 +442,7 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
     public void onFileInputAborted() {
         enableDirectInput();
         String errorMessage = getResources().getString(R.string.file_error_message);
-        Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show();
+        Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_SHORT).show();
     }
 
     /**
@@ -455,7 +452,7 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
     public void onSocketInputAborted() {
         enableDirectInput();
         String errorMessage = getResources().getString(R.string.socket_error_message);
-        Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show();
+        Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_SHORT).show();
     }
 
     /**
@@ -463,7 +460,7 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
      */
     public void onInvalidInputNumber() {
         String errorMessage = getResources().getString(R.string.number_error_message);
-        Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show();
+        Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_SHORT).show();
     }
 
     /**
@@ -472,7 +469,7 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
     public void onClear() {
         textHistoryInput.clear();
         textHistoryPrediction.clear();
-        textPrediction.setText("");
+        textPrediction.clear();
         if (processingFragment.getInputSelection() == INDEX_DIRECT_INPUT) {
             // Direct input; reset textInput
             textInput.setText("");
@@ -547,11 +544,11 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
     private void clearInput() {
         processingFragment.clear();
     }
-	
+
     /**
      * Show generator parameters in a new activity. Called when the user clicks the parameters item.
      */
-    private void openParameters() {
+    private void openActivityParameters() {
         String name = processingFragment.getCurrentGeneratorName();
         String[] parameterNames = processingFragment.getCurrentParameterNames();
         long[] parameters = processingFragment.getCurrentParameters();
@@ -567,7 +564,7 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
     /**
      * Called when the user clicks the settings item.
      */
-    private void openSettings() {
+    private void openActivitySettings() {
         // Start new settings activity
         Intent intent = new Intent(this, SettingsActivity.class);
         startActivity(intent);
@@ -576,7 +573,7 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
     /**
      * Opens an about dialog. Called when the user clicks the about item.
      */
-    private void openAbout() {
+    private void openDialogAbout() {
         // Construct an about dialog
         String versionName;
         try {
@@ -584,17 +581,25 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
         } catch (PackageManager.NameNotFoundException e) {
             versionName = "unknown";
         }
+
         @SuppressLint("InflateParams")
         View inflater = getLayoutInflater().inflate(R.layout.dialog_about, null);
 
-        TextView textVersion = (TextView)inflater.findViewById(R.id.text_version);
+        TextView textVersion = (TextView) inflater.findViewById(R.id.text_version);
         textVersion.setText(String.format("%s %s", textVersion.getText().toString(), versionName));
 
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
         builder.setIcon(R.drawable.ic_launcher);
-        builder.setTitle("About " + getResources().getString(R.string.app_name));
+        builder.setTitle(getResources().getString(R.string.title_dialog_about) + " "
+                + getResources().getString(R.string.app_name));
         builder.setView(inflater);
         builder.create();
+        builder.setPositiveButton(R.string.about_positive, new Dialog.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
         builder.show();
     }
 
diff --git a/app/src/main/java/org/asnelt/derandom/MersenneTwister.java b/app/src/main/java/org/asnelt/derandom/MersenneTwister.java
index 47cf4df..3bcf589 100644
--- a/app/src/main/java/org/asnelt/derandom/MersenneTwister.java
+++ b/app/src/main/java/org/asnelt/derandom/MersenneTwister.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,9 +23,7 @@ import java.util.concurrent.atomic.AtomicLongArray;
 /**
  * This class implements a Mersenne Twister random number generator.
  */
-public class MersenneTwister extends RandomNumberGenerator {
-    /** Human readable name of the generator. */
-    private final String name;
+class MersenneTwister extends RandomNumberGenerator {
     /** Word size of the generator. */
     private final int wordSize;
     /** Human readable name of word size parameter. */
@@ -100,8 +98,8 @@ public class MersenneTwister extends RandomNumberGenerator {
     private final long lowerMask;
     /** Upper mask for state twist transformation. */
     private final long upperMask;
-    /** Two complement bit extension for negative integers. */
-    private static final long COMPLEMENT_INTEGER_EXTENSION = 0xFFFFFFFF00000000L;
+    /** Object for state recovery when the output is partly hidden. */
+    private volatile StateFinder stateFinder = null;
 
     /**
      * Constructor initializing all parameters.
@@ -121,11 +119,11 @@ public class MersenneTwister extends RandomNumberGenerator {
      * @param initializationMultiplier the multiplier parameter of the state initialization
      * @param seed initial seed of the generator
      */
-    public MersenneTwister(String name, int wordSize, int stateSize, int shiftSize, int maskBits,
-                           long twistMask, int temperingU, long temperingD, int temperingS,
-                           long temperingB, int temperingT, long temperingC, int temperingL,
-                           long initializationMultiplier, long seed) {
-        this.name = name;
+    MersenneTwister(String name, int wordSize, int stateSize, int shiftSize, int maskBits,
+                    long twistMask, int temperingU, long temperingD, int temperingS,
+                    long temperingB, int temperingT, long temperingC, int temperingL,
+                    long initializationMultiplier, long seed) {
+        super(name);
         this.wordSize = wordSize;
         this.shiftSize = shiftSize;
         this.maskBits = maskBits;
@@ -177,16 +175,9 @@ public class MersenneTwister extends RandomNumberGenerator {
      */
     @Override
     public synchronized void reset() {
+        super.reset();
         initialize(initialSeed);
-    }
-
-    /**
-     * Returns the name of the generator.
-     * @return name of the generator
-     */
-    @Override
-    public String getName() {
-        return name;
+        stateFinder = null;
     }
 
     /**
@@ -237,7 +228,8 @@ public class MersenneTwister extends RandomNumberGenerator {
         parameters[PARAMETER_NAMES_LIST.indexOf(TEMPERING_T_NAME)] = (long) temperingT;
         parameters[PARAMETER_NAMES_LIST.indexOf(TEMPERING_C_NAME)] = temperingC;
         parameters[PARAMETER_NAMES_LIST.indexOf(TEMPERING_L_NAME)] = (long) temperingL;
-        parameters[PARAMETER_NAMES_LIST.indexOf(INITIALIZATION_MULTIPLIER_NAME)] = initializationMultiplier;
+        parameters[PARAMETER_NAMES_LIST.indexOf(INITIALIZATION_MULTIPLIER_NAME)] =
+                initializationMultiplier;
         return parameters;
     }
 
@@ -288,22 +280,59 @@ public class MersenneTwister extends RandomNumberGenerator {
     }
 
     /**
-     * Find prediction numbers that match the input series and update the state accordingly.
+     * Find prediction numbers that match the input sequence and update the state accordingly.
      * @param incomingNumbers new input numbers
      * @param historyBuffer previous input numbers
-     * @return predicted numbers that best match input series
+     * @return predicted numbers that best match input sequence
      */
     @Override
-    public synchronized long[] findSeries(long[] incomingNumbers, HistoryBuffer historyBuffer) {
+    public synchronized NumberSequence findSequence(NumberSequence incomingNumbers,
+                                                    HistoryBuffer historyBuffer) {
         // Make prediction based on current state
-        long[] predicted = peekNext(incomingNumbers.length);
-        for (long number : incomingNumbers) {
-            if (index >= state.length()) {
-                twistState(state.length());
-                index = 0;
+        NumberSequence predicted = peekNextOutputs(incomingNumbers.length(),
+                incomingNumbers.getNumberType());
+
+        // Check whether the current state is compatible with the incoming numbers
+        if (predicted.equals(incomingNumbers)) {
+            return nextOutputs(incomingNumbers.length(), incomingNumbers.getNumberType());
+        }
+
+        int wordSize = getWordSize();
+        long[] incomingWords = incomingNumbers.getSequenceWords(wordSize);
+        if (incomingNumbers.hasTruncatedOutput()) {
+            try {
+                if (stateFinder == null) {
+                    stateFinder = new StateFinder();
+                }
+                boolean solved = false;
+                long[] observedWordBits = incomingNumbers.getObservedWordBits(wordSize);
+                for (int i = 0; i < incomingWords.length; i++) {
+                    stateFinder.addInput(incomingWords[i], observedWordBits[i]);
+                    solved = stateFinder.isSolved();
+                    if (solved) {
+                        // Advance state according to remaining numbers
+                        next(incomingWords.length - i - 1);
+                        stateFinder = null;
+                        break;
+                    }
+                }
+                if (!solved) {
+                    return nextOutputs(incomingNumbers.length(), incomingNumbers.getNumberType());
+                }
+            } catch (OutOfMemoryError e) {
+                stateFinder = null;
+                setActive(false);
+            }
+        } else {
+            // No hidden output, so tempering can just be reversed
+            for (long word : incomingWords) {
+                if (index >= state.length()) {
+                    twistState(state.length());
+                    index = 0;
+                }
+                state.set(index, reverseTemper(word & wordMask));
+                index++;
             }
-            state.set(index, reverseTemper(number & wordMask));
-            index++;
         }
         return predicted;
     }
@@ -322,7 +351,53 @@ public class MersenneTwister extends RandomNumberGenerator {
     }
 
     /**
-     * Initializes the state elements from a seed value..
+     * Returns the word size of the generator.
+     * @return the word size
+     */
+    protected int getWordSize() {
+        return wordSize;
+    }
+
+    /**
+     * Returns the state of the generator.
+     * @return the current state
+     */
+    @Override
+    protected long[] getState() {
+        long[] stateCopy;
+        try {
+            stateCopy = new long[state.length() + 1];
+            for (int i = 0; i < state.length(); i++) {
+                stateCopy[i] = state.get(i);
+            }
+            stateCopy[stateCopy.length-1] = index;
+        } catch (OutOfMemoryError e) {
+            setActive(false);
+            stateCopy = null;
+        }
+        return stateCopy;
+    }
+
+    /**
+     * Sets the state of the generator.
+     * @param newState the new state
+     * @throws IllegalArgumentException if state does not have enough elements
+     */
+    protected synchronized void setState(long[] newState) {
+        if (newState == null || newState.length < state.length() + 1) {
+            throw new IllegalArgumentException();
+        }
+        for (int i = 0; i < state.length(); i++) {
+            state.set(i, newState[i]);
+        }
+        index = (int) newState[newState.length-1];
+        if (index < 0 || index > state.length()) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    /**
+     * Initializes the state elements from a seed value.
      * @param seed the seed value for initialization of the state
      */
     private void initialize(long seed) {
@@ -356,12 +431,7 @@ public class MersenneTwister extends RandomNumberGenerator {
      * @return the output of the generator
      */
     private long emitState(int stateIndex) {
-        long number = temper(state.get(stateIndex));
-        // For integers add two complement bit extension for negative numbers
-        if (wordSize == Integer.SIZE && number >> Integer.SIZE-1 > 0) {
-            number |= COMPLEMENT_INTEGER_EXTENSION;
-        }
-        return number;
+        return temper(state.get(stateIndex));
     }
 
     /**
@@ -378,7 +448,8 @@ public class MersenneTwister extends RandomNumberGenerator {
     }
 
     /**
-     * Reverses the tempering transformation.
+     * Reverses the tempering transformation by reversing each tempering step. Instead, one could
+     * also use the transpose (which is the inverse) of the binary tempering matrix.
      * @param number the output of the tempering transformation
      * @return the original input to the tempering transformation
      */
@@ -409,4 +480,360 @@ public class MersenneTwister extends RandomNumberGenerator {
         }
         return shifter;
     }
-}
\ No newline at end of file
+
+    /**
+     * Implements a state detection algorithm that can deal with truncated output. The algorithm
+     * follows Argyros and Kiayias, "I Forgot Your Password: Randomness Attacks Against PHP
+     * Applications" (2012).
+     */
+    private class StateFinder {
+        /** This is the binary tempering matrix represented as a long vector. */
+        private long[] temperingVector;
+        /** Use non-sparse coefficients of a single equation for coefficient construction. */
+        private long[] equationCoefficients;
+        /** Sparse representation of equation coefficients of all equations. */
+        private long[][] coefficients;
+        /** The right hand sides of all equations, one bit per equation. */
+        private long[] rightHandSide;
+        /** The total number of equations available so far. */
+        private int numberOfEquations;
+        /** Required number of bits to store a coefficient index. */
+        private int bitsPerIndex;
+        /** Store multiple coefficient indices in each long. */
+        private int indicesPerLong;
+        /** A bit mask to obtain the first coefficient index in a long. */
+        private long firstIndexMask;
+        /** Number of observed numbers in the number sequence. */
+        private int sequenceCounter;
+        /** Flag indicating whether the system of equations is solved. */
+        private boolean solved = false;
+
+        /**
+         * Constructs a state finder initializing all fields and building the tempering matrix.
+         */
+        StateFinder() {
+            equationCoefficients = new long[(state.length() * wordSize) / Long.SIZE];
+            int requiredNumberOfEquations = wordSize * state.length();
+            coefficients = new long[requiredNumberOfEquations][];
+            rightHandSide = new long[state.length()];
+            // Construct tempering matrix
+            long[] transposedTemperingVector = new long[wordSize];
+            long shifter = 1L << (wordSize - 1);
+            for (int i = 0; i < wordSize; i++) {
+                transposedTemperingVector[i] = temper(shifter);
+                shifter >>>= 1;
+            }
+            // Bitwise transpose to get a representation in terms of the output
+            temperingVector = new long[wordSize];
+            for (int i = 0; i < wordSize; i++) {
+                shifter = 1L << (wordSize - 1);
+                for (int j = 0; j < wordSize; j++) {
+                    // Select bit j of word i
+                    long selectedBit = transposedTemperingVector[i] & shifter;
+                    if (selectedBit != 0) {
+                        // Shift bit j to position i
+                        if (i < j) {
+                            selectedBit <<= j - i;
+                        } else if (j < i) {
+                            selectedBit >>>= i - j;
+                        }
+                        temperingVector[j] |= selectedBit;
+                    }
+                    shifter >>>= 1;
+                }
+            }
+            // Find number of bits required to hold an index
+            int maximumIndex = requiredNumberOfEquations;
+            bitsPerIndex = 0;
+            while (maximumIndex > 0) {
+                maximumIndex >>>= 1;
+                bitsPerIndex++;
+            }
+            indicesPerLong = Long.SIZE / bitsPerIndex;
+            firstIndexMask = 0;
+            for (int i = 0; i < bitsPerIndex; i++) {
+                firstIndexMask |= (1L << i);
+            }
+            sequenceCounter = 0;
+            // First maskBits state bits are not used
+            for (int i = 0; i < maskBits; i++) {
+                coefficients[i] = new long[1];
+                coefficients[i][0] = i;
+            }
+            numberOfEquations = maskBits;
+        }
+
+        /**
+         * Extracts the information from a given number and adds it as equations to the state
+         * finder.
+         * @param number the number to process
+         * @param bits marks the visible bits of number by ones
+         */
+        void addInput(long number, long bits) {
+            long shifter1 = 1L << (wordSize - 1);
+            for (int i = 0; i < wordSize; i++) {
+                if ((bits & shifter1) != 0) {
+                    // Reset equationCoefficients
+                    for (int j = 0; j < equationCoefficients.length; j++) {
+                        equationCoefficients[j] = 0L;
+                    }
+                    // Set coefficients according to temperingVector
+                    long shifter2 = (1L << (wordSize - 1));
+                    for (int bitIndex = 0; bitIndex < wordSize; bitIndex++) {
+                        if ((temperingVector[i] & shifter2) != 0) {
+                            setEquationCoefficients(equationCoefficients, bitIndex,
+                                    sequenceCounter);
+                        }
+                        shifter2 >>>= 1;
+                    }
+                    boolean equationValue = ((number & shifter1) != 0);
+                    insertEquation(equationCoefficients, equationValue);
+                    if (numberOfEquations >= coefficients.length) {
+                        recoverState(sequenceCounter);
+                        break;
+                    }
+                }
+                shifter1 >>>= 1;
+            }
+            sequenceCounter++;
+        }
+
+        /**
+         * Determines whether the system of equations is solved.
+         * @return true if the system of equations is solved
+         */
+        boolean isSolved() {
+            return solved;
+        }
+
+        /**
+         * Sets the binary equation coefficient at the given sequence index and bit index.
+         * @param equationCoefficients the equation coefficients to change
+         * @param bitIndex the bit index of the coefficient to set
+         * @param sequenceCounter the sequence index of the coefficient to set
+         */
+        private void setEquationCoefficients(long[] equationCoefficients, int bitIndex,
+                                             int sequenceCounter) {
+            if (sequenceCounter < state.length()) {
+                // Leaf: Flip coefficient at equation bit index
+                // sequenceCounter * wordSize + bitIndex
+                int longIndex = (sequenceCounter * wordSize) / Long.SIZE;
+                int longOffset = (sequenceCounter * wordSize) % Long.SIZE;
+                equationCoefficients[longIndex]
+                        ^= (1L << (Long.SIZE - 1 - (longOffset + bitIndex)));
+            } else {
+                // Recursion
+                switch (bitIndex) {
+                    case 0:
+                        setEquationCoefficients(equationCoefficients, 0,
+                                sequenceCounter - state.length() + shiftSize);
+                        break;
+                    case 1:
+                        setEquationCoefficients(equationCoefficients, 1,
+                                sequenceCounter - state.length() + shiftSize);
+                        setEquationCoefficients(equationCoefficients, 0,
+                                sequenceCounter - state.length());
+                        break;
+                    default:
+                        setEquationCoefficients(equationCoefficients, bitIndex,
+                                sequenceCounter - state.length() + shiftSize);
+                        setEquationCoefficients(equationCoefficients, bitIndex - 1,
+                                sequenceCounter - state.length() + 1);
+                }
+                long twistMaskBit = twistMask & (1L << (wordSize - bitIndex - 1));
+                if (twistMaskBit != 0) {
+                    setEquationCoefficients(equationCoefficients, wordSize - 1,
+                            sequenceCounter - state.length() + 1);
+                }
+            }
+        }
+
+        /**
+         * Converts a direct representation of the binary coefficients of an equation to a sparse
+         * one. The sparse representation stores the indices of the non-zero coefficients where
+         * multiple indices are stored per long.
+         * @param equationCoefficients the direct representation of the equation coefficients
+         * @return the sparse representation of the equation coefficients
+         */
+        private long[] convertToSparseCoefficients(long[] equationCoefficients) {
+            // Count number of non-zero bits
+            int bitSum = 0;
+            for (long coefficientLong : equationCoefficients) {
+                bitSum += Long.bitCount(coefficientLong);
+            }
+            long[] sparseCoefficients = new long[(bitSum + indicesPerLong - 1) / indicesPerLong];
+            int currentIndex = 0;
+            for (int longIndex = 0; longIndex < equationCoefficients.length; longIndex++) {
+                if (equationCoefficients[longIndex] != 0) {
+                    long shifter = 1L << (Long.SIZE - 1);
+                    for (int bitIndex = 0; bitIndex < Long.SIZE; bitIndex++) {
+                        if ((equationCoefficients[longIndex] & shifter) != 0) {
+                            long index = (longIndex * Long.SIZE) + bitIndex;
+                            int subIndex = currentIndex % indicesPerLong;
+                            sparseCoefficients[currentIndex / indicesPerLong]
+                                    |= (index << (subIndex * bitsPerIndex));
+                            currentIndex++;
+                        }
+                        shifter >>>= 1;
+                    }
+                }
+            }
+            return sparseCoefficients;
+        }
+
+        /**
+         * Inserts a new equation into the system of equations by converting the equation
+         * coefficients to a sparse representation and performing online Gaussian elimination.
+         * @param equationCoefficients direct representation of equation coefficients
+         * @param equationValue right hand side of the equation
+         */
+        private void insertEquation(long[] equationCoefficients, boolean equationValue) {
+            // Apply online Gaussian elimination
+            boolean equationInserted = false;
+            while (hasAnyCoefficients(equationCoefficients) && !equationInserted) {
+                int firstCoefficientIndex = getFirstCoefficientIndex(equationCoefficients);
+                if (coefficients[firstCoefficientIndex] == null) {
+                    // Convert equation into a sparse format
+                    coefficients[firstCoefficientIndex]
+                            = convertToSparseCoefficients(equationCoefficients);
+                    // Store right hand side
+                    if (equationValue) {
+                        flipRightHandSide(firstCoefficientIndex);
+                    }
+                    numberOfEquations++;
+                    equationInserted = true;
+                } else {
+                    // XOR stored equation with new equation
+                    for (int i = 0; i < coefficients[firstCoefficientIndex].length; i++) {
+                        long indexMask = firstIndexMask;
+                        for (int j = 0; j < indicesPerLong; j++) {
+                            int index = (int) ((coefficients[firstCoefficientIndex][i] & indexMask)
+                                    >>> (bitsPerIndex * j));
+                            // Check whether index is actually set
+                            if (index == 0 && (i > 0 || j > 0)) {
+                                break;
+                            }
+                            // Flip bit at index in equation
+                            equationCoefficients[index / Long.SIZE]
+                                    ^= (1L << (Long.SIZE - 1 - (index % Long.SIZE)));
+                            indexMask <<= bitsPerIndex;
+                        }
+                    }
+                    // Check right hand side of stored equation
+                    if (isSetRightHandSide(firstCoefficientIndex)) {
+                        // Flip right hand side of new equation
+                        equationValue = !equationValue;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Determines whether the binary equation coefficients have any non-zero entry.
+         * @param equationCoefficients direct representation of equation coefficients
+         * @return true if the equation coefficients contain any non-zero entry
+         */
+        private boolean hasAnyCoefficients(long[] equationCoefficients) {
+            for (long coefficientLong : equationCoefficients) {
+                if (coefficientLong != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Returns the index of the first non-zero equation coefficient.
+         * @param equationCoefficients direct representation of equation coefficients
+         * @return the index of the first non-zero coefficient
+         */
+        private int getFirstCoefficientIndex(long[] equationCoefficients) {
+            for (int longIndex = 0; longIndex < equationCoefficients.length; longIndex++) {
+                if (equationCoefficients[longIndex] != 0) {
+                    long shifter = 1L << (Long.SIZE - 1);
+                    for (int bitIndex = 0; bitIndex < Long.SIZE; bitIndex++) {
+                        if ((equationCoefficients[longIndex] & shifter) != 0) {
+                            return (longIndex * Long.SIZE) + bitIndex;
+                        }
+                        shifter >>>= 1;
+                    }
+                }
+            }
+            return -1;
+        }
+
+        /**
+         * Recovers the Mersenne Twister state from the system of equations. First recovers the
+         * initial state of the generator and then advances the state to the given sequence counter.
+         * @param sequenceCounter the position in the number sequence to advance the state to
+         */
+        private void recoverState(int sequenceCounter) {
+            // Eliminate all but one coefficient in each equation
+            for (int i = coefficients.length - 1; i >= 0; i--) {
+                if (isSetRightHandSide(i)) {
+                    // Flip right hand side of all equations that contain coefficient j
+                    for (int j = 0; j < i; j++) {
+                        if (hasCoefficient(coefficients[j], i)) {
+                            flipRightHandSide(j);
+                        }
+                    }
+                    // We do not bother to actually flip any coefficient
+                }
+            }
+            // Set initial state
+            for (int i = 0; i < state.length(); i++) {
+                state.set(i, rightHandSide[i]);
+            }
+            index = 0;
+            // Advance state according to sequence index
+            next(sequenceCounter + 1);
+            solved = true;
+        }
+
+        /**
+         * Determines whether the sparse coefficient array contains a given index.
+         * @param sparseCoefficients sparse coefficients of an equation
+         * @param index the index to check for
+         * @return true if the sparse coefficient array contains the given index
+         */
+        private boolean hasCoefficient(long[] sparseCoefficients, int index) {
+            for (int i = 0; i < sparseCoefficients.length; i++) {
+                long indexMask = firstIndexMask;
+                for (int j = 0; j < indicesPerLong; j++) {
+                    int nextIndex = (int) ((sparseCoefficients[i] & indexMask)
+                            >>> (bitsPerIndex * j));
+                    // Check whether nextIndex is actually set
+                    if (nextIndex == 0 && (i > 0 || j > 0)) {
+                        break;
+                    }
+                    if (nextIndex == index) {
+                        return true;
+                    }
+                    indexMask <<= bitsPerIndex;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Determines whether the right hand side of the equation at the given index is set.
+         * @param index the index of the equation to check
+         * @return true if the right hand side of the equation is set
+         */
+        private boolean isSetRightHandSide(int index) {
+            int longIndex = index / wordSize;
+            int longOffset = index % wordSize;
+            return ((rightHandSide[longIndex] & (1L << (wordSize - 1 - longOffset))) != 0);
+        }
+
+        /**
+         * Flips the right hand side bit of the equation at the given index.
+         * @param index the index of the equation to change
+         */
+        private void flipRightHandSide(int index) {
+            int longIndex = index / wordSize;
+            int longOffset = index % wordSize;
+            rightHandSide[longIndex] ^= (1L << (wordSize - 1 - longOffset));
+        }
+    }
+}
diff --git a/app/src/main/java/org/asnelt/derandom/NumberSequence.java b/app/src/main/java/org/asnelt/derandom/NumberSequence.java
new file mode 100644
index 0000000..1342977
--- /dev/null
+++ b/app/src/main/java/org/asnelt/derandom/NumberSequence.java
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) 2015, 2016 Arno Onken
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.asnelt.derandom;
+
+import java.math.BigInteger;
+
+/**
+ * This class represents a sequence of typed numbers.
+ */
+class NumberSequence {
+    /** Bit mask for an integer. */
+    private static final long INTEGER_MASK = (1L << Integer.SIZE) - 1L;
+    /** Two complement bit extension for negative integers. */
+    private static final long COMPLEMENT_INTEGER_EXTENSION = ~INTEGER_MASK;
+    /** Number of random bits in a float number. */
+    private static final int FLOAT_RANDOM_BITS = 24;
+    /** Number of random bits in the lower word of a double number. */
+    private static final int DOUBLE_LOWER_RANDOM_BITS = 26;
+    /** Number of random bits in the upper word of a double number. */
+    private static final int DOUBLE_UPPER_RANDOM_BITS = 27;
+    /** Constant for converting long to unsigned long string. */
+    private static final BigInteger TWO_COMPLEMENT = BigInteger.ONE.shiftLeft(Long.SIZE);
+
+    /**
+     * Enumeration of all possible number types.
+     */
+    enum NumberType {
+        RAW, INTEGER, UNSIGNED_INTEGER, LONG, UNSIGNED_LONG, FLOAT, DOUBLE
+    }
+
+    /** The number type of the sequence. */
+    private NumberType numberType;
+    /** Internal bitwise representation of the numbers. */
+    private long[] internalNumbers;
+
+    /**
+     * Constructs an empty number sequence with number type raw.
+     */
+    NumberSequence() {
+        this(NumberType.RAW);
+    }
+
+    /**
+     * Constructs an empty number sequence with a given number type.
+     * @param numberType the number type of the number sequence
+     */
+    NumberSequence(NumberType numberType) {
+        this.internalNumbers = new long[0];
+        this.numberType = numberType;
+    }
+
+    /**
+     * Constructs a number sequence from bitwise number representations and a number type.
+     * @param numbers the numbers in bitwise long format
+     * @param numberType the number type of the sequence
+     */
+    NumberSequence(long[] numbers, NumberType numberType) {
+        this.internalNumbers = numbers;
+        this.numberType = numberType;
+    }
+
+    /**
+     * Constructs a number sequence from a string representation and a number type.
+     * @param numberStrings the numbers in a string representation
+     * @param numberType the number type of the sequence
+     */
+    NumberSequence(String[] numberStrings, NumberType numberType) {
+        this.numberType = numberType;
+        internalNumbers = new long[numberStrings.length];
+        // Parse numbers
+        for (int i = 0; i < internalNumbers.length; i++) {
+            try {
+                switch (numberType) {
+                    case RAW:
+                        internalNumbers[i] = parseNumberWithType(numberStrings[i]);
+                        break;
+                    case INTEGER:
+                        internalNumbers[i] = Integer.parseInt(numberStrings[i]);
+                        break;
+                    case LONG:
+                        internalNumbers[i] = Long.parseLong(numberStrings[i]);
+                        break;
+                    case FLOAT:
+                        if (isFloatString(numberStrings[i])) {
+                            internalNumbers[i] = Float.floatToIntBits(Float.parseFloat(
+                                    numberStrings[i]));
+                        } else {
+                            internalNumbers[i] = parseNumberWithType(numberStrings[i]);
+                        }
+                        break;
+                    case DOUBLE:
+                        internalNumbers[i] = Double.doubleToLongBits(Double.parseDouble(
+                                numberStrings[i]));
+                        break;
+                    default:
+                        internalNumbers[i] = Long.parseLong(numberStrings[i]);
+                }
+            } catch (NumberFormatException e) {
+                internalNumbers[i] = parseNumberWithType(numberStrings[i]);
+            }
+        }
+    }
+
+    /**
+     * Formats the numbers in the number sequence to a given number type and sets the number type of
+     * the sequence.
+     * @param numberType the number type
+     */
+    void formatNumbers(NumberType numberType) {
+        formatNumbers(numberType, Long.SIZE);
+    }
+
+    /**
+     * Formats the numbers in the number sequence to a given number type and sets the number type of
+     * the sequence. The internal bitwise representation is interpreted according to the given word
+     * size.
+     * @param numberType the number type
+     * @param wordSize the word size of the internal numbers
+     */
+    void formatNumbers(NumberType numberType, int wordSize) {
+        // Eventually reverse current format
+        if (this.numberType != numberType) {
+            internalNumbers = getSequenceWords(wordSize);
+            // Apply new format
+            this.numberType = numberType;
+            switch (numberType) {
+                case INTEGER:
+                case UNSIGNED_INTEGER:
+                    this.internalNumbers = formatIntegers(internalNumbers);
+                    break;
+                case LONG:
+                case UNSIGNED_LONG:
+                    this.internalNumbers = assembleLongs(internalNumbers, wordSize);
+                    break;
+                case FLOAT:
+                    this.internalNumbers = formatFloats(internalNumbers, wordSize);
+                    break;
+                case DOUBLE:
+                    this.internalNumbers = assembleDoubles(internalNumbers, wordSize);
+            }
+        }
+    }
+
+    /**
+     * Checks the number format and eventually fixes the complement integer extension.
+     */
+    void fixNumberFormat() {
+        // Eventually add complement integer extension for negative numbers
+        if (numberType == NumberType.INTEGER || numberType == NumberType.UNSIGNED_INTEGER) {
+            internalNumbers = formatIntegers(internalNumbers);
+        }
+    }
+
+    /**
+     * Returns the number type of the number sequence.
+     * @return the number type of the sequence
+     */
+    NumberType getNumberType() {
+        return numberType;
+    }
+
+    /**
+     * Determines whether the number sequence is empty.
+     * @return true if number sequence is empty
+     */
+    boolean isEmpty() {
+        return (internalNumbers == null || internalNumbers.length == 0);
+    }
+
+    /**
+     * Determines whether the number sequence is equal to the given number sequence.
+     * @param numberSequence the number sequence to compare to
+     * @return true if the number sequences are equal
+     */
+    boolean equals(NumberSequence numberSequence) {
+        if (length() != numberSequence.length()) {
+            return false;
+        }
+        for (int i = 0; i < length(); i++) {
+            if (internalNumbers[i] != numberSequence.getInternalNumber(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Concatenates the given number sequence to the current sequence.
+     * @param numberSequence the number sequence to concatenate to the current sequence
+     * @return the total concatenated number sequence
+     * @throws NumberFormatException if number types do not match
+     */
+    NumberSequence concatenate(NumberSequence numberSequence) throws NumberFormatException {
+        if (getNumberType() != numberSequence.getNumberType()) {
+            throw new NumberFormatException();
+        }
+        int firstLength = length();
+        int secondLength = numberSequence.length();
+        long[] newInternalNumbers = new long[firstLength + secondLength];
+        System.arraycopy(internalNumbers, 0, newInternalNumbers, 0, firstLength);
+        System.arraycopy(numberSequence.getInternalNumbers(), 0, newInternalNumbers, firstLength,
+                secondLength);
+        internalNumbers = newInternalNumbers;
+        return this;
+    }
+
+    /**
+     * Counts the number of matching numbers in the current and the given sequence.
+     * @param numberSequence the number sequence to compare to
+     * @return the number of matching numbers
+     */
+    int countMatchesWith(NumberSequence numberSequence) {
+        int minimumLength = Math.min(length(), numberSequence.length());
+        int matches = 0;
+        for (int i = 0; i < minimumLength; i++) {
+            if (internalNumbers[i] == numberSequence.getInternalNumber(i)) {
+                matches++;
+            }
+        }
+        return matches;
+    }
+
+    /**
+     * Returns the number of numbers in the sequence.
+     * @return the number of numbers in the sequence
+     */
+    public int length() {
+        if (internalNumbers == null) {
+            return 0;
+        } else {
+            return internalNumbers.length;
+        }
+    }
+
+    /**
+     * Returns the bitwise long representation of the number at the given index.
+     * @param index the index of the number
+     * @return the bitwise representation of the number
+     * @throws IndexOutOfBoundsException if index is not a valid index of the sequence
+     */
+    long getInternalNumber(int index) throws IndexOutOfBoundsException {
+        if (index < 0 || index > internalNumbers.length) {
+            throw new IndexOutOfBoundsException();
+        }
+        return internalNumbers[index];
+    }
+
+    /**
+     * Returns the whole sequence in its bitwise long representation.
+     * @return the number sequence in its bitwise long representation
+     */
+    long[] getInternalNumbers() {
+        return internalNumbers;
+    }
+
+    /**
+     * Returns a string representation of the number at the given index.
+     * @param index the index of the number
+     * @return a string representation of the number
+     * @throws IndexOutOfBoundsException if index is not a valid index of the sequence
+     */
+    String toString(int index) throws IndexOutOfBoundsException {
+        if (internalNumbers == null || index < 0 || index > internalNumbers.length) {
+            throw new IndexOutOfBoundsException();
+        }
+        String numberString;
+        switch (numberType) {
+            case UNSIGNED_LONG:
+                if (internalNumbers[index] >= 0) {
+                    numberString = Long.toString(internalNumbers[index]);
+                } else {
+                    // Use BigInteger to convert to unsigned long string
+                    BigInteger bigNumber = BigInteger.valueOf(internalNumbers[index]);
+                    if (bigNumber.signum() < 0) {
+                        bigNumber = bigNumber.add(TWO_COMPLEMENT);
+                    }
+                    numberString = bigNumber.toString();
+                }
+                break;
+            case FLOAT:
+                numberString = Float.toString(Float.intBitsToFloat((int) internalNumbers[index]));
+                break;
+            case DOUBLE:
+                numberString = Double.toString(Double.longBitsToDouble(internalNumbers[index]));
+                break;
+            default:
+                numberString = Long.toString(internalNumbers[index]);
+        }
+        return numberString;
+    }
+
+    /**
+     * Determines whether the number type of the sequence has hidden bits.
+     * @return true if the number type has truncated bits
+     */
+    boolean hasTruncatedOutput() {
+        return (numberType == NumberSequence.NumberType.FLOAT
+                || numberType == NumberSequence.NumberType.DOUBLE);
+    }
+
+    /**
+     * Returns the number sequence as a bitwise sequence of words of a given word size.
+     * @param wordSize the number of bits to represent in a long
+     * @return the number sequence as a sequence of words
+     */
+    long[] getSequenceWords(int wordSize) {
+        switch (numberType) {
+            case INTEGER:
+            case UNSIGNED_INTEGER:
+                return reverseFormatIntegers(internalNumbers);
+            case LONG:
+            case UNSIGNED_LONG:
+                return disassembleLongs(internalNumbers, wordSize);
+            case FLOAT:
+                return reverseFormatFloats(internalNumbers, wordSize);
+            case DOUBLE:
+                return disassembleDoubles(internalNumbers, wordSize);
+            default:
+                return internalNumbers;
+        }
+    }
+
+    /**
+     * Sets the word of the number sequence at a given index.
+     * @param index the index of the word
+     * @param word the word to write
+     * @param wordSize the number of bits to represent in a long
+     */
+    void setSequenceWord(int index, long word, int wordSize) {
+        switch (numberType) {
+            case LONG:
+            case UNSIGNED_LONG:
+                internalNumbers = setLongWord(index, word, internalNumbers, wordSize);
+                break;
+            default:
+                internalNumbers[index] = word;
+        }
+    }
+
+    /**
+     * Returns a sequence of bits in a long array that marks the observed number bits with ones.
+     * @param wordSize the number of bits to represent in a long
+     * @return the observed bits of each word of the number sequence marked as ones in a long array
+     */
+    long[] getObservedWordBits(int wordSize) {
+        long[] observedBits;
+        long wordMask;
+        if (wordSize == Long.SIZE) {
+            wordMask = (Long.MAX_VALUE << 1) | 1L;
+        } else {
+            wordMask = (1L << wordSize) - 1L;
+        }
+        switch (numberType) {
+            case INTEGER:
+            case UNSIGNED_INTEGER:
+                observedBits = new long[internalNumbers.length];
+                for (int i = 0; i < observedBits.length; i++) {
+                    observedBits[i] = wordMask & INTEGER_MASK;
+                }
+                break;
+            case LONG:
+            case UNSIGNED_LONG:
+                if (wordSize > Integer.SIZE) {
+                    observedBits = new long[internalNumbers.length];
+                    for (int i = 0; i < observedBits.length; i++) {
+                        observedBits[i] = wordMask;
+                    }
+                } else {
+                    observedBits = new long[internalNumbers.length * 2];
+                    for (int i = 0; i < observedBits.length; i++) {
+                        observedBits[i] = wordMask;
+                    }
+                }
+                break;
+            case FLOAT:
+                observedBits = new long[internalNumbers.length];
+                long floatMask = ((1L << FLOAT_RANDOM_BITS) - 1L) << (wordSize - FLOAT_RANDOM_BITS);
+                for (int i = 0; i < internalNumbers.length; i++) {
+                    observedBits[i] = floatMask;
+                }
+                break;
+            case DOUBLE:
+                observedBits = new long[internalNumbers.length * 2];
+                long doubleLowerMask = ((1L << DOUBLE_LOWER_RANDOM_BITS) - 1L)
+                        << (wordSize - DOUBLE_LOWER_RANDOM_BITS);
+                long doubleUpperMask = ((1L << DOUBLE_UPPER_RANDOM_BITS) - 1L)
+                        << (wordSize - DOUBLE_UPPER_RANDOM_BITS);
+                for (int i = 0; i < internalNumbers.length; i++) {
+                    observedBits[2 * i] = doubleUpperMask;
+                    observedBits[2 * i + 1] = doubleLowerMask;
+                }
+                break;
+            default:
+                observedBits = new long[internalNumbers.length];
+                for (int i = 0; i < observedBits.length; i++) {
+                    observedBits[i] = wordMask;
+                }
+        }
+        return observedBits;
+    }
+
+    /**
+     * Returns the number of words required for each number of the sequence.
+     * @return the number of words required for a number of the sequence
+     */
+    static int getRequiredWordsPerNumber(NumberType numberType) {
+        switch (numberType) {
+            case LONG:
+            case UNSIGNED_LONG:
+            case DOUBLE:
+                return 2;
+            default:
+                return 1;
+        }
+    }
+
+    /**
+     * Parses the number string and returns a bitwise long representation of that number. Also tries
+     * to detect the number type automatically and sets the internal type of the sequence
+     * accordingly.
+     * @param numberString the number represented as a string
+     * @return the bitwise long representation of the number
+     * @throws NumberFormatException if the number type of the number string is incompatible
+     */
+    private long parseNumberWithType(String numberString) throws NumberFormatException {
+        // Try all possible number types and eventually change input type
+        // Throw NumberFormatException if no number type fits
+        long number;
+        NumberSequence.NumberType currentType = getNumberType();
+        if (currentType == NumberSequence.NumberType.FLOAT
+                || getNumberType() == NumberSequence.NumberType.DOUBLE
+                || numberString.contains(".")) {
+            if (isFloatString(numberString)) {
+                float value = Float.parseFloat(numberString);
+                if (currentType != NumberSequence.NumberType.FLOAT) {
+                    numberType = NumberType.FLOAT;
+                }
+                number = Float.floatToIntBits(value);
+            } else {
+                double value = Double.parseDouble(numberString);
+                if (currentType != NumberSequence.NumberType.DOUBLE) {
+                    numberType = NumberType.DOUBLE;
+                }
+                number = Double.doubleToLongBits(value);
+            }
+        } else {
+            BigInteger bigNumber = new BigInteger(numberString);
+            number = bigNumber.longValue();
+            // Check whether type is already at unsigned long but next number is negative
+            if (currentType == NumberSequence.NumberType.UNSIGNED_LONG
+                    && bigNumber.signum() < 0) {
+                throw new NumberFormatException();
+            }
+            // Find minimum range that can hold the number
+            if (number >= Integer.MIN_VALUE && number <= Integer.MAX_VALUE) {
+                if (currentType == NumberSequence.NumberType.RAW) {
+                    numberType = NumberType.INTEGER;
+                }
+            } else if (bigNumber.signum() >= 0 && number < (1L << Integer.SIZE)) {
+                if (currentType == NumberSequence.NumberType.RAW
+                        || currentType == NumberSequence.NumberType.INTEGER) {
+                    numberType = NumberType.UNSIGNED_INTEGER;
+                }
+            } else {
+                if (number >= 0 || bigNumber.signum() < 0) {
+                    if (currentType == NumberSequence.NumberType.RAW
+                            || currentType == NumberSequence.NumberType.INTEGER
+                            || currentType == NumberSequence.NumberType.UNSIGNED_INTEGER)
+                    {
+                        numberType = NumberType.LONG;
+                    }
+                } else {
+                    // The most significant bit is set, but the number is not negative
+                    numberType = NumberType.UNSIGNED_LONG;
+                }
+            }
+        }
+        return number;
+    }
+
+    /**
+     * Determines whether float precision is sufficient to represent the number represented by the
+     * input string.
+     * @param inputString the string representation of the number to check
+     * @return true if float precision is sufficient
+     */
+    private boolean isFloatString(String inputString) {
+        // Test whether value fits into a float
+        double doubleValue, floatValue;
+        try {
+            doubleValue = Double.parseDouble(inputString);
+            String floatString = Float.toString(Float.parseFloat(inputString));
+            floatValue = Double.parseDouble(floatString);
+        } catch (NumberFormatException e) {
+            return false;
+        }
+        return (doubleValue == floatValue);
+    }
+
+    /**
+     * For each long value in the input array, takes the bits that fit into an int and eventually
+     * adds bits for the two complement bit extension.
+     * @param values the long values to format
+     * @return the integer values in a long array
+     */
+    private long[] formatIntegers(long[] values) {
+        long[] numbers = new long[values.length];
+        for (int i = 0; i < values.length; i++) {
+            numbers[i] = values[i] & INTEGER_MASK;
+        }
+        if (numberType == NumberSequence.NumberType.INTEGER) {
+            // Add two complement bit extension for negative numbers
+            for (int i = 0; i < numbers.length; i++) {
+                if ((numbers[i] >> Integer.SIZE - 1) > 0) {
+                    numbers[i] |= COMPLEMENT_INTEGER_EXTENSION;
+                }
+            }
+        }
+        return numbers;
+    }
+
+    /**
+     * Eventually removes the two complement bit extension for negative numbers.
+     * @param values the long array containing the integer values
+     * @return the long array without the bits from the two complement extension
+     */
+    private long[] reverseFormatIntegers(long[] values) {
+        long[] words = new long[values.length];
+        for (int i = 0; i < values.length; i++) {
+            words[i] = values[i] & INTEGER_MASK;
+        }
+        return words;
+    }
+
+    /**
+     * Assembles a sequence of long numbers from an array of words.
+     * @param words the words to assemble longs from
+     * @param wordSize the number of bits to represent in a long
+     * @return the array of long numbers
+     */
+    private long[] assembleLongs(long[] words, int wordSize) {
+        long[] numbers = new long[wordSize > Integer.SIZE ? words.length : (words.length / 2)];
+        for (int i = 0; i < words.length; i++) {
+            setLongWord(i, words[i], numbers, wordSize);
+        }
+        return numbers;
+    }
+
+    /**
+     * This is the inverse function of assembleLongs.
+     * @param numbers an array of long numbers
+     * @param wordSize the number of bits to represent in a long
+     * @return the words of the long array where each word is stored in one long
+     */
+    private long[] disassembleLongs(long[] numbers, int wordSize) {
+        long[] words = new long[wordSize > Integer.SIZE ?
+                numbers.length : (numbers.length * 2)];
+        for (int i = 0; i < words.length; i++) {
+            words[i] = getLongWord(i, numbers, wordSize);
+        }
+        return words;
+    }
+
+    /**
+     * Returns the word at the given index from an array of long numbers.
+     * @param index the index of the word
+     * @param numbers an array of long numbers
+     * @param wordSize the number of bits to represent in a long
+     * @return the word stored in a long
+     */
+    private long getLongWord(int index, long[] numbers, int wordSize) {
+        if (wordSize > Integer.SIZE) {
+            return numbers[index];
+        } else {
+            int numberIndex = index / 2;
+            if (index % 2 == 0) {
+                // Even index, so return upper word
+                return numbers[numberIndex] >> Integer.SIZE;
+            } else {
+                // Uneven index, so return lower word
+                return numbers[numberIndex] & INTEGER_MASK;
+            }
+        }
+    }
+
+    /**
+     * Sets the word at the given index in an array of long numbers.
+     * @param index the index of the word to set
+     * @param word the word to be written
+     * @param numbers an array of long numbers
+     * @param wordSize the number of bits to represent in a long
+     * @return the array of long numbers with the overwritten word
+     */
+    private long[] setLongWord(int index, long word, long[] numbers, int wordSize) {
+        if (wordSize > Integer.SIZE) {
+            numbers[index] = word;
+        } else {
+            int numberIndex = index / 2;
+            if (index % 2 == 0) {
+                // Even index, so set upper word
+                numbers[numberIndex] = (word << Integer.SIZE)
+                        + (numbers[numberIndex] & INTEGER_MASK);
+            } else {
+                // Uneven index, so set lower word
+                numbers[numberIndex] = word + (numbers[numberIndex] & COMPLEMENT_INTEGER_EXTENSION);
+            }
+        }
+        return numbers;
+    }
+
+    /**
+     * Format a sequence of float numbers from an array of words.
+     * @param words the words to assemble floats from
+     * @param wordSize the number of bits to represent in a long
+     * @return the sequence of floats bitwise represented as an array of longs
+     */
+    private long[] formatFloats(long[] words, int wordSize) {
+        long[] values = new long[words.length];
+        int shiftSize = wordSize - FLOAT_RANDOM_BITS;
+        for (int i = 0; i < words.length; i++) {
+            float nextValue = (words[i] >>> shiftSize) / ((float) (1 << FLOAT_RANDOM_BITS));
+            values[i] = Float.floatToIntBits(nextValue);
+        }
+        return values;
+    }
+
+    /**
+     * This is the inverse function of formatFloats.
+     * @param values sequence of floats bitwise represented as an array of longs
+     * @param wordSize the number of bits to represent in a long
+     * @return the words of the sequence of float numbers
+     */
+    private long[] reverseFormatFloats(long[] values, int wordSize) {
+        long[] words = new long[values.length];
+        int shiftSize = wordSize - FLOAT_RANDOM_BITS;
+        for (int i = 0; i < values.length; i++) {
+            float nextValue = Float.intBitsToFloat((int) values[i]);
+            nextValue *= (float) (1 << FLOAT_RANDOM_BITS);
+            words[i] = (int) nextValue;
+            words[i] <<= shiftSize;
+        }
+        return words;
+    }
+
+    /**
+     * Assembles a sequence of double numbers from an array of words.
+     * @param words the words to assemble doubles from
+     * @param wordSize the number of bits to represent in a long
+     * @return the sequence of doubles bitwise represented as an array of longs
+     */
+    private long[] assembleDoubles(long[] words, int wordSize) {
+        long[] values;
+        if (wordSize > Float.SIZE) {
+            values = new long[words.length];
+            int shiftSize = wordSize - (DOUBLE_LOWER_RANDOM_BITS + DOUBLE_UPPER_RANDOM_BITS);
+            for (int i = 0; i < words.length; i++) {
+                double nextValue = (words[i] >>> shiftSize)
+                        / ((double) (1L << (DOUBLE_LOWER_RANDOM_BITS + DOUBLE_UPPER_RANDOM_BITS)));
+                values[i] = Double.doubleToLongBits(nextValue);
+            }
+        } else {
+            int lowerShiftSize = wordSize - DOUBLE_LOWER_RANDOM_BITS;
+            int upperShiftSize = wordSize - DOUBLE_UPPER_RANDOM_BITS;
+            values = new long[words.length / 2];
+            for (int i = 0; i < values.length; i++) {
+                double nextValue = (((words[i * 2] >>> upperShiftSize) << DOUBLE_LOWER_RANDOM_BITS)
+                        + (words[i * 2 + 1] >>> lowerShiftSize))
+                        / ((double) (1L << (DOUBLE_LOWER_RANDOM_BITS + DOUBLE_UPPER_RANDOM_BITS)));
+                values[i] = Double.doubleToLongBits(nextValue);
+            }
+        }
+        return values;
+    }
+
+    /**
+     * This is the inverse function of assembleDoubles.
+     * @param values a sequence of doubles bitwise represented as an array of longs
+     * @param wordSize the number of bits to represent in a long
+     * @return the words of the sequence of double numbers
+     */
+    private long[] disassembleDoubles(long[] values, int wordSize) {
+        long[] words;
+        if (wordSize > Float.SIZE) {
+            words = new long[values.length];
+            int shiftSize = wordSize - (DOUBLE_LOWER_RANDOM_BITS + DOUBLE_UPPER_RANDOM_BITS);
+            for (int i = 0; i < values.length; i++) {
+                double nextValue = Double.longBitsToDouble(values[i]);
+                nextValue *= (double) (1L << (DOUBLE_LOWER_RANDOM_BITS + DOUBLE_UPPER_RANDOM_BITS));
+                words[i] = (long) nextValue;
+                words[i] <<= shiftSize;
+            }
+        } else {
+            int lowerShiftSize = wordSize - DOUBLE_LOWER_RANDOM_BITS;
+            int upperShiftSize = wordSize - DOUBLE_UPPER_RANDOM_BITS;
+            long doubleLowerMask = (1L << DOUBLE_LOWER_RANDOM_BITS) - 1L;
+            words = new long[values.length * 2];
+            for (int i = 0; i < values.length; i++) {
+                double nextValue = Double.longBitsToDouble(values[i]);
+                nextValue *= (double) (1L << (DOUBLE_LOWER_RANDOM_BITS + DOUBLE_UPPER_RANDOM_BITS));
+                words[2 * i] = (long) nextValue;
+                words[2 * i] = (words[2 * i] >>> DOUBLE_LOWER_RANDOM_BITS) << upperShiftSize;
+                words[2 * i + 1] = (long) nextValue;
+                words[2 * i + 1] = (words[2 * i + 1] & doubleLowerMask) << lowerShiftSize;
+            }
+        }
+        return words;
+    }
+}
diff --git a/app/src/main/java/org/asnelt/derandom/NumberSequenceView.java b/app/src/main/java/org/asnelt/derandom/NumberSequenceView.java
new file mode 100644
index 0000000..1bb17b3
--- /dev/null
+++ b/app/src/main/java/org/asnelt/derandom/NumberSequenceView.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015, 2016 Arno Onken
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.asnelt.derandom;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * A vew for displaying a number sequence.
+ */
+public class NumberSequenceView extends TextView {
+    /**
+     * Standard constructor for a NumberSequenceView.
+     * @param context global information about an application environment
+     */
+    public NumberSequenceView(Context context) {
+        super(context);
+    }
+
+    /**
+     * Standard constructor for a NumberSequenceView.
+     * @param context global information about an application environment
+     * @param attributeSet collection of attributes
+     */
+    public NumberSequenceView(Context context, AttributeSet attributeSet) {
+        super(context, attributeSet);
+    }
+
+    /**
+     * Standard constructor for a NumberSequenceView.
+     * @param context global information about an application environment
+     * @param attributeSet collection of attributes
+     * @param defaultStyledAttributes default values for styled attributes
+     */
+    public NumberSequenceView(Context context, AttributeSet attributeSet,
+                              int defaultStyledAttributes) {
+        super(context, attributeSet, defaultStyledAttributes);
+    }
+
+    /**
+     * Clears the view.
+     */
+    public void clear() {
+        setText("");
+    }
+
+    /**
+     * Appends a number sequence to the view.
+     * @param numberSequence the number sequence to append to the view
+     */
+    public void append(NumberSequence numberSequence) {
+        if (numberSequence == null) {
+            return;
+        }
+        // Append numbers
+        for (int i = 0; i < numberSequence.length(); i++) {
+            if (i > 0) {
+                append("\n");
+            }
+            append(numberSequence.toString(i));
+        }
+    }
+}
diff --git a/app/src/main/java/org/asnelt/derandom/ProcessingFragment.java b/app/src/main/java/org/asnelt/derandom/ProcessingFragment.java
index 45a0716..be9176d 100644
--- a/app/src/main/java/org/asnelt/derandom/ProcessingFragment.java
+++ b/app/src/main/java/org/asnelt/derandom/ProcessingFragment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -54,7 +54,8 @@ public class ProcessingFragment extends Fragment {
          * @param historyNumbers previously entered numbers
          * @param historyPredictionNumbers predictions for previous numbers
          */
-        void onHistoryPredictionReplaced(long[] historyNumbers, long[] historyPredictionNumbers);
+        void onHistoryPredictionReplaced(NumberSequence historyNumbers,
+                                         NumberSequence historyPredictionNumbers);
 
         /**
          * Called when the random number generator selection changed.
@@ -67,13 +68,13 @@ public class ProcessingFragment extends Fragment {
          * @param inputNumbers the entered numbers
          * @param predictionNumbers predictions for entered numbers
          */
-        void onHistoryChanged(long[] inputNumbers, long[] predictionNumbers);
+        void onHistoryChanged(NumberSequence inputNumbers, NumberSequence predictionNumbers);
 
         /**
          * Called when the predictions for upcoming numbers changed.
          * @param predictionNumbers predictions of upcoming numbers
          */
-        void onPredictionChanged(long[] predictionNumbers);
+        void onPredictionChanged(NumberSequence predictionNumbers);
 
         /**
          * Called when setting the input method to an input file is aborted and sets the input
@@ -140,13 +141,15 @@ public class ProcessingFragment extends Fragment {
     /** Number of process input tasks. */
     private volatile int inputTaskLength;
     /** Flag for whether processing should continue. */
-    private volatile boolean processingDesirable;
+    private volatile boolean processingEnabled;
     /** Server socket port. */
     private volatile int serverPort;
     /** Server socket. */
-    private ServerSocket serverSocket;
+    private volatile ServerSocket serverSocket;
     /** Client socket. */
     private volatile Socket clientSocket;
+    /** Current number type. */
+    private volatile NumberSequence.NumberType numberType;
     /** Listener for processing changes. */
     private ProcessingFragmentListener listener;
     /** Future for cancelling the server task. */
@@ -167,8 +170,9 @@ public class ProcessingFragment extends Fragment {
         outputWriter = null;
         missingUpdate = false;
         inputSelection = 0;
+        numberType = NumberSequence.NumberType.RAW;
         inputTaskLength = 0;
-        processingDesirable = true;
+        processingEnabled = true;
         serverPort = 0;
         clientSocket = null;
         synchronizationObject = this;
@@ -218,12 +222,12 @@ public class ProcessingFragment extends Fragment {
 
     /**
      * Sets the currently active generator.
-     * @param number index of the currently active generator
+     * @param index index of the currently active generator
      */
-    public void setCurrentGenerator(int number) {
-        if (number != randomManager.getCurrentGenerator()) {
+    public void setCurrentGenerator(int index) {
+        if (index != randomManager.getCurrentGeneratorIndex()) {
             prepareInputProcessing();
-            processingExecutor.execute(new UpdateAllTask(number));
+            processingExecutor.execute(new UpdateAllTask(index));
         }
     }
 
@@ -343,7 +347,9 @@ public class ProcessingFragment extends Fragment {
      * Executes a clear task.
      */
     public void clear() {
-        processingDesirable = false;
+        randomManager.deactivateAll();
+        processingEnabled = false;
+        numberType = NumberSequence.NumberType.RAW;
         processingExecutor.execute(new ClearTask());
     }
 
@@ -462,7 +468,7 @@ public class ProcessingFragment extends Fragment {
      */
     @Override
     public void onDestroy() {
-        processingDesirable = false;
+        processingEnabled = false;
         // Shutdown server thread
         serverExecutor.shutdownNow();
         // Shutdown processing thread
@@ -513,9 +519,9 @@ public class ProcessingFragment extends Fragment {
         @Override
         public void run() {
             historyBuffer.clear();
-            int currentGenerator = randomManager.getCurrentGenerator();
+            int currentGeneratorIndex = randomManager.getCurrentGeneratorIndex();
             randomManager.reset();
-            randomManager.setCurrentGenerator(currentGenerator);
+            randomManager.setCurrentGeneratorIndex(currentGeneratorIndex);
             boolean posted = handler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -530,7 +536,7 @@ public class ProcessingFragment extends Fragment {
             if (!posted) {
                 missingUpdate = true;
             }
-            processingDesirable = true;
+            processingEnabled = true;
         }
     }
 
@@ -544,7 +550,7 @@ public class ProcessingFragment extends Fragment {
         /**
          * Constructor for setting the new input capacity.
          */
-        public ChangeCapacityTask(final int capacity) {
+        ChangeCapacityTask(final int capacity) {
             this.capacity = capacity;
         }
 
@@ -567,18 +573,18 @@ public class ProcessingFragment extends Fragment {
         private final boolean changeGenerator;
 
         /**
-         * Standard constructor that initializes a task that does not change the generator..
+         * Standard constructor that initializes a task that does not change the generator.
          */
-        public UpdateAllTask() {
+        UpdateAllTask() {
             this.generatorIndex = 0;
             this.changeGenerator = false;
         }
 
         /**
-         * Constructor that initializes a task that does change the generator.
+         * Constructor that initializes a task that changes the generator.
          * @param generatorIndex index of the new generator
          */
-        public UpdateAllTask(final int generatorIndex) {
+        UpdateAllTask(final int generatorIndex) {
             this.generatorIndex = generatorIndex;
             this.changeGenerator = true;
         }
@@ -589,23 +595,23 @@ public class ProcessingFragment extends Fragment {
         @Override
         public void run() {
             final boolean generatorChanged;
-            if (changeGenerator && randomManager.getCurrentGenerator() != generatorIndex) {
+            if (changeGenerator && randomManager.getCurrentGeneratorIndex() != generatorIndex) {
                 // Process complete history
-                randomManager.setCurrentGenerator(generatorIndex);
+                randomManager.setCurrentGeneratorIndex(generatorIndex);
                 generatorChanged = true;
             } else {
                 generatorChanged = false;
             }
-            final long[] historyNumbers;
-            final long[] historyPredictionNumbers;
-            final long[] predictionNumbers;
+            final NumberSequence historyNumbers;
+            final NumberSequence historyPredictionNumbers;
+            final NumberSequence predictionNumbers;
             if ((generatorChanged || !changeGenerator) && historyBuffer.length() > 0) {
                 randomManager.resetCurrentGenerator();
-                randomManager.findCurrentSeries(historyBuffer.toArray(), null);
-                historyNumbers = historyBuffer.toArray();
+                historyNumbers = new NumberSequence(historyBuffer.toArray(), numberType);
+                randomManager.findCurrentSequence(historyNumbers, null);
                 historyPredictionNumbers = randomManager.getIncomingPredictionNumbers();
                 // Generate new prediction without updating the state
-                predictionNumbers = randomManager.predict(predictionLength);
+                predictionNumbers = randomManager.predict(predictionLength, numberType);
             } else {
                 historyNumbers = null;
                 historyPredictionNumbers = null;
@@ -647,7 +653,7 @@ public class ProcessingFragment extends Fragment {
          * Constructor for processing an input string.
          * @param input the input string to be processed
          */
-        public ProcessInputTask(final String input) {
+        ProcessInputTask(final String input) {
             this.input = input;
             this.fileUri = null;
         }
@@ -656,7 +662,7 @@ public class ProcessingFragment extends Fragment {
          * Constructor for processing the input file pointed to by fileUri.
          * @param fileUri the URI of the file to be processed
          */
-        public ProcessInputTask(final Uri fileUri) {
+        ProcessInputTask(final Uri fileUri) {
             this.input = null;
             this.fileUri = fileUri;
         }
@@ -664,7 +670,7 @@ public class ProcessingFragment extends Fragment {
         /**
          * Constructor for processing input from current input reader.
          */
-        public ProcessInputTask() {
+        ProcessInputTask() {
             this.input = null;
             this.fileUri = null;
         }
@@ -720,7 +726,7 @@ public class ProcessingFragment extends Fragment {
                 return;
             }
             try {
-                while (inputReader.ready() && processingDesirable) {
+                while (inputReader.ready() && processingEnabled) {
                     String nextInput = inputReader.readLine();
                     if (nextInput == null) {
                         break;
@@ -799,72 +805,73 @@ public class ProcessingFragment extends Fragment {
         }
 
         /**
-         * Processes the given input string by parsing the numbers and searching for compatible
-         * generator states. The generator is eventually changed if the flag autoDetect is set and a
-         * better generator is detected..
-         * @param input string of newline separated integers
-         * @throws NumberFormatException if input contains an invalid number string
+         * Processes the given input string by parsing the input string and processing the numbers.
+         * @param inputString string of newline separated integers
          */
-        private void processInputString(String input) throws NumberFormatException {
-            long[] inputNumbers;
-            String[] stringNumbers = input.split("\n");
-            inputNumbers = new long[stringNumbers.length];
-            // Parse numbers
-            for (int i = 0; i < inputNumbers.length; i++) {
-                inputNumbers[i] = Long.parseLong(stringNumbers[i]);
+        private void processInputString(String inputString) {
+            NumberSequence inputNumbers;
+            String[] stringNumbers = inputString.split("\n");
+            inputNumbers = new NumberSequence(stringNumbers, numberType);
+            NumberSequence.NumberType inputNumberType = inputNumbers.getNumberType();
+            if (inputNumberType != numberType) {
+                // Reformat history numbers
+                NumberSequence historyNumbers = new NumberSequence(historyBuffer.toArray(),
+                        numberType);
+                historyNumbers.formatNumbers(inputNumberType);
+                historyBuffer.clear();
+                numberType = inputNumberType;
+                inputNumbers = historyNumbers.concatenate(inputNumbers);
+                showClear();
             }
-            long[] historyPredictionNumbers;
-            long[] historyNumbers = null;
-            long[] replacedNumbers = null;
-            int bestGenerator = 0;
-            boolean generatorChanged = false;
+            processInputNumbers(inputNumbers);
+        }
+
+        /**
+         * Processes the given input numbers searching for compatible generator states. The
+         * generator is eventually changed if the flag autoDetect is set and a better generator is
+         * detected.
+         * @param inputNumbers the number sequence to be processed
+         */
+        private void processInputNumbers(NumberSequence inputNumbers) {
+            NumberSequence historyPredictionNumbers;
             if (autoDetect) {
                 // Detect best generator and update all states
-                bestGenerator = randomManager.detectGenerator(inputNumbers, historyBuffer);
+                int bestGenerator = randomManager.detectGenerator(inputNumbers, historyBuffer);
                 historyPredictionNumbers = randomManager.getIncomingPredictionNumbers();
-                if (bestGenerator != randomManager.getCurrentGenerator()) {
+                if (bestGenerator != randomManager.getCurrentGeneratorIndex()) {
                     // Set generator and process complete history
-                    randomManager.setCurrentGenerator(bestGenerator);
+                    randomManager.setCurrentGeneratorIndex(bestGenerator);
                     randomManager.resetCurrentGenerator();
-                    historyNumbers = historyBuffer.toArray();
-                    randomManager.findCurrentSeries(historyNumbers, null);
-                    replacedNumbers = randomManager.getIncomingPredictionNumbers();
-                    randomManager.findCurrentSeries(inputNumbers, historyBuffer);
+                    NumberSequence historyNumbers = new NumberSequence(historyBuffer.toArray(),
+                            numberType);
+                    randomManager.findCurrentSequence(historyNumbers, null);
+                    NumberSequence replacedNumbers = randomManager.getIncomingPredictionNumbers();
+                    randomManager.findCurrentSequence(inputNumbers, historyBuffer);
                     historyPredictionNumbers = randomManager.getIncomingPredictionNumbers();
-                    generatorChanged = true;
+                    // Post change to user interface
+                    showGeneratorChange(historyNumbers, replacedNumbers, bestGenerator);
                 }
             } else {
-                randomManager.findCurrentSeries(inputNumbers, historyBuffer);
+                randomManager.findCurrentSequence(inputNumbers, historyBuffer);
                 historyPredictionNumbers = randomManager.getIncomingPredictionNumbers();
             }
             // Generate new prediction without updating the state
-            long[] predictionNumbers = randomManager.predict(predictionLength);
-            historyBuffer.put(inputNumbers);
+            NumberSequence predictionNumbers = randomManager.predict(predictionLength, numberType);
+            historyBuffer.put(inputNumbers.getInternalNumbers());
             // Post result to user interface
-            if (generatorChanged) {
-                showGeneratorChange(inputNumbers, historyPredictionNumbers, predictionNumbers,
-                        historyNumbers, replacedNumbers, bestGenerator);
-            } else {
-                showInputUpdate(inputNumbers, historyPredictionNumbers, predictionNumbers);
-            }
+            showInputUpdate(inputNumbers, historyPredictionNumbers, predictionNumbers);
         }
 
         /**
-         * Sends the processing result to the processing listener.
-         * @param inputNumbers the processed input numbers
-         * @param historyPredictionNumbers the prediction numbers corresponding to the input
-         * @param predictionNumbers the predicted numbers
+         * Sends the instruction to clear to the processing listener.
          */
-        private void showInputUpdate(final long[] inputNumbers,
-                                     final long[] historyPredictionNumbers,
-                                     final long[] predictionNumbers) {
+        private void showClear() {
             boolean posted = handler.post(new Runnable() {
                 @Override
                 public void run() {
-                    // Append input numbers to history
+                    // Clear all fields
                     if (listener != null) {
-                        listener.onHistoryChanged(inputNumbers, historyPredictionNumbers);
-                        listener.onPredictionChanged(predictionNumbers);
+                        listener.onClear();
                     } else {
                         missingUpdate = true;
                     }
@@ -873,24 +880,16 @@ public class ProcessingFragment extends Fragment {
             if (!posted) {
                 missingUpdate = true;
             }
-            writeSocketOutput(predictionNumbers);
         }
 
         /**
-         * Sends the processing result to the processing listener. The result includes a change of
-         * generator.
-         * @param inputNumbers the processed input numbers
-         * @param historyPredictionNumbers the prediction numbers corresponding to the input
-         * @param predictionNumbers the predicted numbers
+         * Sends the generator change to the processing listener.
          * @param historyNumbers the complete previous input
          * @param replacedNumbers the complete previous prediction numbers
          * @param bestGenerator index of the best generator
          */
-        private void showGeneratorChange(final long[] inputNumbers,
-                                         final long[] historyPredictionNumbers,
-                                         final long[] predictionNumbers,
-                                         final long[] historyNumbers,
-                                         final long[] replacedNumbers,
+        private void showGeneratorChange(final NumberSequence historyNumbers,
+                                         final NumberSequence replacedNumbers,
                                          final int bestGenerator) {
             boolean posted = handler.post(new Runnable() {
                 @Override
@@ -899,6 +898,30 @@ public class ProcessingFragment extends Fragment {
                     if (listener != null) {
                         listener.onGeneratorChanged(bestGenerator);
                         listener.onHistoryPredictionReplaced(historyNumbers, replacedNumbers);
+                    } else {
+                        missingUpdate = true;
+                    }
+                }
+            });
+            if (!posted) {
+                missingUpdate = true;
+            }
+        }
+
+        /**
+         * Sends the processing result to the processing listener.
+         * @param inputNumbers the processed input numbers
+         * @param historyPredictionNumbers the prediction numbers corresponding to the input
+         * @param predictionNumbers the predicted numbers
+         */
+        private void showInputUpdate(final NumberSequence inputNumbers,
+                                     final NumberSequence historyPredictionNumbers,
+                                     final NumberSequence predictionNumbers) {
+            boolean posted = handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    // Append input numbers to history
+                    if (listener != null) {
                         listener.onHistoryChanged(inputNumbers, historyPredictionNumbers);
                         listener.onPredictionChanged(predictionNumbers);
                     } else {
@@ -917,12 +940,12 @@ public class ProcessingFragment extends Fragment {
          * the prediction block.
          * @param predictionNumbers the predicted numbers
          */
-        private void writeSocketOutput(long[] predictionNumbers) {
+        private void writeSocketOutput(NumberSequence predictionNumbers) {
             if (outputWriter != null && predictionNumbers != null) {
                 try {
                     // Write numbers to output stream
-                    for (long number : predictionNumbers) {
-                        outputWriter.write(Long.toString(number));
+                    for (int i = 0; i < predictionNumbers.length(); i++) {
+                        outputWriter.write(predictionNumbers.toString(i));
                         outputWriter.newLine();
                     }
                     // Finish this sequence of numbers with an additional newline
@@ -945,9 +968,9 @@ public class ProcessingFragment extends Fragment {
         @Override
         public void run() {
             // Generate new prediction without updating the state
-            final long[] predictionNumbers;
+            final NumberSequence predictionNumbers;
             if (historyBuffer.length() > 0) {
-                predictionNumbers = randomManager.predict(predictionLength);
+                predictionNumbers = randomManager.predict(predictionLength, numberType);
             } else {
                 predictionNumbers = null;
             }
diff --git a/app/src/main/java/org/asnelt/derandom/RandomManager.java b/app/src/main/java/org/asnelt/derandom/RandomManager.java
index da957a1..5242eed 100644
--- a/app/src/main/java/org/asnelt/derandom/RandomManager.java
+++ b/app/src/main/java/org/asnelt/derandom/RandomManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
 /**
  * Manages all random number generators.
  */
-public class RandomManager {
+class RandomManager {
     /** Random number generators. */
     private volatile AtomicReferenceArray<RandomNumberGenerator> generators;
     /** Names of all linear congruential generators. */
@@ -233,26 +233,26 @@ public class RandomManager {
             5489L
     };
     /** Index of currently active generator. */
-    private volatile int currentGenerator;
+    private volatile int currentGeneratorIndex;
     /** Best prediction for the latest incoming numbers. */
-    private volatile long[] incomingPredictionNumbers;
+    private volatile NumberSequence incomingPredictionNumbers;
 
     /**
-     * Constructor initializing all random numbers generators.
+     * Constructor initializing all random number generators.
      */
-    public RandomManager() {
+    RandomManager() {
         this.generators = new AtomicReferenceArray<>(0);
         initializeLinearCongruentialGenerators();
         initializeMersenneTwisters();
-        this.currentGenerator = 0;
-        incomingPredictionNumbers = new long[0];
+        this.currentGeneratorIndex = 0;
+        incomingPredictionNumbers = new NumberSequence();
     }
 
     /**
      * Returns human readable names of all generators.
      * @return all generator names
      */
-    public String[] getGeneratorNames() {
+    String[] getGeneratorNames() {
         String[] names = new String[generators.length()];
 
         for (int i = 0; i < generators.length(); i++) {
@@ -265,28 +265,28 @@ public class RandomManager {
     /**
      * Resets the state of the current generator.
      */
-    public void resetCurrentGenerator() {
-        generators.get(currentGenerator).reset();
+    void resetCurrentGenerator() {
+        generators.get(currentGeneratorIndex).reset();
     }
 
     /**
      * Resets the random manager including the states of all generators.
      */
-    public void reset() {
+    void reset() {
         for (int i = 0; i < generators.length(); i++) {
             generators.get(i).reset();
         }
-        currentGenerator = 0;
-        incomingPredictionNumbers = new long[0];
+        currentGeneratorIndex = 0;
+        incomingPredictionNumbers = new NumberSequence();
     }
 
     /**
      * Sets the currently active generator.
-     * @param number index of the currently active generator
+     * @param index index of the currently active generator
      */
-    public void setCurrentGenerator(int number) {
-        if (number >= 0 && number < generators.length()) {
-            currentGenerator = number;
+    void setCurrentGeneratorIndex(int index) {
+        if (index >= 0 && index < generators.length()) {
+            currentGeneratorIndex = index;
         }
     }
 
@@ -294,32 +294,32 @@ public class RandomManager {
      * Returns the index of the currently active generator.
      * @return index of the currently active generator
      */
-    public int getCurrentGenerator() {
-        return currentGenerator;
+    int getCurrentGeneratorIndex() {
+        return currentGeneratorIndex;
     }
 
     /**
      * Returns the name of the currently active generator.
      * @return name of the currently active generator
      */
-    public String getCurrentGeneratorName() {
-        return generators.get(currentGenerator).getName();
+    String getCurrentGeneratorName() {
+        return generators.get(currentGeneratorIndex).getName();
     }
 
     /**
      * Returns the parameter names of the currently active generator.
      * @return all parameter names of the currently active generator
      */
-    public String[] getCurrentParameterNames() {
-        return generators.get(currentGenerator).getParameterNames();
+    String[] getCurrentParameterNames() {
+        return generators.get(currentGeneratorIndex).getParameterNames();
     }
 
     /**
      * Returns all parameter values of the currently active generator.
      * @return parameter values of the currently active generator
      */
-    public long[] getCurrentParameters() {
-        return generators.get(currentGenerator).getParameters();
+    long[] getCurrentParameters() {
+        return generators.get(currentGeneratorIndex).getParameters();
     }
 
     /**
@@ -327,19 +327,19 @@ public class RandomManager {
      * @param number number of values to predict
      * @return predictions
      */
-    public long[] predict(int number) {
-        return generators.get(currentGenerator).peekNext(number);
+    NumberSequence predict(int number, NumberSequence.NumberType numberType) {
+        return generators.get(currentGeneratorIndex).peekNextOutputs(number, numberType);
     }
 
     /**
-     * Find prediction numbers of the currently active generator that match the input series and
+     * Find prediction numbers of the currently active generator that match the input sequence and
      * update the state and incomingPredictionNumbers accordingly.
      * @param incomingNumbers new input numbers
      * @param historyBuffer previous input numbers
      */
-    public void findCurrentSeries(long[] incomingNumbers, HistoryBuffer historyBuffer) {
+    void findCurrentSequence(NumberSequence incomingNumbers, HistoryBuffer historyBuffer) {
         incomingPredictionNumbers =
-                generators.get(currentGenerator).findSeries(incomingNumbers, historyBuffer);
+                generators.get(currentGeneratorIndex).findSequence(incomingNumbers, historyBuffer);
     }
 
     /**
@@ -349,56 +349,57 @@ public class RandomManager {
      * @param historyBuffer previous input numbers
      * @return index of the best matching generator
      */
-    public int detectGenerator(long[] incomingNumbers, HistoryBuffer historyBuffer) {
+    int detectGenerator(NumberSequence incomingNumbers, HistoryBuffer historyBuffer) {
         // Check whether the current generator predicts the incoming numbers
-        long[] prediction = predict(incomingNumbers.length);
-        boolean anyFailure = false;
-        for (int i = 0; i < prediction.length; i++) {
-            if (prediction[i] != incomingNumbers[i]) {
-                anyFailure = true;
-                break;
-            }
-        }
-        if (!anyFailure) {
+        NumberSequence prediction = predict(incomingNumbers.length(),
+                incomingNumbers.getNumberType());
+        if (prediction.equals(incomingNumbers)) {
             // Keep current generator
-            incomingPredictionNumbers = generators.get(currentGenerator).next(
-                    incomingNumbers.length);
-            return currentGenerator;
+            incomingPredictionNumbers = generators.get(currentGeneratorIndex).nextOutputs(
+                    incomingNumbers.length(), incomingNumbers.getNumberType());
+            return currentGeneratorIndex;
         }
         // Evaluate prediction quality for all generators
         int bestScore = 0;
-        int bestGenerator = currentGenerator;
+        int bestGeneratorIndex = currentGeneratorIndex;
         for (int i = 0; i < generators.length(); i++) {
-            prediction = generators.get(i).findSeries(incomingNumbers, historyBuffer);
-            int score = 0;
-            for (int j = 0; j < prediction.length; j++) {
-                if (prediction[j] == incomingNumbers[j]) {
-                    score++;
-                }
+            if (!generators.get(i).isActive()) {
+                continue;
             }
+            prediction = generators.get(i).findSequence(incomingNumbers, historyBuffer);
+            int score = prediction.countMatchesWith(incomingNumbers);
             if (score > bestScore) {
                 bestScore = score;
-                bestGenerator = i;
+                bestGeneratorIndex = i;
             }
-            if (i == currentGenerator) {
+            if (i == currentGeneratorIndex) {
                 if (score == bestScore) {
                     // For equal score current generator is the default generator
-                    bestGenerator = currentGenerator;
+                    bestGeneratorIndex = currentGeneratorIndex;
                 }
                 incomingPredictionNumbers = prediction;
             }
         }
-        return bestGenerator;
+        return bestGeneratorIndex;
     }
 
     /**
      * Returns the best prediction for the latest incoming numbers.
      * @return prediction for latest incoming numbers
      */
-    public long[] getIncomingPredictionNumbers() {
+    NumberSequence getIncomingPredictionNumbers() {
         return incomingPredictionNumbers;
     }
 
+    /**
+     * Deactivates all generators.
+     */
+    void deactivateAll() {
+        for (int i = 0; i < generators.length(); i++) {
+            generators.get(i).setActive(false);
+        }
+    }
+
     /**
      * Initializes all linear congruential generators.
      */
@@ -436,9 +437,10 @@ public class RandomManager {
                 try {
                     generators.set(this.generators.length() + i, new MersenneTwister(
                             MT_NAMES[i], MT_WORD_SIZES[i], MT_STATE_SIZES[i], MT_SHIFT_SIZES[i],
-                            MT_MASK_BITS[i], MT_TWIST_MASKS[i], MT_TEMPERING_US[i], MT_TEMPERING_DS[i],
-                            MT_TEMPERING_SS[i], MT_TEMPERING_BS[i], MT_TEMPERING_TS[i], MT_TEMPERING_CS[i],
-                            MT_TEMPERING_LS[i], MT_INITIALIZATION_MULTIPLIERS[i], MT_DEFAULT_SEEDS[i]));
+                            MT_MASK_BITS[i], MT_TWIST_MASKS[i], MT_TEMPERING_US[i],
+                            MT_TEMPERING_DS[i], MT_TEMPERING_SS[i], MT_TEMPERING_BS[i],
+                            MT_TEMPERING_TS[i], MT_TEMPERING_CS[i], MT_TEMPERING_LS[i],
+                            MT_INITIALIZATION_MULTIPLIERS[i], MT_DEFAULT_SEEDS[i]));
                 } catch (OutOfMemoryError e) {
                     // Not enough memory for Mersenne Twisters
                     return;
diff --git a/app/src/main/java/org/asnelt/derandom/RandomNumberGenerator.java b/app/src/main/java/org/asnelt/derandom/RandomNumberGenerator.java
index a8f9df2..c2df712 100644
--- a/app/src/main/java/org/asnelt/derandom/RandomNumberGenerator.java
+++ b/app/src/main/java/org/asnelt/derandom/RandomNumberGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,17 +19,20 @@ package org.asnelt.derandom;
 /**
  * This abstract class implements a random number generator.
  */
-public abstract class RandomNumberGenerator {
-    /**
-     * Resets the generator to its initial state.
-     */
-    public abstract void reset();
+abstract class RandomNumberGenerator {
+    /** Human readable name of the generator. */
+    private final String name;
+    /** Flag that signifies whether the generator is compatible with the input so far. */
+    private volatile boolean active;
 
     /**
-     * Returns the name of the generator.
-     * @return name of the generator
+     * Constructor initializing all parameters.
+     * @param name name of the generator
      */
-    public abstract String getName();
+    RandomNumberGenerator(String name) {
+        this.name = name;
+        setActive(true);
+    }
 
     /**
      * Returns human readable names of all parameters.
@@ -52,12 +55,13 @@ public abstract class RandomNumberGenerator {
     public abstract long[] peekNext(int number) throws IllegalArgumentException;
 
     /**
-     * Find prediction numbers that match the input series and update the state accordingly.
+     * Find prediction numbers that match the input sequence and update the state accordingly.
      * @param incomingNumbers new input numbers
      * @param historyBuffer previous input numbers
-     * @return predicted numbers that best match input series
+     * @return predicted numbers that best match input sequence
      */
-    public abstract long[] findSeries(long[] incomingNumbers, HistoryBuffer historyBuffer);
+    public abstract NumberSequence findSequence(NumberSequence incomingNumbers,
+                                                HistoryBuffer historyBuffer);
 
     /**
      * Generates the next prediction and updates the state accordingly.
@@ -65,13 +69,46 @@ public abstract class RandomNumberGenerator {
      */
     public abstract long next();
 
+    /**
+     * Resets the generator to its initial state.
+     */
+    public void reset() {
+        setActive(true);
+    }
+
+    /**
+     * Returns the name of the generator.
+     * @return name of the generator
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the activity state of the generator.
+     * @param active the new activity state.
+     */
+    void setActive(boolean active) {
+        this.active = active;
+    }
+
+    /**
+     * Returns the activity state of the generator. The activity state signifies whether the
+     * generator is compatible with the input so far and whether the generator should be used in
+     * generator detection.
+     * @return the activity state of the generator
+     */
+    boolean isActive() {
+        return active;
+    }
+
     /**
      * Generates the following predictions and updates the state accordingly.
      * @param number number of values to predict
      * @return predicted values
      * @throws IllegalArgumentException if number is less than zero
      */
-    public long[] next(int number) throws IllegalArgumentException {
+    synchronized long[] next(int number) throws IllegalArgumentException {
         if (number < 0) {
             throw new IllegalArgumentException();
         }
@@ -81,4 +118,60 @@ public abstract class RandomNumberGenerator {
         }
         return predictions;
     }
+
+    /**
+     * Generates the following outputs and updates the state accordingly.
+     * @param number the number of outputs to predict
+     * @param numberType the number type of the outputs
+     * @return predicted outputs
+     * @throws IllegalArgumentException if number is less than zero
+     */
+    NumberSequence nextOutputs(int number, NumberSequence.NumberType numberType)
+            throws IllegalArgumentException {
+        if (number < 0) {
+            throw new IllegalArgumentException();
+        }
+        int requiredWordNumber = number * NumberSequence.getRequiredWordsPerNumber(numberType);
+        NumberSequence numberSequence = new NumberSequence(next(requiredWordNumber),
+                NumberSequence.NumberType.RAW);
+        numberSequence.formatNumbers(numberType, getWordSize());
+        return numberSequence;
+    }
+
+    /**
+     * Returns the following outputs without updating the state of the generator.
+     * @param number the number of outputs to predict
+     * @param numberType the number type of the outputs
+     * @return predicted outputs
+     * @throws IllegalArgumentException if number is less than zero
+     */
+    NumberSequence peekNextOutputs(int number, NumberSequence.NumberType numberType)
+            throws IllegalArgumentException {
+        if (number < 0) {
+            throw new IllegalArgumentException();
+        }
+        int requiredWordNumber = number * NumberSequence.getRequiredWordsPerNumber(numberType);
+        NumberSequence numberSequence = new NumberSequence(peekNext(requiredWordNumber),
+                NumberSequence.NumberType.RAW);
+        numberSequence.formatNumbers(numberType, getWordSize());
+        return numberSequence;
+    }
+
+    /**
+     * Returns the word size of the generator.
+     * @return the word size
+     */
+    protected abstract int getWordSize();
+
+    /**
+     * Returns the state of the generator.
+     * @return the current state
+     */
+    protected abstract long[] getState();
+
+    /**
+     * Sets the state of the generator.
+     * @param state the new state
+     */
+    protected abstract void setState(long[] state);
 }
diff --git a/app/src/main/java/org/asnelt/derandom/SettingsActivity.java b/app/src/main/java/org/asnelt/derandom/SettingsActivity.java
index 874d7d4..9317413 100644
--- a/app/src/main/java/org/asnelt/derandom/SettingsActivity.java
+++ b/app/src/main/java/org/asnelt/derandom/SettingsActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -174,7 +174,8 @@ public class SettingsActivity extends PreferenceActivity
                     }
                     numberPreference.setText(defaultValue);
                     String errorMessage = getResources().getString(R.string.number_error_message);
-                    Toast.makeText(SettingsActivity.this, errorMessage, Toast.LENGTH_SHORT).show();
+                    Toast.makeText(getApplicationContext(), errorMessage,
+                            Toast.LENGTH_SHORT).show();
                 }
                 String summary = numberPreference.getText();
                 if (key.equals(SettingsActivity.KEY_PREF_SOCKET_PORT)) {
diff --git a/app/src/main/res/drawable-hdpi/ic_action_discard.png b/app/src/main/res/drawable-hdpi/ic_action_discard.png
index 703b31f8027859b5810937a5c2da2b97428c68ed..4a9f769475ae98c44086a5498057c799cdc1eb2e 100644
GIT binary patch
literal 161
zcmeAS@N?(olHy`uVBq!ia0y~yU{C>J4i*Lm25-&)VFm_<3{Mxw5Rc=@2@<R#4gY2S
zYyUG>IN0@H=85Fy$M#k?9Jd5IRCluSI-gN^bu5fI(PNIIOG99@8?TgU#+3yNQ!m&z
zJ^Q12Zb2~%ua)y0N3;L?ADBM7T(6zfT|e{xPYdpUfBs)ud{*)k1H;d~3-z`ibFXD!
PU|{fc^>bP0l+XkK0ew8)

literal 450
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F<YIk|nMYCBgY=CFO}lsSJ)O
z`AMk?p1FzXsX?iUDV2pMQ*9U+7*jl5978H@y@|Zb+hicH>{vKk17it;OoM3&gX{s$
zn(M4{+89$9BpKKe81{Jgi8n{Rj*Wg)dvfW)2VeJnt26Z!;c8V<;aR>ZQKBef({^d8
zV0TZ39{ohi=smX>I%Ti#<34sc!f=0j>(U9?a%y&mrEeAGt2iZ`I)Cu!q?-a2f@dm2
zA~||o;`JP^rRdl#pBDe#Au8&<g6W;8$E^&hvmY-ndC8zs%&0Wgq;YmeqEVW#RxjJZ
zcp;0%FHGCMEV&&mrlt8=WQvk;`+}oBA;HywmPdJt|M}}iv6^h-e^s84(J0hxcipRJ
z_S?i@o0Dx%ZU@WeXX`YC7pbpb>*AHtWtpnDmf29?1xwD$8lenUjxDd8yb|14mdvuW
zXNfs(&pB6$*-Js8(XNEyzgL6t1nJU+2Y6$}n+_aJZJOoayX(#?_QN*a%88!AGta(X
z{8*x^EquS2tMb#?=bf#-$9r?NItjANG4U|$ew0>pHOyoM0|Nttr>mdKI;Vst0L=)i
Avj6}9

diff --git a/app/src/main/res/drawable-hdpi/ic_action_refresh.png b/app/src/main/res/drawable-hdpi/ic_action_refresh.png
index dae27903e9ba3415808d48e6ac20afd6de64907b..67b7f900a7461b40319d14d3e3171508f3f4603f 100644
GIT binary patch
literal 528
zcmeAS@N?(olHy`uVBq!ia0y~yU{C>J4mJh`hKCF@W-u@?uqAoByD)&kPv_nB3=9mM
z1s;*b3=9k&VC;4>+YTgHR^XTp(hJ5F?9%-V42-iqT^vIq4!@nY+l$#zr0slHuh#{W
zq`X!E#~QY&Q<P?%I&{fNaPb6pkGIktFLQEDTUqoUXnX82U8E4v(KXeu&P>`u@5v_P
zWRH#ZIy{Hx{jd4+=A6t#kBLfO7dDs#FlagO{`fL~*8XsYpxjs9>l}m}Bv)LHEKT6|
zU`b?%Iq;;hU(cbbie1dK$5bGIMXJ$F{Q#Q*Zx4fE0^1FibIkASnBA)mn@RE(b3fU&
zBAP)i>S!9*<S)wkub4X8SIlRa7sE7l>Ty{Eu}%A2xMv<nVX6;4C)%TVVcV+pyIKrc
zt-`*()=Rv_$h+<1xm90NryE2HN(xQ9dx5hgaUK7`8K2p7O)ji<(`%S_)#~cW72*!x
zvutnkJBV(L+nUQVXJvlT@goObCGb?7)t+x<ke8KGTKYq7@qyk?H@A1*{8rTaxm=`D
z*<iy=hnaoy#Rs;o`pr0PZNikwiRYMa85ppxVGNaDu##=VQx>%eQ%-XH=2~OX8W@n+
z_DBB#gGbxThmuW8T~+jzyp@bJl_c-Kc6C|j@+8EC&-Q%vpXn(|u3ae$<9R=<{qWRn
RM>{BHJzf1=);T3K0RUe$%)0;p

literal 663
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F<YIk|nMYCBgY=CFO}lsSJ)O
z`AMk?p1FzXsX?iUDV2pMQ*9U+m@GYA978H@y@{~(J8U4*w|b2wgUkZkhMUo4r{eem
z$`1MMcK-f_;Vs|Y%<`f}nFcw9H%}#PC!|`Q-28Zk+b4C~$Re5N)s^<&sslv?1Oy&%
zEx(+3Bm0xW`(^Jp@^z>f@B|9I4CD+lIQFT_e*VK>(Gj0thBB(uG8?%*^1ab4)9_-p
zQ{02N<tgf13LhoCk~X$(I3<3f)#SN5qr&;b2o3r2M)80I?^v1WN#g51g=~Jpq4X!>
z-{cr!aVv>PqcWd5_DzlU5m)xuH$J>Mb!U?M6W4%s9XFFNeW<z7$uvV(?$!^THyaP+
zeAU;Rl4Ta(74l+2jk#V+-RY?>*E0SLQTi|aAoPT<NP-K~%tZ$TPxzmI$aRS0iGE^5
z=(1kE*?NWwYa~}&{);&gEc#&96v?lijTbGo9XcENQ(sBNtoRb5{3l}nxzt|KhO_Hz
zLQ<t)nX?C;ceFgwqou+g;QxD3s6e&a`rT)y=5C&l-!mubKykX#MVUF3^K}d^ml*w8
z*l<N*LScqg@r<MGo4U>{T)jlD@xZ-=_`F$+xm{I$G22ZP_ODoBE@ix+m({Fg*VK@e
z3vVx(<>f14TRug*yO(vA*<GQN#VYGh|JMAvPt>~a=&?18PhYM%CK_IvD=DsL(w^uS
zG0R(7{fK|3<NJMT+d>YoSRU*Bxb>(jkH`$S+^Qu?5!bK0Yt!cDFh8O4Y0&}wjoIdI
z=AImdn-(v-xlZJVa(v(vm9F#LmzH`ql!X~~Ze}R1uy5SEsG*^u;oA?f1O~;q?**61
TPwHb}U|{fc^>bP0l+XkKIJqG4

diff --git a/app/src/main/res/drawable-mdpi/ic_action_discard.png b/app/src/main/res/drawable-mdpi/ic_action_discard.png
index 248fb09cd0c918955323e790c4c3250997c99819..e2f5f35558db0965392b15b5d8950261a8c92aab 100644
GIT binary patch
literal 115
zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4i*Lm2CurW#S9D#R-P`7Ar_~T6D0mT*nj9h
z`~SoT2kTjDygo9#%1}7|cgZaKRf!?Ydc5m5tg3H}ZvOv&`ixWmfAf8^;b(AGpLFn5
TddMdR1_lOCS3j3^P6<r_D@Q8a

literal 324
zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}k|nMYCBgY=CFO}lsSJ)O
z`AMk?p1FzXsX?iUDV2pMQ*9U+7@m2$IEGZ*N=lGmRhBiFwCdsi|NpZbPBFHBDROf=
zc7Dl><4$dhyBlvv^#9R`vSwhrH1S4OK>I|V85#u#(swOkOqlbTNmD*!!)oS@DvY}4
z8-J(?%x8V1+j5D;knaZnl8TeOstcH9G<%uixSVE8m&$yg!^&sjJ++8o*7u8-8$^Vp
zMc$vkaGAm2M}FYaElgGiriWE^@HQH&xUuh8z|_{jxs_WfIqN#B4-Yd>TMzRI%`*Go
z%ZyJ_mPaqnOt`{$rG&v!^wQ15sl3}A44JoHoqSox+--H&#Mw#b8$>Tewgnn+c`z_A
bvN4?aVSW6ZU0DwU0|SGntDnm{r-UW|DGhl*

diff --git a/app/src/main/res/drawable-mdpi/ic_action_refresh.png b/app/src/main/res/drawable-mdpi/ic_action_refresh.png
index 94ab6f4c5dd8f3a082b2a84d6e08f2564a589d94..dbaf3677276091cc8733877bbcef67f3550f6c86 100644
GIT binary patch
literal 360
zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJo*pj^6T^PXNr}OT51_lPs
z0*}aI1_lNXFm^kcZ3hx8D{xE)=>_8opQXzf7#Kc#x;Tb-9DjSo+l$#zr1hbKgyTuK
zo<+?a2bb=c8P}N4>Yle``$dZq1wPrI3`bu)f5PU}{4#pcBL()ZS$|6TYInc-QpNqZ
zI<X_!q@wHU1A*7N+q4YdJ(hXD@0#?v4Lw|5XI>jr9_2h1ahRccM-Rh}nQCdwdQJNe
zf0M}*@83LsisAhOEc!QIC9M8-#)#4V;Jj(G(wS!^9AEnEW<z@d`%3w13zyC}(B$3G
zTl*uLvGmRFNa2{H5&T94zh<%7bJbm1*H_uTi9fgH_nEDE@^ivPSKsK|=(Bo}o4u@o
y@TJf)`GadjW^Qk~Rr<H*b;iZB`O^Ad(tok23P17B{r0#E6k?vPelF{r5}E+kRFM?`

literal 508
zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}k|nMYCBgY=CFO}lsSJ)O
z`AMk?p1FzXsX?iUDV2pMQ*9U+80UGqIEGZ*dJ}%Ju-Slz*SAixf#(2|&H;v!2E7Y>
z7Z{oTr#5`qf09Ax0^b29IR(iBf0s8^@4dWpT5xhr&6ZuCZ?^Z&)A2t4sciG6od0_m
z%MGXUAKnmumZ4$)-Kg!KLuLhq2WnpkGFyAW>Wltko4>&-o|o9A9lGRJRvS3%e3G%a
z@z%z3=NaDo;&>WkQnrgxV!nUt#`%m)KVF;*zq~f0deO8<o6mOFnc3Ow-tACK@VvR1
zwdSATg+8xWUTptd9(YAWKYo+@$mi&hM2F*LSA>4tEcDsQ@!$l*Mdl?Y=?}s=Wf-Ii
zH);q^%c+TK?mD}oUoA*r*_oqS6DGZnY}mHU?j*z32WrL_woRQ<=9I9NTWUl6TdOt^
zz4OP`v2#s3$EEqYR#LRj=cmfjxsSik>OEh){_)}uwTsf*A8vI_i!E?WYPuaHa;3)L
z#2P+Lk14IM?R^TSBurhJk?EwZl>0MGFoE^4NzmCcQFHU8BfmM;u}vu9{$_S<cD6%*
z%)E6e0k6vgXGy+Zw#a14zIu_x9WP3snD=+D&{r;c{`7yLRPa6)2?nz_{WpCVS#dKk
PFfe$!`njxgN@xNA$2;KP

diff --git a/app/src/main/res/drawable-xhdpi/ic_action_discard.png b/app/src/main/res/drawable-xhdpi/ic_action_discard.png
index 9eeeed124dbc2e6c32b179a48cb449bec2b2aeb2..388b5b060af924493b057a63216fe7db75d4435a 100644
GIT binary patch
literal 151
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4i*LmhQHi~JPZsBah@)YArXh)UO33vV8Gyf
z(NDSWW^Wgtn$uqQ4>~ulY+LZh-a$@<Ws#!ht93WZ|M4z7#c+%9|BBzX3{Viy$d_&Z
x`#=`sN%pk2M?cS2litzc`tzsE(p8m<9{EW<aaKM1bjf0nVoz5;mvv4FO#s%+HZuSK

literal 543
zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hE<t`_ZS!$BuiW)N`mv#O3D+9QW+dm
z@{>{(JaZG%Q-e|yQz{EjrrIztFdp=DaSW-r^(N|KV2cA!s%*Wc1J?nj5Aoa=m~tNI
zJkW9A-oUV)k^2MJfqKmc8Vp<u8uqSflRU=%<=rO56V=9&`(~)<&wdkqLxqt61R^x%
zv<bieJ4^0o<NCc8&tKgXb~q{irNZ{n)kz!`J+k*_KD;_J%fd3;Z<Vt2>Q}Ni*QPn#
zV!5(q&Fc7DuA<kX7F;S@v6snd>g9>DidXxNyLuQrW~n~icZ2D7Wg!1N)9nGteGGoT
z=Dlfqqf~V9)&|!}dp##SWSJCyG1>IunHvI}vsZHdm=ki!s{Ctw>jkCl2P1_p_({}f
zH#V?uddqS$|K@gH*#k`KIn75Mm^)5oZ0qvxn)~yy!Nf_Mcb%?#(fo)%$U^7+hV$i8
z_gNVlv+N6xCjUO!tY4ho*r>qeU4HzpR^g%7w^b)p-`sv_C6k64<K}<{0Vf5Ac?`a;
zH=<M)1gb1><ZfUPXi)4>>=0vMR7hCqykWCP!!v<37nu|oSY{|M2=2LkPvFX*65W^F
zx(rMyfm%mmqSPK(c2-|rog`oMS;cM2z25~7KU)3#7pWEcm0zmv{Uc`6Z&goE_y6_3
q^r*qB{w|;N?g&l>FqqS@mw}n#aes|a)-{7}kdUXVpUXO@geCy&<mXoa

diff --git a/app/src/main/res/drawable-xhdpi/ic_action_refresh.png b/app/src/main/res/drawable-xhdpi/ic_action_refresh.png
index ab4ab9da697b8c68c0c7ba498dc3bb3dc3da9c2b..393118decca6b3f8279279870a3ea45d08049926 100644
GIT binary patch
literal 665
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F<YIY)RhkE(~Ds(|LD20|NtR
zfk$L90|SEx7`vU!wgU;46*#7Y^n&rZcRk?@3`{DXE{-7?_ukIfn<W}3(t5pPXV*O!
zM+3J2PPY03hr%~pK4RC@>l!7mxMWjDucpWqn}*g<IR}m<7evHvaUWc9aP~X5?v4+U
zXBr-tshvA-SFC%Eefx|@YxZ8dlQ_S4n@FdN(!>sC2l*Y$Hq2&?j2q_vWji4KK+}N#
zi*}Y?X<`jq1na)wxqEfad~tln=p<#&rNeBWC9(YtGk-(e#GeU_4Vn*1xgSsCeXzw;
zUWK86_lBO~CSDuHJjU>b#|JD5L}#3~n>}~Z*65qJ>_3>*FFW!_Jb}?7#&I$G9rm)-
ze@y2+>z`02bXv;bp2K^Q4fj$To-+LveGtHXR_=pd1;YcS`tsRZ&7K+GRsGi(-VnO@
zx8w|t+<U4Fnc_dwO2l^=ycbQlmr=LGY5PZm_o5GWUaYw@wQ@bfw$gKDsrR%S?iT26
z6?-dmi8*}Ut=VB7QC~m)Z`5LJdsdlsW!Y=C9|`l)-Ew#3HGF@zGf-Sg@}Bff#?q=K
z$3NMa+qT^87g8;KH&bf&ff>_x{7yXgV!lqrZ|!;c>L&`VF8eizyH)egDB+$U%2l|~
zv%#HpSNFPSo0IN^pW=Lw+IWvG?aU*|Lyv>(+3x86c&0cvVh(STUHXD|5l<xw=9R4Z
zB=93-QyKRTnJYDVE#DJf>lUmN{=8ElZt+pqJ-=!XEBLnUcE0qkcwz3}BZeOFmvTML
zRbr=`=)P=OGJg`=q<vnurhM_bR5eNEaN1O*i5@CKKhNs>K01GU(XYV$pd{w$>gTe~
HDWM4fTCE=y

literal 895
zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hE<t`_ZS!$BuiW)N`mv#O3D+9QW+dm
z@{>{(JaZG%Q-e|yQz{EjrrIztFe`exIEGZ*dK3M=aJ2zXc~wnngMq_62A&Ii6L==@
zeXwHq8~1Ck<bp^A<_U~O4#p0~43Z6upO#n`_V3#ExSpkBt+i|}KhOU8s*#)Su#4@m
zabZ$ooY>&uprRmT;JY(M&;Q^#nTGhrI@U}^2AlTNv->wrlCCRZo9=$WenIgC){I*_
zmonNm&U4)F*uVMa8x;xm`M+QM)7zCV!gAoa;sMhaT@I^R-k*K>T-CNeC1gQ0L!|Hz
z|Gphpo=T_NGU-Pt%uDeXJHlMYSk$+8BLhRf@xSY(X)H?0!fVqim@=-}Tcz1>dfGI)
z-@lXb!*a1gyKz|Xh3pei1wB3MxSOvpZx&ME^x30uB3Am0%6FL^qRF1?N_@qd1s&4X
zGu>yp&-lU3+US4(kHzJTq7^3Z!;ki??$CX>X!f(T=9H!n6I&1cxo~B^?19-j^FPlP
zc6hcktU+J*xGAfBglWVHrkTt?w(3?)ub3FIM`!w)uRHeawPp#vvQ}l<38sw4LF_l!
z9B01F_ffJH|0>P-qi`p~+VhKE^h~|2*dn{o{p9`f;JXoqstzxH2hXjIXYmo+Bgi<{
z<L~MJ%?b_^zg`ViJFru#B(~jl+GEY<i`?hVUDR^7U8L#9maB#5A6Pr9i4~}Yomc+y
zdrdjhiY<5hN|c^Y;!G}o&(!6$fv<@{?S89WtJ485mbmRkbE~RtCaYxJy7}^ouEVz6
zxVyHDTa33(30Z#YX6B=p&Mu|RIlFD%&Ukxm*~Xih`?`)~to$~St3JwZd-;o3r?x#Q
z{H8s*v1Z4Cn?g02nT4v8-^)q&zie)(QhBge=hd~V3qtQVOxwL(+w*+dgue_`mFp_+
z?^gWqt>f&hmC_rQzMZy*pGk^2+;I`#te=0o&iswK{Wo<>`x1*vC)R@8WooZ}JPDr`
zVE04Jxuk8)Wk0@*cR$XBM{kxl;`miy?lW(XY0|GQ#@zMqXIg%HZ~a(uzR5*F{pTz@
zjd$BK3Cm{08T}QO|8wf1+?1SO1}p72CdkFS*>WwkaUJi3<NF-G2hI%jV4C+}UR3By
zZH{XqbF{=3-Ff8^Jw;CC-}wL)1t9^>4we?3|11&=i&V4{`wI64GB7YOc)I$ztaD0e
F0st6?k--1}

diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_discard.png b/app/src/main/res/drawable-xxhdpi/ic_action_discard.png
index cb1260a4c68d931cb7dbe80341355c1c2438bd9a..3fcdfdb55ebcba8d2fff8be03ea3518c137e3464 100644
GIT binary patch
literal 194
zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84i*LmhW}5h&oD4Bbb7ithGg7(d+98%g9C%h
zL)(kY;=Ej?dYu7{T1RqA6jIuMRIYE*pUm0V(IK<)(we<#q5hBVcv~IW*|mh5iG@R;
zcERzz>;ei74GfGRE)wHC(^|3fkC?xT1$=3Y;$HEsvB>x29LLBHOM{Y?mUG;jvN~}2
l(?3&1L!+-ND*XzV=AAFjZ++R+o`HdZ!PC{xWt~$(69E2$L(c#J

literal 765
zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^RWBuiW)N`mv#O3D+9QW+dm
z@{>{(JaZG%Q-e|yQz{EjrrIztFwOULaSW-r^=9rx-@^t1ZNf*g*%vUEG#WUtH!wyt
z=v`oxU|@T}BFk{^HsdT-BL^!7HjRCcLY4`cv>vguzOU~3jFI(u-SczLZ@5omWPpMO
z@uIVHt!4OJ`M0n9WRoxO!0PaMpU-LQcFX0dAB&d~^kR9Tci@zXYyb8;(u{I~3wAT?
zz9(;T{=KQ)Yu-cyo$V99e136!{g1!X=RIO)vM`=`&hg5Q4Qn&!B~@$)i473;cJE~5
za1iKYo4vp{sL(#^6T|Tht-0+S3xtknYft!dXU2iJUO$H9H`gu{ovk`o@n5-T%hLCy
zi|;Ic$Zfsp`Ki54K8e@&UF$3ISN<1z(BuE<*&Ahk^?p!1pyl*<zKa2qPLg=Sbk{Q%
zx+|VAsw8nRbaHR#zhT$>{jR&fw3O_tiJXqoMt@^?=IT~^9?8*D@SIUIeQ$TdmHoF<
z)^z^k{h_ElPdl<LBXhdawteOPEb%>?n|$8RzQ|=X#bD{>Qy%M`^TRLhGt91fwn^>V
z*_|e{Vy`Pk2W35rW>|cD$CTck^VVhuK7W^U-|XbEhRv-z%bqFbzP%i7y)pIGnPYw%
zk3QGiHGwI{tLc|t!>$^CHwNAxA`M4(9QYx&pyz;E>M14$CItos2FDo+XQUgrnOGbc
zSU&vuzKSC!XsM0^&+_nDQ&<^T92gWh`b{{p_2!7I>ffP|#K3gm1j~h*gk*a*Mw9$0
zYmbABU;-K9BXEpWkXe91xW}L2?Ci~_p0I9sUSwJ)uHigKlCQ|`#x%VNzXXj{k4<OX
z$nkNn(hbMl<#jDF3%M#g4ql(Re3!r~77O7=ioaLMKU`gQWTRfoPCKXirNQB=4qWRi
zc<nxCy7msU&1>gwyYg+lPssJIAFd0$9p#{Dh=JjO^?W8C20hpFocS$h6+yzDu6{1-
HoD!M<`@uqF

diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png b/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png
index 44ee117ee91541ed71f27685dde0b03b51b89f7f..61da9e5815577183035f0c374fff6871564008bb 100644
GIT binary patch
literal 982
zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D4d*pj^6T^PXNr}OT51_lPs
z0*}aIkTNiKJDF_<5-cllOa}{q%s5y1ft7)QInmR_F{I+w+u7C`B91cc^~J}$nj}o^
z96BE<a|P-&>3`sLW68}86)|yKJw@nf(3Nw3Z!X=8U`<MVsH~W<TBsvISwrY)%>p<3
z?%PT&XYRdADNo~nuKm65@Be@I-`#!l=A73=4;7(K7bOt&v6nMY<i(Vdh)Z&wcPE*7
zyqdB_<>#qzPkGh7Q@=!A;`a3KF_agJC_f=ltn6|=@=z%Q^8twu)BpTooyR=AF;%aK
z!Kr$N{*9;yW*@FPcJ*2na7Rpa_gl)s*xh(=$;?eZm||FOZ91{CM<K!Z!zLHQVo7#0
zlgYuHjt5>oIq@q^>V@~@$4V<M1oG_AcsXy0tK`0aVV63VET0tTS*5;mUd=VVmEM=k
zJlR#gdPYr8oWEan?({8zm%dg?o2i{Rq5B}HZLtkA4|8?n8OKJc0-lPN+Z+<#)Y;~E
z&GJtcd7-x;OyA(5QB#BNlK;>Dc+^$7UR%R>-AHE2hNcXjr<sedvM<<v<d=G5eyEtq
z4VQCa{F3j&g9J9P{dRp&p0K^0$9d}CPR8<}B9UM7gQv*kH#I!H@jPsC=+wu-7aErS
z<6-4_ntJl1X2zMI`W>f=_HZy+ZHzzrZuKUa_@;9aS^g7~e+fACbuXCZ9C!PJZBmcs
z9}Y(6O+GfY50p-nef+-WMZv<SJKE-LnX`AdQ@S?ubmJ3chyTRBNIP}FHDGsYwe9t|
zC>C+Y|Dt+LIq6>2>-KKp&}&+zd;5BO-ZGvT7T>6C`TTc%BzJIBCEwg<Ut*m4Kq=$S
zroHRetaS4h%-S?d`F^4Iw)g`<@rq4%-kq3{dt}nvjpqdp=FKoYxz+gT8@t!W9v7n5
zP4h8*(5iabWJAis&`+#fRhR4L*@Opuy7Oi3a-sg(v{Xx`KR%Y#R(rU;nnE8QOyvJy
zsp?)YVClKzLjOE|rujjy?3p*|9MudB>Nv{H_F{QqZt`Q}3!)+C)*P^#zW)uwJhPRi
z%re|>Q}aF;i3dnN(D-0*xbX+`9PL%>we${VPu}8m$;;Dr5}%jVl4Fzhr2Uy#a!}&)
zf)o3=S_`@@_A{tEeV-pX>3!+};RDMq)IOj3Bw4Z2cICxwAC5e9nfg)dkog0>4OS)f
zu3cJ>Ql}M46|cLazj;T{(d!GdvU<|ytN5-oJk-1Tqt%t)0+q_3v<^w~kEYj$YP-ku
SKP##N<xfvnKbLh*2~7Y~6}9UC

literal 1239
zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^RWBuiW)N`mv#O3D+9QW+dm
z@{>{(JaZG%Q-e|yQz{EjrrIztuvB`wIEGZ*dK2qdcFRCOwDMk2!xM&>hBw~d7giTD
zSTNjV{$0>e(2x>;qy5ce?jH;V415!^qJCcWJvd3XH)ZDk`<I*9f};PQ?@3GX6l7vR
z0vXJ9_V&jU6lH#T-`tj<B=b}L%7M1}PnVl&<e9!GDL(bv#}l&n`}SALzslqHo=>%&
zdQbkv*6XiRO_TpUI^D1S>HmrKehi++JofRVN>Am;=MqZPzr9=Kb3k*=p*4LSf!qsp
zY^T0mm~wNS<F!9po_D1t)XOqyJX+pW5x=0|>#9AES2}&@6-l`4$*A)2%FCVUk6t&0
zHmDTz#2d6pg(vLYuQK7P{*?z(%xrT79VYqDjVvnHQu|^v`G3yCOFO+(>!1IaUQ}E;
zci}40wpBNmZE&w#TX=;jV#55@KB@Yi7Lh3tCMWu5cPXx~m0!!id-8n9w~3a!+V|~h
zcf8Hw5#?qV`DfO`4>ND~SU+){uJz(tRl}xT)1~sxPs+c1W?8dU^IEBAUm98!e~R3e
zx3qfEQ4@B@AhJGd#&h|dYfc;f%*iZ1bfJ*THrV--y6<M`v$j35(RKD}{4=EQ7)-QF
zSE(2DRxEqG<1@4K>FOsHCk}f2GngijB2;DOW4K>q$0p-@)3Sr^d(W$QR3r7wgg2Ds
z&y}Y?H#rM4ZfH>uejVRjWXHHK)9!wu-L2hOj8_he#y;E3c(+Dwf%Yz=bvs2k5~eMC
z>tGrzmNMgS&2x=i4;a&~&2OxUV=OVAccR9rnjzy`Fni1)L6i8l?wt#Ep9<W!_~r6h
z6*^h-j}^P=ZCb$J%kW)Ru{zf9Uf?3b>|~BtUOz0ket&g6c&tIM;caKcp%mTN?%&&U
z#S#wvy`VHnKvaz-mh)%%tn>T_S#}7$aJs<B^ua0ot$81lZ9#VYYmV-laTN{o{!Gkc
z*f#TRD?`-_mJPFVS{SZg^JkrrxA3MM(+5S<AAB|q7k`y9y~&yX&9UM1g4VQMmI|*w
zw@hf8x4mvwU7g{Hx;5qgYI0Vew{}Q|M1;kM<hT8JULn<2x=ibf4)4BKDGO)z$4<&W
z^tVJWbe{3DzpUQPXO7>zmnfgy@cQH~VdbAI4Q}7xP*QsK23w-vr<04Gv&G-t9h36r
z@tTgs$)AeVPS2OVclgxPt+gBl8QMR2&uK^P{I<{KNTWO3r`PlDI31t!Hn#jsJHxgk
zYh#Yxxny>0%dz!=chYa(eeBF2Jw0|h|Mg;%trM>KbQfPQyk9Qs(0XHYYsLFhOy7Ce
z+Y8k%n<6h&^}eULbo=_xO?EPsLV}YzFI~H&WHVLk-J^u-j}9zbc0-dtB{oWaGK1a6
zQ=*@pIS&}h9yE<xYq4qWg{cKR+3g))<n2!OujG5M?#lFq-wf2{tRrV;uJB3KlDM8Y
zZ|Cyf)h65DCLaiyV}I(weiQ$tz5Gq@6T(mBZOgs6RX(WUluWd!pK@aTzvqE}Z1?Re
zwKl8eba1h&J!zKZyKd#*6Wf>Gsf!5I?pPFeKrAv;W$Hhj6SHS=Ff(y5d}8Lj!2a{0
kt3)F!QpwP;;D_n~hS^_UOfQNrUICKuboFyt=akR{0I51fa{vGU

diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_discard.png b/app/src/main/res/drawable-xxxhdpi/ic_action_discard.png
new file mode 100644
index 0000000000000000000000000000000000000000..8d322aa9baba09c58b0058760fb7d53a392b63a1
GIT binary patch
literal 243
zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4i*Lm29JsRH#0CW?DBMR45_&F_R4AALk0q_
zf$tYcx^%x(bX+3kBI;NoWF}d*=4XnI@Z9Kw)|YN9^_ZlR*}AB^{Pn-;y6zVK=iA+W
zB-PbAh43*jFfc68ZCGN}F9~Ae#|sn>D3|`9!@pxW)3TZamZBG)Gxu#=ayI@^z^86Q
zUV|x8NA1>We%9Zpy;LkQ=I4ZI606SE?`;jfll1TJ;~vdlTHEzKCvDj|FX<`6N{iV^
Q79h(!UHx3vIVCg!0ImO2jsO4v

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_refresh.png b/app/src/main/res/drawable-xxxhdpi/ic_action_refresh.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce518e1add7e2349fd5bdaeff0510aa74c926569
GIT binary patch
literal 1326
zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^RW*pj^6T^PXNr}OT51_lPs
z0*}aI1_lNXFm^kcZ3hx8D{xE)=>_A8+c&>3FtF_Oba4!+xb=39e}=59%<=t`og8j$
z*>{7jDwF?cm#rpOU|?YWqKZ`st^w=imi*hP@u>T)>mplGme#00<sX)@e|=!T#`Z>&
z!ojd)(XSI0dEN@SRi&XK&;PsSL(j~+wV%t<?)|P+n11Hdx8J|(%BRn~J98q>VFOeo
zQ4*8GXxG5$S?+O5`DK;oZq=<;_0rbuv6D(1FHKq!>1pp}rTTJ4ao)!MTer^boSsyD
zrT<VK6Cb1T0j{r+jQg0EHA*vO=P)1nrj_EDd|>l|m2y1a4Vi5?bKV4I|GqEZ^ER5}
z@p`sp4aK!4`({@@s7zQR{&_PWL&1UM15VAy%8SKrgkS%Y!^}{|cwAZP{@L^ceXE|O
zOEP>=_z<3R{Ca}mj`dwXBUv3jo~Sh4c3Lj1p)B&VGs}e&77P+oOF}LkHmy&JyEJ{$
zzQBve|8M=g|Mc8%zLU0j-ddWpreOAx-73A;ZSG4?nm6G~(~^~?yd`>XH;Nc=)dW4x
zvSF8DQ)@is%A|Z??t#DuNxh$^zv4Uo?Rt6>^S!kZKV&|b*nYdZyQGJiy^-6r>4a57
zxYm`T)}$l5R`yq^HEeHJV(7Z(7g@)YmlJUNugKnc2YDJ6&5!?9_MhQ~jN@az2e%u!
z8y@Y9FPreyYJ=jUYlaU#J90nxIO$e=_u8$CzaKcj!mvdnuH@$rgJ)Csd*$yI{~XMd
zu&U)*q>|0&>lbcq{^`l;@X^Nh{l)55wrcCw8N5k#S&TNpPVY;YteR)ek7C%j@OR`d
zixXNa4Ojd;$>Q*l_xkbR=CnF5Mww49VpJaNo*Qz;U$a57|J>o`j5;AkhT9+fWoOE`
zd=BPJSnvLL{*{XP^P7JvGD&a-yq5F-Eq-pN+j^0%^IgHd^ET@#GVM5bxo*+PrXnNz
zkb~;B<uCVcG+pM?VV!X3a-CY)L``8OhWjtdG@-0Nr@A+G`^*$zl&}?0Vz@6FSIPFt
zKv1c{d)~LOH%h^3E)1XNealW3pJ}AX^r6(amU-(!H5Z1>^S){5KPpmmVOVba_WO|v
zNpq74A6~zZdGV94;GC|<uY1Kels9s(<@zw~`J!J|M=G6okJx?P@?QCo`pH!cw^ePQ
zSiV_&!z!HH@89B@qg8y<R-~8b%)Ps4^89H*oBG85t$cmld9MDG-3!cZ-`-Z;dqe#C
zAtT+FQs)ible+)#r<j-)&XszncX9D-=5<|bzy9Yskyi9h>ehCKbgvip<)dF$xu%%f
z)=qsFcuM!m58Ioo>q8B9>Rwo}X;N{1&fZk+Q>mSoG>%$5GOCfeeOs&b=?0H{?GK@z
zz1$+rn`;!_rmmWN;JoVAwG8}6zHQE1xKj6u+<TjE)3jey>22%ZD>CO)7^`lVs7<JO
zdsX|L_@%=6oBMbV=pGP!@LuY9{d_hnj^+)qXE!Z5J87HOEzOr{qPJu2-TPTDdvEfW
z6~%cwIqRhwYs_-20-pDsX1jEEQ%<K0TU4WyNABf)$p_Be*k>Z^#C1FDT)7F)JLTmr
z0)}RJw*DJiGQHpYn%tPSP0x<^jrn6+fxORo4e|}s8>5r2ZxuUf|NBFJy#1o2wr{DP
x#gpnLf9YN_d6J*S^M4(&&506isOa!n`>%@^+|2lQ$^lgFdAj<!taD0e0sy`uYHk1k

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xxxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..34cd486ed48ea0209db08af870c1d411b3c11f84
GIT binary patch
literal 39782
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%0AIl^Qr4Kln8wNqHBwqQGn>ARo7gkrl?C^
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
RGB7YOc)I$ztaD0e0ssdheA)m2

literal 0
HcmV?d00001

diff --git a/app/src/main/res/layout/activity_display_parameters.xml b/app/src/main/res/layout/activity_display_parameters.xml
index 67cdd50..bf8cb5c 100644
--- a/app/src/main/res/layout/activity_display_parameters.xml
+++ b/app/src/main/res/layout/activity_display_parameters.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 6c78b03..7515133 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -30,8 +30,8 @@ limitations under the License.
         android:id="@+id/view_horizontal_center"
         android:layout_width="1dp"
         android:layout_height="match_parent"
-        android:layout_centerInParent="true"
-        android:background="@android:color/darker_gray" />
+        android:background="@color/separator"
+        android:layout_centerHorizontal="true" />
 	
     <TextView
         android:id="@+id/label_input"
@@ -44,6 +44,7 @@ limitations under the License.
         android:layout_toStartOf="@+id/view_horizontal_center"
         android:layout_marginRight="16dp"
         android:layout_marginEnd="16dp"
+        android:textColor="@color/accent"
         android:textStyle="bold"
         android:textSize="14sp"
         android:text="@string/input" />
@@ -59,6 +60,7 @@ limitations under the License.
         android:layout_toEndOf="@+id/view_horizontal_center"
         android:layout_marginLeft="16dp"
         android:layout_marginStart="16dp"
+        android:textColor="@color/accent"
         android:textStyle="bold"
         android:textSize="14sp"
         android:text="@string/prediction" />
@@ -130,7 +132,6 @@ limitations under the License.
         android:id="@+id/text_input"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
         android:layout_alignParentLeft="true"
         android:layout_alignParentStart="true"
         android:layout_alignRight="@+id/view_horizontal_center"
@@ -140,17 +141,17 @@ limitations under the License.
         android:layout_marginEnd="16dp"
         android:scrollbars="vertical"
         android:gravity="top"
-        android:hint="@string/edit_message"
+        android:hint="@string/edit_hint"
         android:inputType="textMultiLine"
-        android:textSize="14sp" />
+        android:textSize="14sp"
+        android:layout_alignParentBottom="true" />
 
-    <TextView
+    <org.asnelt.derandom.NumberSequenceView
         android:id="@+id/text_prediction"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:layout_alignLeft="@+id/view_horizontal_center"
         android:layout_alignStart="@+id/view_horizontal_center"
-        android:layout_alignParentBottom="true"
         android:layout_alignParentRight="true"
         android:layout_alignParentEnd="true"
         android:layout_below="@+id/view_vertical_center"
@@ -159,20 +160,17 @@ limitations under the License.
         android:scrollbars="vertical"
         android:gravity="top"
         android:textSize="14sp"
-        android:freezesText="true" />
+        android:freezesText="true"
+        android:textIsSelectable="true"
+        android:layout_alignParentBottom="true" />
 
     <ProgressBar
         android:id="@+id/progress_bar"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignRight="@+id/view_horizontal_center"
-        android:layout_alignEnd="@+id/view_horizontal_center"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentStart="true"
-        android:layout_below="@+id/view_vertical_center"
-        android:layout_marginRight="16dp"
-        android:layout_marginEnd="16dp"
+        android:layout_alignBottom="@+id/text_input"
+        android:layout_toLeftOf="@+id/view_horizontal_center"
+        android:layout_toStartOf="@+id/view_horizontal_center"
         android:visibility="gone"
         style="@android:style/Widget.ProgressBar" />
 
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 59af380..b7445fc 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/layout/dialog_about.xml b/app/src/main/res/layout/dialog_about.xml
index 0d3a10f..c1b4982 100644
--- a/app/src/main/res/layout/dialog_about.xml
+++ b/app/src/main/res/layout/dialog_about.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -16,10 +16,14 @@ limitations under the License.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical" android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin">
 
     <TextView
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="@string/app_description"
         android:id="@+id/text_app_description" />
diff --git a/app/src/main/res/menu/display_parameters.xml b/app/src/main/res/menu/display_parameters.xml
index 6235c15..5835e65 100644
--- a/app/src/main/res/menu/display_parameters.xml
+++ b/app/src/main/res/menu/display_parameters.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml
index 97590ce..9136570 100644
--- a/app/src/main/res/menu/main.xml
+++ b/app/src/main/res/menu/main.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -44,10 +44,5 @@ limitations under the License.
         android:orderInCategory="102"
         android:title="@string/action_about"
         app:showAsAction="never" />
-    <item
-        android:id="@+id/action_exit"
-        android:orderInCategory="103"
-        android:title="@string/action_exit"
-        app:showAsAction="never" />
 
 </menu>
diff --git a/app/src/main/res/menu/settings.xml b/app/src/main/res/menu/settings.xml
index 507a733..b8dee4f 100644
--- a/app/src/main/res/menu/settings.xml
+++ b/app/src/main/res/menu/settings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/values-v11/styles.xml b/app/src/main/res/values-v11/styles.xml
index 62443d6..0eaf545 100644
--- a/app/src/main/res/values-v11/styles.xml
+++ b/app/src/main/res/values-v11/styles.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/values-v14/styles.xml b/app/src/main/res/values-v14/styles.xml
index 9134b22..35073a0 100644
--- a/app/src/main/res/values-v14/styles.xml
+++ b/app/src/main/res/values-v14/styles.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml
new file mode 100644
index 0000000..feff694
--- /dev/null
+++ b/app/src/main/res/values-v23/styles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 Arno Onken
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+
+    <!--
+        Base application theme for API 23+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 23+ devices.
+    -->
+    <style name="AppBaseTheme" parent="Theme.AppCompat">
+        <!-- API 23 theme customizations can go here. -->
+        <item name="android:colorBackgroundFloating">@color/window_background</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
index 571b4eb..a32c092 100644
--- a/app/src/main/res/values-w820dp/dimens.xml
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..302bcd0
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 Arno Onken
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+    <color name="accent">#ffff84</color>
+    <color name="primary">#311d00</color>
+    <color name="primary_dark">#000000</color>
+    <color name="foreground">#afa680</color>
+    <color name="background">#100d07</color>
+    <color name="text_color_primary">#fffded</color>
+    <color name="text_color">#fff9d4</color>
+    <color name="text_color_hint">#968c6a</color>
+    <color name="text_color_secondary">#cec499</color>
+    <color name="window_background">#181209</color>
+    <color name="separator">#fffded</color>
+</resources>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index a98ae5e..c1a0fed 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2ef2dac..e655eb2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -20,12 +20,11 @@ limitations under the License.
     <string name="input">Input</string>
     <string name="action_settings">Settings</string>
     <string name="prediction">Prediction</string>
-    <string name="edit_message">Enter a sequence of integers that were generated by a pseudo random number generator. Enter one integer per line. Refresh to get a prediction.</string>
+    <string name="edit_hint">Enter a sequence of numbers that were generated by a pseudo random number generator. Enter one number per line. Refresh to get a prediction.</string>
     <string name="action_refresh">Refresh</string>
     <string name="action_discard">Discard</string>
     <string name="action_parameters">Parameters</string>
     <string name="action_about">About</string>
-    <string name="action_exit">Exit</string>
     <string name="input_direct_name">Text field</string>
     <string name="input_file_name">File</string>
     <string name="input_socket_name">Socket</string>
@@ -34,8 +33,10 @@ limitations under the License.
     <string name="socket_error_message">Could not establish socket connection</string>
     <string name="number_error_message">Invalid input number</string>
     <string name="title_activity_display_parameters">Generator parameters</string>
+    <string name="title_dialog_about">About</string>
+    <string name="about_positive">OK</string>
     <string name="version_name">Version</string>
-    <string name="copyright">Copyright &#169; 2015 Arno Onken\nReleased under the Apache License, Version 2.0.\n</string>
+    <string name="copyright">Copyright &#169; 2015, 2016 Arno Onken\nReleased under the Apache License, Version 2.0.\n</string>
     <string name="app_description">Predicts pseudo random numbers based on a sequence of observed numbers.\n</string>
     <string name="website">Website: http://github.com/asnelt/derandom/</string>
     <string name="title_activity_settings">Settings</string>
@@ -52,7 +53,7 @@ limitations under the License.
     <string name="pref_history_length">History length</string>
     <string name="pref_history_length_summary_singular">number is kept in the input history</string>
     <string name="pref_history_length_summary_plural">numbers are kept in the input history</string>
-    <string name="pref_history_length_default_value">1024</string>
+    <string name="pref_history_length_default_value">2048</string>
     <string name="pref_parameter_base">Parameter display base</string>
     <string-array name="pref_parameter_base_entries">
         <item>Octal</item>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index bcc5305..faae7d6 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -31,6 +31,32 @@ limitations under the License.
     <!-- Application theme. -->
     <style name="AppTheme" parent="AppBaseTheme">
         <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+        <item name="colorAccent">@color/accent</item>
+        <item name="colorPrimary">@color/primary</item>
+        <item name="colorPrimaryDark">@color/primary_dark</item>
+        <item name="android:colorForeground">@color/foreground</item>
+        <item name="android:colorBackground">@color/background</item>
+        <item name="android:textColorPrimary">@color/text_color_primary</item>
+        <item name="android:textColor">@color/text_color</item>
+        <item name="android:textColorHint">@color/text_color_hint</item>
+        <item name="android:textColorSecondary">@color/text_color_secondary</item>
+        <item name="android:windowBackground">@color/window_background</item>
+        <item name="dialogTheme">@style/DialogTheme</item>
+        <item name="alertDialogTheme">@style/DialogTheme</item>
+    </style>
+
+    <!-- Dialog theme. -->
+    <style name="DialogTheme" parent="Theme.AppCompat.Dialog">
+        <item name="colorAccent">@color/accent</item>
+        <item name="colorPrimary">@color/primary</item>
+        <item name="colorPrimaryDark">@color/primary_dark</item>
+        <item name="android:colorForeground">@color/foreground</item>
+        <item name="android:colorBackground">@color/background</item>
+        <item name="android:textColorPrimary">@color/text_color_primary</item>
+        <item name="android:textColor">@color/text_color</item>
+        <item name="android:textColorHint">@color/text_color_hint</item>
+        <item name="android:textColorSecondary">@color/text_color_secondary</item>
+        <item name="android:windowBackground">@color/window_background</item>
     </style>
 
 </resources>
diff --git a/app/src/main/res/xml-v14/preferences.xml b/app/src/main/res/xml-v14/preferences.xml
new file mode 100644
index 0000000..0fd1513
--- /dev/null
+++ b/app/src/main/res/xml-v14/preferences.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2016 Arno Onken
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <PreferenceCategory
+        android:title="@string/pref_prediction_title"
+        android:key="pref_key_prediction_settings">
+        <SwitchPreference
+            android:key="pref_auto_detect"
+            android:title="@string/pref_auto_detect"
+            android:summary="@string/pref_auto_detect_summary"
+            android:defaultValue="true" />
+        <EditTextPreference
+            android:key="pref_prediction_length"
+            android:title="@string/pref_prediction_length"
+            android:dialogTitle="@string/pref_prediction_length"
+            android:inputType="number"
+            android:defaultValue="@string/pref_prediction_length_default_value" />
+    </PreferenceCategory>
+    <PreferenceCategory
+        android:title="@string/pref_io_title"
+        android:key="pref_key_io_settings">
+        <SwitchPreference
+            android:key="pref_colored_past"
+            android:title="@string/pref_colored_past"
+            android:summary="@string/pref_colored_past_summary"
+            android:defaultValue="true" />
+        <EditTextPreference
+            android:key="pref_history_length"
+            android:title="@string/pref_history_length"
+            android:dialogTitle="@string/pref_history_length"
+            android:inputType="number"
+            android:defaultValue="@string/pref_history_length_default_value" />
+        <ListPreference
+            android:key="pref_parameter_base"
+            android:title="@string/pref_parameter_base"
+            android:dialogTitle="@string/pref_parameter_base"
+            android:entries="@array/pref_parameter_base_entries"
+            android:entryValues="@array/pref_parameter_base_values"
+            android:defaultValue="@string/pref_parameter_base_default" />
+        <EditTextPreference
+            android:key="pref_socket_port"
+            android:title="@string/pref_socket_port"
+            android:dialogTitle="@string/pref_socket_port"
+            android:inputType="number"
+            android:defaultValue="@string/pref_socket_port_default_value" />
+    </PreferenceCategory>
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/backup.xml b/app/src/main/res/xml/backup.xml
index 5f94330..0edbe03 100644
--- a/app/src/main/res/xml/backup.xml
+++ b/app/src/main/res/xml/backup.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index dbb6bc9..d770112 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 Arno Onken
+Copyright (C) 2015, 2016 Arno Onken
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/build.gradle b/build.gradle
index 3b1fe98..0dea270 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:1.3.0'
+        classpath 'com.android.tools.build:gradle:2.2.3'
     }
 }
 
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index daea343..ebaa784 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,19 +1,6 @@
-# Copyright (C) 2015 Arno Onken
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#Wed Apr 10 15:27:10 PDT 2013
+#Thu Aug 18 10:28:08 CEST 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282..aec9973 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,90 +1,90 @@
-@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
-
-@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=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@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 Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_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=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-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
+@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
+
+@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=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@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 Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_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=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+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
index 6631603..eaca12d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Arno Onken
+ * Copyright (C) 2015, 2016 Arno Onken
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
-- 
GitLab