From 05f0f49c64dde3a00070ede33a64c37e1ec32dea Mon Sep 17 00:00:00 2001 From: Prasad Ashok Chavan Date: Sun, 24 Sep 2023 08:38:42 +0530 Subject: [PATCH 1/3] Added Bitonic sort --- Sorts/BitonicSort.js | 80 ++++++++++++++++++++++++++++++++++ Sorts/test/BitonicSort.test.js | 36 +++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 Sorts/BitonicSort.js create mode 100644 Sorts/test/BitonicSort.test.js diff --git a/Sorts/BitonicSort.js b/Sorts/BitonicSort.js new file mode 100644 index 0000000000..bbae0a84d4 --- /dev/null +++ b/Sorts/BitonicSort.js @@ -0,0 +1,80 @@ + + /* + + * JS program for bitonic sort + * Bitonic Sort is a parallel sorting algorithm that works by dividing the + array into two parts, recursively sorting them, and then merging them in a + specific way. + * more information: https://en.wikipedia.org/wiki/Bitonic_sorter + + */ + function compAndSwap(a, i, j, order){ + if ((a[i] > a[j] && order === 1) || + (a[i] < a[j] && order === 0)) + { + // Swapping elements + var temp = a[i]; + a[i] = a[j]; + a[j] = temp; + } + } + + // It recursively sorts a bitonic sequence in ascending + //order, if order = 1 + function bitonicMergeArr(a, low, cnt, dir) { + if (cnt > 1) { + var k = parseInt(cnt / 2); + for (var i = low; i < low + k; i++) + compAndSwap(a, i, i + k, dir); + bitonicMergeArr(a, low, k, dir); + bitonicMergeArr(a, low + k, k, dir); + } + } + + function bitonicSort(a, low, cnt, order) { //(arr 0 arrLen AS-Ds-order) + if (cnt > 1) { + var k = parseInt(cnt / 2); + + // sort in ascending order since order here is 1 + bitonicSort(a, low, k, 1); + + // sort in descending order since order here is 0 + bitonicSort(a, low + k, k, 0); + + // Will merge whole sequence in ascending order + // since dir=1. + bitonicMergeArr(a, low, cnt, order); + } + } + + //Calling of bitonicSort func for sorting the entire array + //of length N in ASCENDING order + //here up=1 for ASCENDING & up=0 for DESCENDING + function sort(a, N, up) { + bitonicSort(a, 0, N, up); + } + + //displaying array + function logArray(arr) { + for (var i = 0; i < arr.length; ++i) + console.log(arr[i] + " "); + } + + export{ sort } + + // Test Case method + // var a = [4, 16, 8, 0, 100]; + // var up = 1; //change to 0 for Descending + // sort(a, a.length, up); + // console.log("Sorted array: "); + // logArray(a); + + /** Output: + * Sorted array: + 0 + 4 + 8 + 16 + 100 + */ + \ No newline at end of file diff --git a/Sorts/test/BitonicSort.test.js b/Sorts/test/BitonicSort.test.js new file mode 100644 index 0000000000..076c3be772 --- /dev/null +++ b/Sorts/test/BitonicSort.test.js @@ -0,0 +1,36 @@ +import {sort} from '../BitonicSort' + +// Suppose 1 Array having elements as power of 2 + var a = [4, 16, 8, 0, 100]; + var up = 1; //change to 0 for Descending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); //[0, 4, 8, 16, 100] + +// Test Case 2 + var a = [-4, 32, -8, 40, 80]; + var up = 1; //change to 0 for Descending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); //[-8, -4, 32, 40, 80] + +// Test Case 3 + var a = [24, 32, 80, 40, 16]; + var up = 0; //change to 1 for Ascending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); //[80, 40, 32, 24, 16] + +// Test Case 4 + var a = [24, 32]; + var up = 0; //change to 0 for Descending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); [32, 24] + +// Test Case 5 + var a = [8]; + var up = 0; //change to 0 for Descending + sort(a, a.length, up); + console.log("Sorted array: "); + logArray(a); [8] \ No newline at end of file From a10b5bd69e647fbf7b055703fb3b9b4775d7ddf5 Mon Sep 17 00:00:00 2001 From: Prasad Ashok Chavan Date: Sun, 24 Sep 2023 09:11:29 +0530 Subject: [PATCH 2/3] Update Fixes --- Sorts/BitonicSort.js | 120 ++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/Sorts/BitonicSort.js b/Sorts/BitonicSort.js index bbae0a84d4..9b91db3a3b 100644 --- a/Sorts/BitonicSort.js +++ b/Sorts/BitonicSort.js @@ -1,80 +1,74 @@ +/* - /* - * JS program for bitonic sort * Bitonic Sort is a parallel sorting algorithm that works by dividing the array into two parts, recursively sorting them, and then merging them in a - specific way. + specific way. * more information: https://en.wikipedia.org/wiki/Bitonic_sorter - + */ - function compAndSwap(a, i, j, order){ - if ((a[i] > a[j] && order === 1) || - (a[i] < a[j] && order === 0)) - { - // Swapping elements - var temp = a[i]; - a[i] = a[j]; - a[j] = temp; - } - } +function compAndSwap (a, i, j, order) { + if ((a[i] > a[j] && order === 1) || (a[i] < a[j] && order === 0)) { + // Swapping elements + const temp = a[i] + a[i] = a[j] + a[j] = temp + } +} - // It recursively sorts a bitonic sequence in ascending - //order, if order = 1 - function bitonicMergeArr(a, low, cnt, dir) { - if (cnt > 1) { - var k = parseInt(cnt / 2); - for (var i = low; i < low + k; i++) - compAndSwap(a, i, i + k, dir); - bitonicMergeArr(a, low, k, dir); - bitonicMergeArr(a, low + k, k, dir); - } - } +// It recursively sorts a bitonic sequence in ascending +// order, if order = 1 +function bitonicMergeArr (a, low, cnt, dir) { + if (cnt > 1) { + const k = parseInt(cnt / 2) + for (let i = low; i < low + k; i++) { compAndSwap(a, i, i + k, dir) } + bitonicMergeArr(a, low, k, dir) + bitonicMergeArr(a, low + k, k, dir) + } +} - function bitonicSort(a, low, cnt, order) { //(arr 0 arrLen AS-Ds-order) - if (cnt > 1) { - var k = parseInt(cnt / 2); +function bitonicSort (a, low, cnt, order) { // (arr 0 arrLen AS-Ds-order) + if (cnt > 1) { + const k = parseInt(cnt / 2) - // sort in ascending order since order here is 1 - bitonicSort(a, low, k, 1); + // sort in ascending order since order here is 1 + bitonicSort(a, low, k, 1) - // sort in descending order since order here is 0 - bitonicSort(a, low + k, k, 0); + // sort in descending order since order here is 0 + bitonicSort(a, low + k, k, 0) - // Will merge whole sequence in ascending order - // since dir=1. - bitonicMergeArr(a, low, cnt, order); - } - } + // Will merge whole sequence in ascending order + // since dir=1. + bitonicMergeArr(a, low, cnt, order) + } +} - //Calling of bitonicSort func for sorting the entire array - //of length N in ASCENDING order - //here up=1 for ASCENDING & up=0 for DESCENDING - function sort(a, N, up) { - bitonicSort(a, 0, N, up); - } +// Calling of bitonicSort func for sorting the entire array +// of length N in ASCENDING order +// here up=1 for ASCENDING & up=0 for DESCENDING +function sort (a, N, up) { + bitonicSort(a, 0, N, up) +} - //displaying array - function logArray(arr) { - for (var i = 0; i < arr.length; ++i) - console.log(arr[i] + " "); - } +// displaying array +function logArray (arr) { + for (let i = 0; i < arr.length; ++i) { console.log(arr[i] + ' ') } +} - export{ sort } +export { sort } - // Test Case method - // var a = [4, 16, 8, 0, 100]; - // var up = 1; //change to 0 for Descending - // sort(a, a.length, up); - // console.log("Sorted array: "); - // logArray(a); +// Test Case method +const a = [4, 16, 8, 0, 100] +const up = 1 // change to 0 for Descending +sort(a, a.length, up) +console.log('Sorted array: ') +logArray(a) - /** Output: - * Sorted array: - 0 - 4 - 8 - 16 - 100 +/** Output: + * Sorted array: + 0 + 4 + 8 + 16 + 100 */ - \ No newline at end of file From 8f57cf3647411d5acc437b5fc19b1b5cd2db2f1e Mon Sep 17 00:00:00 2001 From: Prasad Ashok Chavan Date: Mon, 25 Sep 2023 12:04:04 +0530 Subject: [PATCH 3/3] Polynomial Hash --- Hashes/PolynomialHash.js | 99 ++++++++++++++++++++++++++++++ Hashes/test/PolynomialHash.test.js | 59 ++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 Hashes/PolynomialHash.js create mode 100644 Hashes/test/PolynomialHash.test.js diff --git a/Hashes/PolynomialHash.js b/Hashes/PolynomialHash.js new file mode 100644 index 0000000000..4475ce9299 --- /dev/null +++ b/Hashes/PolynomialHash.js @@ -0,0 +1,99 @@ +/** + * Polynomial Hash also known as Polynomial rolling hash function. + * + * Polynomial rolling hash function is a hash function that uses only multiplications and additions. + * + * + * NOTE: If two strings are equal, their hash values should also be equal. But the inverse need not be true. + * + * Wikipedia: https://en.wikipedia.org/wiki/Rolling_hash + */ +const DEFAULT_BASE = 37; +const DEFAULT_MODULUS = 101; + +export default class PolynomialHash { + /** + * @param {number} [base] - Base number that is used to create the polynomial. + * @param {number} [modulus] - Modulus number that keeps the hash from overflowing. + */ + constructor({ base = DEFAULT_BASE, modulus = DEFAULT_MODULUS } = {}) { + this.base = base; + this.modulus = modulus; + } + + /** + * Function that creates hash representation of the word. + * + * Time complexity: O(word.length). + * + * @param {string} word - String that needs to be hashed. + * @return {number} + */ + hash(word) { + const charCodes = Array.from(word).map((char) => this.charToNumber(char)); + + let hash = 0; + for (let charIndex = 0; charIndex < charCodes.length; charIndex += 1) { + hash *= this.base; + hash += charCodes[charIndex]; + hash %= this.modulus; + } + + return hash; + } + + /** + * Function that creates hash representation of the word + * based on previous word (shifted by one character left) hash value. + * + * Recalculates the hash representation of a word so that it isn't + * necessary to traverse the whole word again. + * + * Time complexity: O(1). + * + * @param {number} prevHash + * @param {string} prevWord + * @param {string} newWord + * @return {number} + */ + roll(prevHash, prevWord, newWord) { + let hash = prevHash; + + const prevValue = this.charToNumber(prevWord[0]); + const newValue = this.charToNumber(newWord[newWord.length - 1]); + + let prevValueMultiplier = 1; + for (let i = 1; i < prevWord.length; i += 1) { + prevValueMultiplier *= this.base; + prevValueMultiplier %= this.modulus; + } + + hash += this.modulus; + hash -= (prevValue * prevValueMultiplier) % this.modulus; + + hash *= this.base; + hash += newValue; + hash %= this.modulus; + + 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; + } +} \ No newline at end of file diff --git a/Hashes/test/PolynomialHash.test.js b/Hashes/test/PolynomialHash.test.js new file mode 100644 index 0000000000..4d1c15a043 --- /dev/null +++ b/Hashes/test/PolynomialHash.test.js @@ -0,0 +1,59 @@ +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]; + + // @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); + + // 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 less than 100', () => { + const polynomialHash = new PolynomialHash({ modulus: 100 }); + + expect(polynomialHash.hash('Some long text that is used as a key')).toBe(41); + expect(polynomialHash.hash('Test')).toBe(92); + expect(polynomialHash.hash('a')).toBe(97); + expect(polynomialHash.hash('b')).toBe(98); + expect(polynomialHash.hash('c')).toBe(99); + expect(polynomialHash.hash('d')).toBe(0); + expect(polynomialHash.hash('e')).toBe(1); + expect(polynomialHash.hash('ab')).toBe(87); + + // @TODO: Provide Unicode support. + expect(polynomialHash.hash('\u{20000}')).toBe(92); + }); +}); \ No newline at end of file