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); }); });