From c413c33a204795c3d9ea66c1573d079cd9608950 Mon Sep 17 00:00:00 2001
From: Nicolas DUBIEN <github@dubien.org>
Date: Thu, 14 Feb 2019 00:49:09 +0100
Subject: [PATCH 1/5] Fix eslint on pre-commit

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index d2dda87267..aec18d563b 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "description": "Algorithms and data-structures implemented on JavaScript",
   "main": "index.js",
   "scripts": {
-    "lint": "eslint ./src/*",
+    "lint": "eslint src/",
     "test": "jest",
     "ci": "npm run lint && npm run test -- --coverage"
   },

From 669fd31097ed30034dac46f56960227566d218f6 Mon Sep 17 00:00:00 2001
From: Nicolas DUBIEN <github@dubien.org>
Date: Thu, 14 Feb 2019 00:50:09 +0100
Subject: [PATCH 2/5] Switch to property based for PolynomialHash

---
 package-lock.json                             | 115 ++++++++++++++----
 package.json                                  |   1 +
 .../__test__/PolynomialHash.test.js           |  34 ++----
 .../__test__/SimplePolynomialHash.test.js     |  63 +++++-----
 4 files changed, 136 insertions(+), 77 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 342f9d381c..5cafdaab95 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2263,6 +2263,16 @@
       "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
       "dev": true
     },
+    "fast-check": {
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-1.10.1.tgz",
+      "integrity": "sha512-7TTA4QMpoSUX5nbwsOfSQAOllKFUA3FWzlTIT7V57ZN/Ju6q/wCeRXDkygNI0nVAEfPeva+fPlPlbAqizCXIUw==",
+      "dev": true,
+      "requires": {
+        "lorem-ipsum": "~1.0.6",
+        "pure-rand": "^1.6.2"
+      }
+    },
     "fast-deep-equal": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
@@ -2444,7 +2454,8 @@
         "ansi-regex": {
           "version": "2.1.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "aproba": {
           "version": "1.2.0",
@@ -2465,12 +2476,14 @@
         "balanced-match": {
           "version": "1.0.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "brace-expansion": {
           "version": "1.1.11",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -2485,17 +2498,20 @@
         "code-point-at": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "console-control-strings": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
@@ -2612,7 +2628,8 @@
         "inherits": {
           "version": "2.0.3",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "ini": {
           "version": "1.3.5",
@@ -2624,6 +2641,7 @@
           "version": "1.0.0",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
@@ -2638,6 +2656,7 @@
           "version": "3.0.4",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
@@ -2645,12 +2664,14 @@
         "minimist": {
           "version": "0.0.8",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "minipass": {
           "version": "2.2.4",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "safe-buffer": "^5.1.1",
             "yallist": "^3.0.0"
@@ -2669,6 +2690,7 @@
           "version": "0.5.1",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "minimist": "0.0.8"
           }
@@ -2749,7 +2771,8 @@
         "number-is-nan": {
           "version": "1.0.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
@@ -2761,6 +2784,7 @@
           "version": "1.4.0",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "wrappy": "1"
           }
@@ -2846,7 +2870,8 @@
         "safe-buffer": {
           "version": "5.1.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "safer-buffer": {
           "version": "2.1.2",
@@ -2882,6 +2907,7 @@
           "version": "1.0.2",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -2901,6 +2927,7 @@
           "version": "3.0.1",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "ansi-regex": "^2.0.0"
           }
@@ -2944,12 +2971,14 @@
         "wrappy": {
           "version": "1.0.2",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "yallist": {
           "version": "3.0.2",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         }
       }
     },
@@ -4761,6 +4790,23 @@
         "js-tokens": "^3.0.0"
       }
     },
+    "lorem-ipsum": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/lorem-ipsum/-/lorem-ipsum-1.0.6.tgz",
+      "integrity": "sha512-Rx4XH8X4KSDCKAVvWGYlhAfNqdUP5ZdT4rRyf0jjrvWgtViZimDIlopWNfn/y3lGM5K4uuiAoY28TaD+7YKFrQ==",
+      "dev": true,
+      "requires": {
+        "minimist": "~1.2.0"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
     "lru-cache": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
@@ -5526,6 +5572,12 @@
       "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
       "dev": true
     },
+    "pure-rand": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-1.6.2.tgz",
+      "integrity": "sha512-HNwHOH63m7kCxe0kWEe5jSLwJiL2N83RUUN8POniFuZS+OsbFcMWlvXgxIU2nwKy2zYG2bQan40WBNK4biYPRg==",
+      "dev": true
+    },
     "qs": {
       "version": "6.5.2",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@@ -6152,7 +6204,8 @@
             "ansi-regex": {
               "version": "2.1.1",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "aproba": {
               "version": "1.2.0",
@@ -6173,12 +6226,14 @@
             "balanced-match": {
               "version": "1.0.0",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "brace-expansion": {
               "version": "1.1.11",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "balanced-match": "^1.0.0",
                 "concat-map": "0.0.1"
@@ -6193,17 +6248,20 @@
             "code-point-at": {
               "version": "1.1.0",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "concat-map": {
               "version": "0.0.1",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "console-control-strings": {
               "version": "1.1.0",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "core-util-is": {
               "version": "1.0.2",
@@ -6320,7 +6378,8 @@
             "inherits": {
               "version": "2.0.3",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "ini": {
               "version": "1.3.5",
@@ -6332,6 +6391,7 @@
               "version": "1.0.0",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "number-is-nan": "^1.0.0"
               }
@@ -6346,6 +6406,7 @@
               "version": "3.0.4",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "brace-expansion": "^1.1.7"
               }
@@ -6353,12 +6414,14 @@
             "minimist": {
               "version": "0.0.8",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "minipass": {
               "version": "2.2.4",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "safe-buffer": "^5.1.1",
                 "yallist": "^3.0.0"
@@ -6377,6 +6440,7 @@
               "version": "0.5.1",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "minimist": "0.0.8"
               }
@@ -6457,7 +6521,8 @@
             "number-is-nan": {
               "version": "1.0.1",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "object-assign": {
               "version": "4.1.1",
@@ -6469,6 +6534,7 @@
               "version": "1.4.0",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "wrappy": "1"
               }
@@ -6554,7 +6620,8 @@
             "safe-buffer": {
               "version": "5.1.1",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "safer-buffer": {
               "version": "2.1.2",
@@ -6590,6 +6657,7 @@
               "version": "1.0.2",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "code-point-at": "^1.0.0",
                 "is-fullwidth-code-point": "^1.0.0",
@@ -6609,6 +6677,7 @@
               "version": "3.0.1",
               "bundled": true,
               "dev": true,
+              "optional": true,
               "requires": {
                 "ansi-regex": "^2.0.0"
               }
@@ -6652,12 +6721,14 @@
             "wrappy": {
               "version": "1.0.2",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             },
             "yallist": {
               "version": "3.0.2",
               "bundled": true,
-              "dev": true
+              "dev": true,
+              "optional": true
             }
           }
         },
diff --git a/package.json b/package.json
index aec18d563b..72610d8589 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
     "eslint-plugin-jest": "^22.1.0",
     "eslint-plugin-jsx-a11y": "^6.1.2",
     "eslint-plugin-react": "^7.11.1",
+    "fast-check": "^1.10.1",
     "jest": "^23.6.0",
     "pre-commit": "^1.2.2"
   },
diff --git a/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js b/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
index 0d487848dc..0db25b6bf0 100644
--- a/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
+++ b/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
@@ -1,26 +1,18 @@
+import fc from 'fast-check';
 import PolynomialHash from '../PolynomialHash';
 
 describe('PolynomialHash', () => {
   it('should calculate new hash based on previous one', () => {
-    const bases = [3, 79, 101, 3251, 13229, 122743, 3583213];
-    const mods = [79, 101];
-    const frameSizes = [5, 20];
+    fc.assert(
+      fc.property(
+        fc.constantFrom(3, 79, 101, 3251, 13229, 122743, 3583213),
+        fc.integer(2, 0x7fffffff),
+        fc.integer(1, 50),
+        fc.unicodeString(0, 100), // no surrogate pairs
+        (base, modulus, frameSize, text) => {
+          fc.pre(base * modulus < 0x7fffffff); // avoid overflows
+          const polynomialHash = new PolynomialHash({ base, modulus });
 
-    // @TODO: Provide Unicode support.
-    const text = 'Lorem Ipsum is simply dummy text of the printing and '
-      + 'typesetting industry. Lorem Ipsum has been the industry\'s standard '
-      + 'galley of type and \u{ffff} scrambled it to make a type specimen book. It '
-      + 'electronic 耀 typesetting, remaining essentially unchanged. It was '
-      // + 'popularised in the \u{20005} \u{20000}1960s with the release of Letraset sheets '
-      + 'publishing software like Aldus PageMaker 耀 including versions of Lorem.';
-
-    // Check hashing for different prime base.
-    bases.forEach((base) => {
-      mods.forEach((modulus) => {
-        const polynomialHash = new PolynomialHash({ base, modulus });
-
-        // Check hashing for different word lengths.
-        frameSizes.forEach((frameSize) => {
           let previousWord = text.substr(0, frameSize);
           let previousHash = polynomialHash.hash(previousWord);
 
@@ -36,9 +28,9 @@ describe('PolynomialHash', () => {
             previousWord = currentWord;
             previousHash = currentHash;
           }
-        });
-      });
-    });
+        },
+      ),
+    );
   });
 
   it('should generate numeric hashed less than 100', () => {
diff --git a/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js b/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
index 28c551966d..4bad6c10e0 100644
--- a/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
+++ b/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
@@ -1,40 +1,35 @@
+import fc from 'fast-check';
 import SimplePolynomialHash from '../SimplePolynomialHash';
 
-describe('PolynomialHash', () => {
+describe('SimplePolynomialHash', () => {
   it('should calculate new hash based on previous one', () => {
-    const bases = [3, 5];
-    const frameSizes = [5, 10];
-
-    const text = 'Lorem Ipsum is simply dummy text of the printing and '
-      + 'typesetting industry. Lorem Ipsum has been the industry\'s standard '
-      + 'galley of type and \u{ffff} scrambled it to make a type specimen book. It '
-      + 'electronic 耀 typesetting, remaining essentially unchanged. It was '
-      + 'popularised in the 1960s with the release of Letraset sheets '
-      + 'publishing software like Aldus 耀 PageMaker including versions of Lorem.';
-
-    // Check hashing for different prime base.
-    bases.forEach((base) => {
-      const polynomialHash = new SimplePolynomialHash(base);
-
-      // Check hashing for different word lengths.
-      frameSizes.forEach((frameSize) => {
-        let previousWord = text.substr(0, frameSize);
-        let previousHash = polynomialHash.hash(previousWord);
-
-        // Shift frame through the whole text.
-        for (let frameShift = 1; frameShift < (text.length - frameSize); frameShift += 1) {
-          const currentWord = text.substr(frameShift, frameSize);
-          const currentHash = polynomialHash.hash(currentWord);
-          const currentRollingHash = polynomialHash.roll(previousHash, previousWord, currentWord);
-
-          // Check that rolling hash is the same as directly calculated hash.
-          expect(currentRollingHash).toBe(currentHash);
-
-          previousWord = currentWord;
-          previousHash = currentHash;
-        }
-      });
-    });
+    fc.assert(
+      fc.property(
+        fc.constantFrom(3, 79, 101, 3251, 13229, 122743, 3583213),
+        fc.integer(1, 10),
+        fc.unicodeString(0, 100), // no surrogate pairs
+        (base, frameSize, text) => {
+          fc.pre(0xffff * (base ** (frameSize - 1)) < 0x7fffffff); // avoid overflows
+          const polynomialHash = new SimplePolynomialHash(base);
+
+          let previousWord = text.substr(0, frameSize);
+          let previousHash = polynomialHash.hash(previousWord);
+
+          // Shift frame through the whole text.
+          for (let frameShift = 1; frameShift < (text.length - frameSize); frameShift += 1) {
+            const currentWord = text.substr(frameShift, frameSize);
+            const currentHash = polynomialHash.hash(currentWord);
+            const currentRollingHash = polynomialHash.roll(previousHash, previousWord, currentWord);
+
+            // Check that rolling hash is the same as directly calculated hash.
+            expect(currentRollingHash).toBe(currentHash);
+
+            previousWord = currentWord;
+            previousHash = currentHash;
+          }
+        },
+      ),
+    );
   });
 
   it('should generate numeric hashed', () => {

From c3b695d2cb6528f3a5c7c8b95727ff5e1450cc61 Mon Sep 17 00:00:00 2001
From: Nicolas DUBIEN <github@dubien.org>
Date: Thu, 14 Feb 2019 01:29:22 +0100
Subject: [PATCH 3/5] Simplify test for PolynomialHash.roll

---
 .../__test__/PolynomialHash.test.js           | 31 ++++++++-----------
 .../__test__/SimplePolynomialHash.test.js     | 31 ++++++++-----------
 2 files changed, 26 insertions(+), 36 deletions(-)

diff --git a/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js b/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
index 0db25b6bf0..c5349bbdc3 100644
--- a/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
+++ b/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
@@ -7,27 +7,22 @@ describe('PolynomialHash', () => {
       fc.property(
         fc.constantFrom(3, 79, 101, 3251, 13229, 122743, 3583213),
         fc.integer(2, 0x7fffffff),
-        fc.integer(1, 50),
-        fc.unicodeString(0, 100), // no surrogate pairs
-        (base, modulus, frameSize, text) => {
-          fc.pre(base * modulus < 0x7fffffff); // avoid overflows
-          const polynomialHash = new PolynomialHash({ base, modulus });
-
-          let previousWord = text.substr(0, frameSize);
-          let previousHash = polynomialHash.hash(previousWord);
+        fc.string(0, 50),
+        fc.char(),
+        fc.char(),
+        (base, modulus, commonWord, previousChar, newChar) => {
+          fc.pre(base * modulus + 0x10ffff < 0x7fffffff); // avoid overflows
 
-          // Shift frame through the whole text.
-          for (let frameShift = 1; frameShift < (text.length - frameSize); frameShift += 1) {
-            const currentWord = text.substr(frameShift, frameSize);
-            const currentHash = polynomialHash.hash(currentWord);
-            const currentRollingHash = polynomialHash.roll(previousHash, previousWord, currentWord);
+          const polynomialHash = new PolynomialHash({ base, modulus });
+          const previousWord = previousChar + commonWord;
+          const currentWord = commonWord + newChar;
+          const previousHash = polynomialHash.hash(previousWord);
 
-            // Check that rolling hash is the same as directly calculated hash.
-            expect(currentRollingHash).toBe(currentHash);
+          const currentHash = polynomialHash.hash(currentWord);
+          const currentRollingHash = polynomialHash.roll(previousHash, previousWord, currentWord);
 
-            previousWord = currentWord;
-            previousHash = currentHash;
-          }
+          // Check that rolling hash is the same as directly calculated hash.
+          expect(currentRollingHash).toBe(currentHash);
         },
       ),
     );
diff --git a/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js b/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
index 4bad6c10e0..2574f024fc 100644
--- a/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
+++ b/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
@@ -6,27 +6,22 @@ describe('SimplePolynomialHash', () => {
     fc.assert(
       fc.property(
         fc.constantFrom(3, 79, 101, 3251, 13229, 122743, 3583213),
-        fc.integer(1, 10),
-        fc.unicodeString(0, 100), // no surrogate pairs
-        (base, frameSize, text) => {
-          fc.pre(0xffff * (base ** (frameSize - 1)) < 0x7fffffff); // avoid overflows
-          const polynomialHash = new SimplePolynomialHash(base);
-
-          let previousWord = text.substr(0, frameSize);
-          let previousHash = polynomialHash.hash(previousWord);
+        fc.string(0, 50),
+        fc.char(),
+        fc.char(),
+        (base, commonWord, previousChar, newChar) => {
+          fc.pre(0xffff * (base ** commonWord.length) < 0x7fffffff); // avoid overflows
 
-          // Shift frame through the whole text.
-          for (let frameShift = 1; frameShift < (text.length - frameSize); frameShift += 1) {
-            const currentWord = text.substr(frameShift, frameSize);
-            const currentHash = polynomialHash.hash(currentWord);
-            const currentRollingHash = polynomialHash.roll(previousHash, previousWord, currentWord);
+          const polynomialHash = new SimplePolynomialHash(base);
+          const previousWord = previousChar + commonWord;
+          const currentWord = commonWord + newChar;
+          const previousHash = polynomialHash.hash(previousWord);
 
-            // Check that rolling hash is the same as directly calculated hash.
-            expect(currentRollingHash).toBe(currentHash);
+          const currentHash = polynomialHash.hash(currentWord);
+          const currentRollingHash = polynomialHash.roll(previousHash, previousWord, currentWord);
 
-            previousWord = currentWord;
-            previousHash = currentHash;
-          }
+          // Check that rolling hash is the same as directly calculated hash.
+          expect(currentRollingHash).toBe(currentHash);
         },
       ),
     );

From dd3437f28aeda0743f831a11fe9239c689cf2642 Mon Sep 17 00:00:00 2001
From: Nicolas DUBIEN <github@dubien.org>
Date: Thu, 14 Feb 2019 01:49:33 +0100
Subject: [PATCH 4/5] Add support for unicode strings in SimplePolynomialHash

---
 .../polynomial-hash/SimplePolynomialHash.js         | 13 ++++++++-----
 .../__test__/SimplePolynomialHash.test.js           |  6 +++---
 2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/src/algorithms/cryptography/polynomial-hash/SimplePolynomialHash.js b/src/algorithms/cryptography/polynomial-hash/SimplePolynomialHash.js
index 8e1d5123f4..c6727e4d57 100644
--- a/src/algorithms/cryptography/polynomial-hash/SimplePolynomialHash.js
+++ b/src/algorithms/cryptography/polynomial-hash/SimplePolynomialHash.js
@@ -22,9 +22,11 @@ export default class SimplePolynomialHash {
    * @return {number}
    */
   hash(word) {
+    const charCodes = Array.from(word).map(char => char.codePointAt(0));
+
     let hash = 0;
-    for (let charIndex = 0; charIndex < word.length; charIndex += 1) {
-      hash += word.charCodeAt(charIndex) * (this.base ** charIndex);
+    for (let charIndex = 0; charIndex < charCodes.length; charIndex += 1) {
+      hash += charCodes[charIndex] * (this.base ** charIndex);
     }
 
     return hash;
@@ -51,12 +53,13 @@ export default class SimplePolynomialHash {
   roll(prevHash, prevWord, newWord) {
     let hash = prevHash;
 
-    const prevValue = prevWord.charCodeAt(0);
-    const newValue = newWord.charCodeAt(newWord.length - 1);
+    const prevValue = prevWord.codePointAt(0);
+    const newWordChars = Array.from(newWord);
+    const newValue = newWordChars[newWordChars.length - 1].codePointAt(0);
 
     hash -= prevValue;
     hash /= this.base;
-    hash += newValue * (this.base ** (newWord.length - 1));
+    hash += newValue * (this.base ** (newWordChars.length - 1));
 
     return hash;
   }
diff --git a/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js b/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
index 2574f024fc..8118c9d0ee 100644
--- a/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
+++ b/src/algorithms/cryptography/polynomial-hash/__test__/SimplePolynomialHash.test.js
@@ -6,9 +6,9 @@ describe('SimplePolynomialHash', () => {
     fc.assert(
       fc.property(
         fc.constantFrom(3, 79, 101, 3251, 13229, 122743, 3583213),
-        fc.string(0, 50),
-        fc.char(),
-        fc.char(),
+        fc.fullUnicodeString(0, 50),
+        fc.fullUnicode(),
+        fc.fullUnicode(),
         (base, commonWord, previousChar, newChar) => {
           fc.pre(0xffff * (base ** commonWord.length) < 0x7fffffff); // avoid overflows
 

From 944c63cfcd8689bfa743a9e7592d3b19d3d7964a Mon Sep 17 00:00:00 2001
From: Nicolas DUBIEN <github@dubien.org>
Date: Thu, 14 Feb 2019 01:56:13 +0100
Subject: [PATCH 5/5] Add support for unicode in PolynomialHash.roll

---
 .../polynomial-hash/PolynomialHash.js         | 28 ++++---------------
 .../__test__/PolynomialHash.test.js           |  8 +++---
 2 files changed, 9 insertions(+), 27 deletions(-)

diff --git a/src/algorithms/cryptography/polynomial-hash/PolynomialHash.js b/src/algorithms/cryptography/polynomial-hash/PolynomialHash.js
index ba066c3138..79a5eead82 100644
--- a/src/algorithms/cryptography/polynomial-hash/PolynomialHash.js
+++ b/src/algorithms/cryptography/polynomial-hash/PolynomialHash.js
@@ -20,7 +20,7 @@ export default class PolynomialHash {
    * @return {number}
    */
   hash(word) {
-    const charCodes = Array.from(word).map(char => this.charToNumber(char));
+    const charCodes = Array.from(word).map(char => char.codePointAt(0));
 
     let hash = 0;
     for (let charIndex = 0; charIndex < charCodes.length; charIndex += 1) {
@@ -49,11 +49,12 @@ export default class PolynomialHash {
   roll(prevHash, prevWord, newWord) {
     let hash = prevHash;
 
-    const prevValue = this.charToNumber(prevWord[0]);
-    const newValue = this.charToNumber(newWord[newWord.length - 1]);
+    const prevValue = prevWord.codePointAt(0);
+    const newWordChars = Array.from(newWord);
+    const newValue = newWordChars[newWordChars.length - 1].codePointAt(0);
 
     let prevValueMultiplier = 1;
-    for (let i = 1; i < prevWord.length; i += 1) {
+    for (let i = 1; i < newWordChars.length; i += 1) {
       prevValueMultiplier *= this.base;
       prevValueMultiplier %= this.modulus;
     }
@@ -67,23 +68,4 @@ export default class PolynomialHash {
 
     return hash;
   }
-
-  /**
-   * Converts char to number.
-   *
-   * @param {string} char
-   * @return {number}
-   */
-  charToNumber(char) {
-    let charCode = char.codePointAt(0);
-
-    // Check if character has surrogate pair.
-    const surrogate = char.codePointAt(1);
-    if (surrogate !== undefined) {
-      const surrogateShift = 2 ** 16;
-      charCode += surrogate * surrogateShift;
-    }
-
-    return charCode;
-  }
 }
diff --git a/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js b/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
index c5349bbdc3..28d337b2be 100644
--- a/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
+++ b/src/algorithms/cryptography/polynomial-hash/__test__/PolynomialHash.test.js
@@ -7,9 +7,9 @@ describe('PolynomialHash', () => {
       fc.property(
         fc.constantFrom(3, 79, 101, 3251, 13229, 122743, 3583213),
         fc.integer(2, 0x7fffffff),
-        fc.string(0, 50),
-        fc.char(),
-        fc.char(),
+        fc.fullUnicodeString(0, 50),
+        fc.fullUnicode(),
+        fc.fullUnicode(),
         (base, modulus, commonWord, previousChar, newChar) => {
           fc.pre(base * modulus + 0x10ffff < 0x7fffffff); // avoid overflows
 
@@ -41,6 +41,6 @@ describe('PolynomialHash', () => {
     expect(polynomialHash.hash('ab')).toBe(87);
 
     // @TODO: Provide Unicode support.
-    expect(polynomialHash.hash('\u{20000}')).toBe(92);
+    expect(polynomialHash.hash('\u{20000}')).toBe(72);
   });
 });