From ff606a019e693d56752ad536ec69b771488420ab Mon Sep 17 00:00:00 2001 From: David Leal Date: Mon, 17 Oct 2022 16:56:18 -0500 Subject: [PATCH 01/44] chore: improve the Gitpod file (#1203) * chore: improve the Gitpod file * Updated Documentation in README.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .gitpod.yml | 10 +++++++++- DIRECTORY.md | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitpod.yml b/.gitpod.yml index 655da0e58..3f6c645c7 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,3 +1,11 @@ +github: + prebuilds: + addBadge: true + addComment: false + addCheck: false + master: true + branches: true + pullRequestsFromForks: true + tasks: - init: npm install - diff --git a/DIRECTORY.md b/DIRECTORY.md index 5f668918c..e1bbd7700 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -89,6 +89,7 @@ * [ClimbingStairs](Dynamic-Programming/ClimbingStairs.js) * [CoinChange](Dynamic-Programming/CoinChange.js) * [EditDistance](Dynamic-Programming/EditDistance.js) + * [FastFibonacciNumber](Dynamic-Programming/FastFibonacciNumber.js) * [FibonacciNumber](Dynamic-Programming/FibonacciNumber.js) * [FindMonthCalendar](Dynamic-Programming/FindMonthCalendar.js) * [KadaneAlgo](Dynamic-Programming/KadaneAlgo.js) @@ -240,6 +241,7 @@ * [Problem020](Project-Euler/Problem020.js) * [Problem023](Project-Euler/Problem023.js) * [Problem025](Project-Euler/Problem025.js) + * [Problem028](Project-Euler/Problem028.js) * [Problem044](Project-Euler/Problem044.js) * **Recursive** * [BinaryEquivalent](Recursive/BinaryEquivalent.js) From 55f502e1f1583063c0f835c1072e75c65dd851ef Mon Sep 17 00:00:00 2001 From: Sandra Laguna <73791262+sandra-laguna@users.noreply.github.com> Date: Wed, 19 Oct 2022 13:22:27 +0200 Subject: [PATCH 02/44] algorithm: count letters (#1164) --- String/CountLetters.js | 33 ++++++++++++++++++++++++++++++++ String/test/CountLetters.test.js | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 String/CountLetters.js create mode 100644 String/test/CountLetters.test.js diff --git a/String/CountLetters.js b/String/CountLetters.js new file mode 100644 index 000000000..6b6400150 --- /dev/null +++ b/String/CountLetters.js @@ -0,0 +1,33 @@ +/** + * @function countLetters + * @description Given a string, count the number of each letter. + * @param {String} str - The input string + * @return {Object} - Object with letters and number of times + * @example countLetters("hello") => {h: 1, e: 1, l: 2, o: 1} + */ + +const countLetters = (str) => { + const specialChars = /\W/g + + if (typeof str !== 'string') { + throw new TypeError('Input should be a string') + } + + if (specialChars.test(str)) { + throw new TypeError('Input must not contain special characters') + } + + if (/\d/.test(str)) { + throw new TypeError('Input must not contain numbers') + } + + const obj = {} + for (let i = 0; i < str.toLowerCase().length; i++) { + const char = str.toLowerCase().charAt(i) + obj[char] = (obj[char] || 0) + 1 + } + + return obj +} + +export { countLetters } diff --git a/String/test/CountLetters.test.js b/String/test/CountLetters.test.js new file mode 100644 index 000000000..424dcfa31 --- /dev/null +++ b/String/test/CountLetters.test.js @@ -0,0 +1,33 @@ +import { countLetters } from '../CountLetters' + +describe('CountLetters', () => { + it('expect throws on use wrong param', () => { + expect(() => countLetters(0)).toThrow() + }) + + it('expect throws when using a number in the string', () => { + expect(() => countLetters('h3llo')).toThrow() + }) + + it('expect throws when using a special characters in the string', () => { + expect(() => countLetters('hello!')).toThrow() + }) + + it('count the letters in a string. Allows lower case', () => { + const value = 'hello' + const count = countLetters(value) + expect(count).toEqual({ h: 1, e: 1, l: 2, o: 1 }) + }) + + it('count the letters in a string. Allows upper case', () => { + const value = 'HELLO' + const count = countLetters(value) + expect(count).toEqual({ h: 1, e: 1, l: 2, o: 1 }) + }) + + it('count the letters in a string. Allows upper and lower case', () => { + const value = 'HelLo' + const count = countLetters(value) + expect(count).toEqual({ h: 1, e: 1, l: 2, o: 1 }) + }) +}) From 0084acf2d4dc9ac9bc32a5db6cd27255f0835ae7 Mon Sep 17 00:00:00 2001 From: ddaniel27 <67126972+ddaniel27@users.noreply.github.com> Date: Thu, 20 Oct 2022 06:20:37 -0500 Subject: [PATCH 03/44] algorithm: sieve (#1205) --- Maths/SieveOfEratosthenesIntArray.js | 22 +++++++++++++++++++ .../test/SieveOfEratosthenesIntArray.test.js | 12 ++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Maths/SieveOfEratosthenesIntArray.js create mode 100644 Maths/test/SieveOfEratosthenesIntArray.test.js diff --git a/Maths/SieveOfEratosthenesIntArray.js b/Maths/SieveOfEratosthenesIntArray.js new file mode 100644 index 000000000..7de49ffb7 --- /dev/null +++ b/Maths/SieveOfEratosthenesIntArray.js @@ -0,0 +1,22 @@ +/** + * Function to get all prime numbers below a given number + * This function returns an array of prime numbers + * @see {@link https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes} + */ + +function sieveOfEratosthenes (max) { + const sieve = [] + const primes = [] + + for (let i = 2; i <= max; ++i) { + if (!sieve[i]) { // If i has not been marked then it is prime + primes.push(i) + for (let j = i << 1; j <= max; j += i) { // Mark all multiples of i as non-prime + sieve[j] = true + } + } + } + return primes +} + +export { sieveOfEratosthenes } diff --git a/Maths/test/SieveOfEratosthenesIntArray.test.js b/Maths/test/SieveOfEratosthenesIntArray.test.js new file mode 100644 index 000000000..eeb6dd9d8 --- /dev/null +++ b/Maths/test/SieveOfEratosthenesIntArray.test.js @@ -0,0 +1,12 @@ +import { sieveOfEratosthenes } from '../SieveOfEratosthenesIntArray' +import { PrimeCheck } from '../PrimeCheck' + +describe('should return an array of prime numbers', () => { + it('should have each element in the array as a prime numbers', () => { + const n = 100 + const primes = sieveOfEratosthenes(n) + primes.forEach(prime => { + expect(PrimeCheck(prime)).toBeTruthy() + }) + }) +}) From b07529fb6f5c38adb063efe9e3a7bbef1af7bc98 Mon Sep 17 00:00:00 2001 From: Omkarnath Parida Date: Thu, 20 Oct 2022 17:05:30 +0530 Subject: [PATCH 04/44] tests: Project Euler Problem 3 (#1207) --- Project-Euler/test/Problem003.test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Project-Euler/test/Problem003.test.js diff --git a/Project-Euler/test/Problem003.test.js b/Project-Euler/test/Problem003.test.js new file mode 100644 index 000000000..0f6cf4e6d --- /dev/null +++ b/Project-Euler/test/Problem003.test.js @@ -0,0 +1,12 @@ +import { largestPrime } from '../Problem003.js' + +describe('Largest prime factor', () => { + test('if the number is 13195', () => { + expect(largestPrime(13195)).toBe(29) + }) + // Project Euler Condition Check + test('if the number is 600851475143', () => { + // Default value is same as the tested value + expect(largestPrime()).toBe(6857) + }) +}) From 636017ca51dad05339cde3b5c66cbe1d71957f7e Mon Sep 17 00:00:00 2001 From: SczSca <90069772+SczSca@users.noreply.github.com> Date: Thu, 20 Oct 2022 06:38:56 -0500 Subject: [PATCH 05/44] algorithm: reverse (#1197) --- Data-Structures/Array/Reverse.js | 17 +++++++++++++++++ Data-Structures/Array/test/Reverse.test.js | 13 +++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 Data-Structures/Array/Reverse.js create mode 100644 Data-Structures/Array/test/Reverse.test.js diff --git a/Data-Structures/Array/Reverse.js b/Data-Structures/Array/Reverse.js new file mode 100644 index 000000000..79a789c01 --- /dev/null +++ b/Data-Structures/Array/Reverse.js @@ -0,0 +1,17 @@ +/** https://www.geeksforgeeks.org/write-a-program-to-Reverse-an-array-or-string/ + * This function will accept an array and + * Reverse its elements and returns the inverted array + * @param {Array} arr array with elements of any data type + * @returns {Array} array with inverted elements + */ + +const Reverse = (arr) => { + // limit specifies the amount of Reverse actions + for (let i = 0, j = arr.length - 1; i < arr.length / 2; i++, j--) { + const temp = arr[i] + arr[i] = arr[j] + arr[j] = temp + } + return arr +} +export { Reverse } diff --git a/Data-Structures/Array/test/Reverse.test.js b/Data-Structures/Array/test/Reverse.test.js new file mode 100644 index 000000000..bf137fe65 --- /dev/null +++ b/Data-Structures/Array/test/Reverse.test.js @@ -0,0 +1,13 @@ +import { Reverse } from '../Reverse.js' +import each from 'jest-each' + +describe('reverse elements in an array', () => { + each` + array | expected + ${[]} | ${[]} + ${[1]} | ${[1]} + ${[1, 2, 3, 4]} | ${[4, 3, 2, 1]} + `.test('returns $expected when given $array', ({ array, expected }) => { + expect(Reverse(array)).toEqual(expected) + }) +}) From 58671861a557d5944cd5d8877949dc13b0068c40 Mon Sep 17 00:00:00 2001 From: Changi Cho <38618187+changicho@users.noreply.github.com> Date: Thu, 20 Oct 2022 20:39:37 +0900 Subject: [PATCH 06/44] algorithm: SegmentTree (#1178) --- Data-Structures/Tree/SegmentTree.js | 97 +++++++++++++++++++ Data-Structures/Tree/test/SegmentTree.test.js | 16 +++ 2 files changed, 113 insertions(+) create mode 100644 Data-Structures/Tree/SegmentTree.js create mode 100644 Data-Structures/Tree/test/SegmentTree.test.js diff --git a/Data-Structures/Tree/SegmentTree.js b/Data-Structures/Tree/SegmentTree.js new file mode 100644 index 000000000..3778bb1e3 --- /dev/null +++ b/Data-Structures/Tree/SegmentTree.js @@ -0,0 +1,97 @@ +/** + * Segment Tree + * concept : [Wikipedia](https://en.wikipedia.org/wiki/Segment_tree) + * inspired by : https://www.geeksforgeeks.org/segment-tree-efficient-implementation/ + * + * time complexity + * - init : O(N) + * - update : O(log(N)) + * - query : O(log(N)) + * + * space complexity : O(N) + */ +class SegmentTree { + size + tree + constructor (arr) { + // we define tree like this + // tree[1] : root node of tree + // tree[i] : i'th node + // tree[i * 2] : i'th left child + // tree[i * 2 + 1] : i'th right child + // and we use bit, shift operation for index + this.size = arr.length + this.tree = new Array(2 * arr.length) + this.tree.fill(0) + + this.build(arr) + } + + // function to build the tree + build (arr) { + const { size, tree } = this + // insert leaf nodes in tree + // leaf nodes will start from index N + // in this array and will go up to index (2 * N โ€“ 1) + for (let i = 0; i < size; i++) { + tree[size + i] = arr[i] + } + + // build the tree by calculating parents + // tree's root node will contain all leaf node's sum + for (let i = size - 1; i > 0; --i) { + // current node's value is the sum of left child, right child + tree[i] = tree[i * 2] + tree[i * 2 + 1] + } + } + + update (index, value) { + const { size, tree } = this + + // only update values in the parents of the given node being changed. + // to get the parent move to parent node (index / 2) + + // set value at position index + index += size + // tree[index] is leaf node and index's value of array + tree[index] = value + + // move upward and update parents + for (let i = index; i > 1; i >>= 1) { + // i ^ 1 turns (2 * i) to (2 * i + 1) + // i ^ 1 is second child + tree[i >> 1] = tree[i] + tree[i ^ 1] + } + } + + // interval [L,R) with left index(L) included and right (R) excluded. + query (left, right) { + const { size, tree } = this + // cause R is excluded, increase right for convenient + right++ + let res = 0 + + // loop to find the sum in the range + for (left += size, right += size; left < right; left >>= 1, right >>= 1) { + // L is the left border of an query interval + + // if L is odd it means that it is the right child of its parent and our interval includes only L and not the parent. + // So we will simply include this node to sum and move to the parent of its next node by doing L = (L + 1) / 2. + + // if L is even it is the left child of its parent + // and the interval includes its parent also unless the right borders interfere. + if ((left & 1) > 0) { + res += tree[left++] + } + + // same in R (the right border of an query interval) + if ((right & 1) > 0) { + res += tree[--right] + } + } + + return res + } +} + +export { SegmentTree } diff --git a/Data-Structures/Tree/test/SegmentTree.test.js b/Data-Structures/Tree/test/SegmentTree.test.js new file mode 100644 index 000000000..440fbd828 --- /dev/null +++ b/Data-Structures/Tree/test/SegmentTree.test.js @@ -0,0 +1,16 @@ +import { SegmentTree } from '../SegmentTree' + +describe('SegmentTree sum test', () => { + const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + + const segment = new SegmentTree(a) + + it('init sum check', () => { + expect(segment.query(0, 2)).toBe(6) + }) + + it('init sum check', () => { + segment.update(2, 1) + expect(segment.query(0, 2)).toBe(4) + }) +}) From 73bf91d7e153b07bade0543203c1d452affd8251 Mon Sep 17 00:00:00 2001 From: Prashal Ruchiranga <98536996+prashalruchiranga@users.noreply.github.com> Date: Thu, 20 Oct 2022 17:10:22 +0530 Subject: [PATCH 07/44] =?UTF-8?q?Add=20an=20algorithm=20to=20find=20Taylor?= =?UTF-8?q?=20series=20approximation=20of=20exponential=20f=E2=80=A6=20(#1?= =?UTF-8?q?160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Maths/ExponentialFunction.js | 25 +++++++++++++++++++++++++ Maths/test/ExponentialFunction.test.js | 16 ++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 Maths/ExponentialFunction.js create mode 100644 Maths/test/ExponentialFunction.test.js diff --git a/Maths/ExponentialFunction.js b/Maths/ExponentialFunction.js new file mode 100644 index 000000000..421269381 --- /dev/null +++ b/Maths/ExponentialFunction.js @@ -0,0 +1,25 @@ +/** + * @function exponentialFunction + * @description Calculates the n+1 th order Taylor series approximation of exponential function e^x given n + * @param {Integer} power + * @param {Integer} order - 1 + * @returns exponentialFunction(2,20) = 7.3890560989301735 + * @url https://en.wikipedia.org/wiki/Exponential_function + */ +function exponentialFunction (power, n) { + let output = 0 + let fac = 1 + if (isNaN(power) || isNaN(n) || n < 0) { + throw new TypeError('Invalid Input') + } + if (n === 0) { return 1 } + for (let i = 0; i < n; i++) { + output += (power ** i) / fac + fac *= (i + 1) + } + return output +} + +export { + exponentialFunction +} diff --git a/Maths/test/ExponentialFunction.test.js b/Maths/test/ExponentialFunction.test.js new file mode 100644 index 000000000..d1eed4895 --- /dev/null +++ b/Maths/test/ExponentialFunction.test.js @@ -0,0 +1,16 @@ +import { exponentialFunction } from '../ExponentialFunction' + +describe('Tests for exponential function', () => { + it('should be a function', () => { + expect(typeof exponentialFunction).toEqual('function') + }) + + it('should throw error for invalid input', () => { + expect(() => exponentialFunction(2, -34)).toThrow() + }) + + it('should return the exponential function of power of 5 and order of 21', () => { + const ex = exponentialFunction(5, 20) + expect(ex).toBe(148.4131078683383) + }) +}) From 16fa774012c70282cd2bd3b824b42823febd55a9 Mon Sep 17 00:00:00 2001 From: Roland Hummel Date: Thu, 20 Oct 2022 15:59:09 +0200 Subject: [PATCH 08/44] chore: only test changed packages (#1194) --- .github/workflows/Ci.yml | 10 ++++++++-- .husky/pre-commit | 2 +- CONTRIBUTING.md | 11 +++++++++-- Project-Euler/test/Problem044.test.js | 5 +++-- package-lock.json | 2 +- package.json | 3 ++- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/Ci.yml b/.github/workflows/Ci.yml index 8d4120314..a783e0bb9 100644 --- a/.github/workflows/Ci.yml +++ b/.github/workflows/Ci.yml @@ -12,6 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: node-version: 16 @@ -20,8 +21,13 @@ jobs: - name: ๐Ÿ“ฆ Install dependencies run: npm ci - - name: ๐Ÿงช Run tests - run: npm test + - name: ๐Ÿงช Run all tests + if: ${{ github.event_name == 'push' }} + run: npm run test + + - name: ๐Ÿงช Run tests for changed files only + if: ${{ github.event_name == 'pull_request' }} + run: npm run test-changed - name: ๐Ÿ’„ Code style run: npm run style diff --git a/.husky/pre-commit b/.husky/pre-commit index 35ea7e3c0..9b70cd595 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,4 +2,4 @@ . "$(dirname "$0")/_/husky.sh" npm run style -npm run test +npm run test-changed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7386f9dca..8415cfe5e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,7 +63,8 @@ should add unique value. #### Module System -We use the [ES Module](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) system, which bring an official, standardized module system to JavaScript. +We use the [ES Module](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) system, which bring an +official, standardized module system to JavaScript. It roughly means you will need to use `export` and `import` statements instead of `module.exports` and `require()`. @@ -71,7 +72,7 @@ It roughly means you will need to use `export` and `import` statements instead o Be confident that your code works. When was the last time you committed a code change, your build failed, and half of your app stopped working? Mine was last week. Writing tests for our Algorithms will help us ensure the implementations -are air tight even after multiple fixes and code changes. +are airtight even after multiple fixes and code changes. We use [Jest](https://jestjs.io/) to run unit tests on our algorithms. It provides a very readable and expressive way to structure your test code. @@ -107,6 +108,12 @@ You can also start Jest in "watch" mode: npm test -- --watchAll ``` +We also prepared a helper script that runs tests only for changed files: + +```shell +npm run test-changed +``` + This will run all tests and watch source and test files for changes. When a change is made, the tests will run again. #### Coding Style diff --git a/Project-Euler/test/Problem044.test.js b/Project-Euler/test/Problem044.test.js index bf6cccd60..b542b3db6 100644 --- a/Project-Euler/test/Problem044.test.js +++ b/Project-Euler/test/Problem044.test.js @@ -1,10 +1,10 @@ import { problem44 } from '../Problem044.js' describe('checking nth prime number', () => { - it('should be invalid input if number is negative', () => { + test('should be invalid input if number is negative', () => { expect(() => problem44(-3)).toThrowError('Invalid Input') }) - it('should be invalid input if number is 0', () => { + test('should be invalid input if number is 0', () => { expect(() => problem44(0)).toThrowError('Invalid Input') }) // Project Euler Condition Check @@ -12,6 +12,7 @@ describe('checking nth prime number', () => { expect(problem44(1)).toBe(5482660) }) // Project Euler Second Value for Condition Check + // FIXME skip this test for now because it runs very long and clogs up the CI & pre-commit hook test('if the number is greater or equal to 2167', () => { expect(problem44(2167)).toBe(8476206790) }) diff --git a/package-lock.json b/package-lock.json index aee3c0188..53a83cf06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "standard": "^17.0.0" }, "engines": { - "node": ">=18" + "node": ">=16.6.0" } }, "node_modules/@ampproject/remapping": { diff --git a/package.json b/package.json index 2fdd75f06..6ab94ac09 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "type": "module", "description": "A repository for All algorithms implemented in Javascript (for educational purposes only)", "scripts": { - "test": "jest --no-cache", + "test": "jest", + "test-changed": "jest --onlyChanged", "style": "standard", "prepare": "husky install" }, From 21d73b64471282275640f07d0e7185897a464a0b Mon Sep 17 00:00:00 2001 From: Hitesh Gupta <72217750+hiitesh1127@users.noreply.github.com> Date: Thu, 20 Oct 2022 10:59:36 -0400 Subject: [PATCH 09/44] algorithm: unique paths (#1211) * dp problem * update Directory.md * suggested changes --- DIRECTORY.md | 1 + Dynamic-Programming/UniquePaths.js | 41 +++++++++++++++++++ Dynamic-Programming/tests/UniquePaths.test.js | 11 +++++ 3 files changed, 53 insertions(+) create mode 100644 Dynamic-Programming/UniquePaths.js create mode 100644 Dynamic-Programming/tests/UniquePaths.test.js diff --git a/DIRECTORY.md b/DIRECTORY.md index e1bbd7700..b5cf0077a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -105,6 +105,7 @@ * [RodCutting](Dynamic-Programming/RodCutting.js) * [Shuf](Dynamic-Programming/Shuf.js) * [SieveOfEratosthenes](Dynamic-Programming/SieveOfEratosthenes.js) + * [UniquePaths](Dynamic-Programming/UniquePaths.js) * **Sliding-Window** * [LongestSubstringWithoutRepeatingCharacters](Dynamic-Programming/Sliding-Window/LongestSubstringWithoutRepeatingCharacters.js) * [PermutationinString](Dynamic-Programming/Sliding-Window/PermutationinString.js) diff --git a/Dynamic-Programming/UniquePaths.js b/Dynamic-Programming/UniquePaths.js new file mode 100644 index 000000000..4b3d67a1a --- /dev/null +++ b/Dynamic-Programming/UniquePaths.js @@ -0,0 +1,41 @@ + +/* + * + * Unique Paths + * + * There is a robot on an `m x n` grid. + * The robot is initially located at the top-left corner. + * The robot tries to move to the bottom-right corner. + * The robot can only move either down or right at any point in time. + * + * Given the two integers `m` and `n`, + * return the number of possible unique paths that the robot can take to reach the bottom-right corner. + * More info: https://leetcode.com/problems/unique-paths/ + */ + +/* + * @param {number} m + * @param {number} n + * @return {number} + */ + +const uniquePaths = (m, n) => { + // only one way to reach end + if (m === 1 || n === 1) return 1 + + // build a linear grid of size m + // base case, position 1 has only 1 move + const paths = new Array(m).fill(1) + + for (let i = 1; i < n; i++) { + for (let j = 1; j < m; j++) { + // paths[j] in RHS represents the cell value stored above the current cell + // paths[j-1] in RHS represents the cell value stored to the left of the current cell + // paths [j] on the LHS represents the number of distinct pathways to the cell (i,j) + paths[j] = paths[j - 1] + paths[j] + } + } + return paths[m - 1] +} + +export { uniquePaths } diff --git a/Dynamic-Programming/tests/UniquePaths.test.js b/Dynamic-Programming/tests/UniquePaths.test.js new file mode 100644 index 000000000..eb6f8c74d --- /dev/null +++ b/Dynamic-Programming/tests/UniquePaths.test.js @@ -0,0 +1,11 @@ +import { uniquePaths } from '../UniquePaths' + +describe('Unique Paths', () => { + it('should return 28 when m is 3 and n is 7', () => { + expect(uniquePaths(3, 7)).toBe(28) + }) + + it('should return 48620 when m is 10 and n is 10', () => { + expect(uniquePaths(10, 10)).toBe(48620) + }) +}) From cf482c4eef1a416397fb0e2b06940ae9baf4b22f Mon Sep 17 00:00:00 2001 From: Hitesh Gupta <72217750+hiitesh1127@users.noreply.github.com> Date: Thu, 20 Oct 2022 11:00:43 -0400 Subject: [PATCH 10/44] fix typo in FenwickTree.js (#1208) --- Trees/FenwickTree.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Trees/FenwickTree.js b/Trees/FenwickTree.js index df9406700..f3c65f56d 100644 --- a/Trees/FenwickTree.js +++ b/Trees/FenwickTree.js @@ -1,7 +1,7 @@ /* * Author: Mohit Kumar - * Fedwick Tree Implementation in JavaScript - * Fedwick Tree Implementation for finding prefix sum. + * Fenwick Tree Implementation in JavaScript + * Fenwick Tree Implementation for finding prefix sum. */ class FenwickTree { From 6f9a8e4b5af3556cdab8a2e25489fff6fdb66604 Mon Sep 17 00:00:00 2001 From: Hitesh Gupta <72217750+hiitesh1127@users.noreply.github.com> Date: Thu, 20 Oct 2022 11:03:06 -0400 Subject: [PATCH 11/44] added-ModularArithmetic-code (#1217) * added-ModularArithmetic-code * fix-typo * suggested changes --- Maths/ModularArithmetic.js | 56 ++++++++++++++++++++++++++++ Maths/test/ModularArithmetic.test.js | 45 ++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 Maths/ModularArithmetic.js create mode 100644 Maths/test/ModularArithmetic.test.js diff --git a/Maths/ModularArithmetic.js b/Maths/ModularArithmetic.js new file mode 100644 index 000000000..26c54ebbb --- /dev/null +++ b/Maths/ModularArithmetic.js @@ -0,0 +1,56 @@ +import { extendedEuclideanGCD } from './ExtendedEuclideanGCD' + +/** + * https://brilliant.org/wiki/modular-arithmetic/ + * @param {Number} arg1 first argument + * @param {Number} arg2 second argument + * @returns {Number} + */ + +export class ModRing { + constructor (MOD) { + this.MOD = MOD + } + + isInputValid = (arg1, arg2) => { + if (!this.MOD) { + throw new Error('Modulus must be initialized in the object constructor') + } + if (typeof arg1 !== 'number' || typeof arg2 !== 'number') { + throw new TypeError('Input must be Numbers') + } + } + /** + * Modulus is Distributive property, + * As a result, we separate it into numbers in order to keep it within MOD's range + */ + + add = (arg1, arg2) => { + this.isInputValid(arg1, arg2) + return ((arg1 % this.MOD) + (arg2 % this.MOD)) % this.MOD + } + + subtract = (arg1, arg2) => { + this.isInputValid(arg1, arg2) + // An extra MOD is added to check negative results + return ((arg1 % this.MOD) - (arg2 % this.MOD) + this.MOD) % this.MOD + } + + multiply = (arg1, arg2) => { + this.isInputValid(arg1, arg2) + return ((arg1 % this.MOD) * (arg2 % this.MOD)) % this.MOD + } + + /** + * + * It is not Possible to find Division directly like the above methods, + * So we have to use the Extended Euclidean Theorem for finding Multiplicative Inverse + * https://github.com/TheAlgorithms/JavaScript/blob/master/Maths/ExtendedEuclideanGCD.js + */ + + divide = (arg1, arg2) => { + // 1st Index contains the required result + // The theorem may have return Negative value, we need to add MOD to make it Positive + return (extendedEuclideanGCD(arg1, arg2)[1] + this.MOD) % this.MOD + } +} diff --git a/Maths/test/ModularArithmetic.test.js b/Maths/test/ModularArithmetic.test.js new file mode 100644 index 000000000..af42e243d --- /dev/null +++ b/Maths/test/ModularArithmetic.test.js @@ -0,0 +1,45 @@ +import { ModRing } from '../ModularArithmetic' + +describe('Modular Arithmetic', () => { + const MOD = 10000007 + let ring + beforeEach(() => { + ring = new ModRing(MOD) + }) + + describe('add', () => { + it('Should return 9999993 for 10000000 and 10000000', () => { + expect(ring.add(10000000, 10000000)).toBe(9999993) + }) + it('Should return 9999986 for 10000000 and 20000000', () => { + expect(ring.add(10000000, 20000000)).toBe(9999986) + }) + }) + + describe('subtract', () => { + it('Should return 1000000 for 10000000 and 9000000', () => { + expect(ring.subtract(10000000, 9000000)).toBe(1000000) + }) + it('Should return 7 for 10000000 and 20000000', () => { + expect(ring.subtract(10000000, 20000000)).toBe(7) + }) + }) + + describe('multiply', () => { + it('Should return 1000000 for 100000 and 10000', () => { + expect(ring.multiply(100000, 10000)).toBe(9999307) + }) + it('Should return 7 for 100000 and 10000100', () => { + expect(ring.multiply(10000000, 20000000)).toBe(98) + }) + }) + + describe('divide', () => { + it('Should return 4 for 3 and 11', () => { + expect(ring.divide(3, 11)).toBe(4) + }) + it('Should return 2 for 18 and 7', () => { + expect(ring.divide(18, 7)).toBe(2) + }) + }) +}) From ce9e2946be2291b1192ea545ea2e3881b0e879cd Mon Sep 17 00:00:00 2001 From: Adrito Mukherjee <98008131+Adrito-M@users.noreply.github.com> Date: Thu, 20 Oct 2022 20:35:24 +0530 Subject: [PATCH 12/44] algorithm: kosaraju (#1215) * kosaraju test added * Fixes: #1214 * Fixes: #1214 * Update package-lock.json * Kosaraju.js exports function kosaraju rather than class --- Graphs/Kosaraju.js | 100 +++++++++++++++++++++++++++++++++++ Graphs/test/Kosaraju.test.js | 30 +++++++++++ 2 files changed, 130 insertions(+) create mode 100644 Graphs/Kosaraju.js create mode 100644 Graphs/test/Kosaraju.test.js diff --git a/Graphs/Kosaraju.js b/Graphs/Kosaraju.js new file mode 100644 index 000000000..a0b4266b0 --- /dev/null +++ b/Graphs/Kosaraju.js @@ -0,0 +1,100 @@ +/** + * Author: Adrito Mukherjee + * Kosaraju's Algorithm implementation in Javascript + * Kosaraju's Algorithm finds all the connected components in a Directed Acyclic Graph (DAG) + * It uses Stack data structure to store the Topological Sorted Order of vertices and also Graph data structure + * + * Wikipedia: https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm + * + */ + +class Kosaraju { + constructor (graph) { + this.connections = {} + this.reverseConnections = {} + this.stronglyConnectedComponents = [] + for (const [i, j] of graph) { + this.addEdge(i, j) + } + this.topoSort() + return this.kosaraju() + } + + addNode (node) { + // Function to add a node to the graph (connection represented by set) + this.connections[node] = new Set() + this.reverseConnections[node] = new Set() + this.topoSorted = [] + } + + addEdge (node1, node2) { + // Function to add an edge (adds the node too if they are not present in the graph) + if (!(node1 in this.connections) || !(node1 in this.reverseConnections)) { + this.addNode(node1) + } + if (!(node2 in this.connections) || !(node2 in this.reverseConnections)) { + this.addNode(node2) + } + this.connections[node1].add(node2) + this.reverseConnections[node2].add(node1) + } + + dfsTopoSort (node, visited) { + visited.add(node) + for (const child of this.connections[node]) { + if (!visited.has(child)) this.dfsTopoSort(child, visited) + } + this.topoSorted.push(node) + } + + topoSort () { + // Function to perform topological sorting + const visited = new Set() + const nodes = Object.keys(this.connections).map((key) => Number(key)) + for (const node of nodes) { + if (!visited.has(node)) this.dfsTopoSort(node, visited) + } + } + + dfsKosaraju (node, visited) { + visited.add(node) + this.stronglyConnectedComponents[ + this.stronglyConnectedComponents.length - 1 + ].push(node) + for (const child of this.reverseConnections[node]) { + if (!visited.has(child)) this.dfsKosaraju(child, visited) + } + } + + kosaraju () { + // Function to perform Kosaraju Algorithm + const visited = new Set() + while (this.topoSorted.length > 0) { + const node = this.topoSorted.pop() + if (!visited.has(node)) { + this.stronglyConnectedComponents.push([]) + this.dfsKosaraju(node, visited) + } + } + return this.stronglyConnectedComponents + } +} + +function kosaraju (graph) { + const stronglyConnectedComponents = new Kosaraju(graph) + return stronglyConnectedComponents +} + +export { kosaraju } + +// kosaraju([ +// [1, 2], +// [2, 3], +// [3, 1], +// [2, 4], +// [4, 5], +// [5, 6], +// [6, 4], +// ]) + +// [ [ 1, 3, 2 ], [ 4, 6, 5 ] ] diff --git a/Graphs/test/Kosaraju.test.js b/Graphs/test/Kosaraju.test.js new file mode 100644 index 000000000..a2da394db --- /dev/null +++ b/Graphs/test/Kosaraju.test.js @@ -0,0 +1,30 @@ +import { kosaraju } from '../Kosaraju.js' + +test('Test Case 1', () => { + const graph = [ + [1, 2], + [2, 3], + [3, 1], + [2, 4], + [4, 5], + [5, 6], + [6, 4] + ] + const stronglyConnectedComponents = kosaraju(graph) + expect(stronglyConnectedComponents).toStrictEqual([ + [1, 3, 2], + [4, 6, 5] + ]) +}) + +test('Test Case 2', () => { + const graph = [ + [1, 2], + [2, 3], + [3, 1], + [2, 4], + [4, 5] + ] + const stronglyConnectedComponents = kosaraju(graph) + expect(stronglyConnectedComponents).toStrictEqual([[1, 3, 2], [4], [5]]) +}) From 63a3394d95852b261a9cfdd43475b7c3a8f470ab Mon Sep 17 00:00:00 2001 From: Hitesh Gupta <72217750+hiitesh1127@users.noreply.github.com> Date: Fri, 21 Oct 2022 07:24:37 -0400 Subject: [PATCH 13/44] algorithm: letter combinations (#1209) --- DIRECTORY.md | 1 + Recursive/LetterCombination.js | 53 ++++++++++++++++++++++++ Recursive/test/LetterCombination.test.js | 48 +++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 Recursive/LetterCombination.js create mode 100644 Recursive/test/LetterCombination.test.js diff --git a/DIRECTORY.md b/DIRECTORY.md index b5cf0077a..a32addb9d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -252,6 +252,7 @@ * [FibonacciNumberRecursive](Recursive/FibonacciNumberRecursive.js) * [FloodFill](Recursive/FloodFill.js) * [KochSnowflake](Recursive/KochSnowflake.js) + * [LetterCombination](Recursive/LetterCombination.js) * [Palindrome](Recursive/Palindrome.js) * [SubsequenceRecursive](Recursive/SubsequenceRecursive.js) * [TowerOfHanoi](Recursive/TowerOfHanoi.js) diff --git a/Recursive/LetterCombination.js b/Recursive/LetterCombination.js new file mode 100644 index 000000000..5131ebd85 --- /dev/null +++ b/Recursive/LetterCombination.js @@ -0,0 +1,53 @@ +/* + * + * Letter Combinations of a Phone Number + * + * Given a string containing digits from 2-9 inclusive, + * return all possible letter combinations that the number could represent. + * Return the answer in any order. + + * A mapping of digits to letters (just like on the telephone buttons) is given below. + * Note that 1 does not map to any letters. + * More info: https://leetcode.com/problems/letter-combinations-of-a-phone-number/ + */ + +/* + * @param {string} digits + * @returns {string[]} all the possible combinations + */ + +const letterCombinations = (digits) => { + const length = digits?.length + const result = [] + if (!length) { + return result + } + const digitMap = { + 2: 'abc', + 3: 'def', + 4: 'ghi', + 5: 'jkl', + 6: 'mno', + 7: 'pqrs', + 8: 'tuv', + 9: 'wxyz' + } + + const combinations = (index, combination) => { + let letter + let letterIndex + if (index >= length) { + result.push(combination) + return + } + const digit = digitMap[digits[index]] + letterIndex = 0 + while ((letter = digit[letterIndex++])) { + combinations(index + 1, combination + letter) + } + } + combinations(0, '') + return result +} + +export { letterCombinations } diff --git a/Recursive/test/LetterCombination.test.js b/Recursive/test/LetterCombination.test.js new file mode 100644 index 000000000..29631b8f4 --- /dev/null +++ b/Recursive/test/LetterCombination.test.js @@ -0,0 +1,48 @@ +import { letterCombinations } from '../LetterCombination' + +describe('Letter Combinations', () => { + it('should return empty array if provided string is not valid', () => { + const result = letterCombinations('') + expect(Array.isArray(result)).toBe(true) + expect(result.length).toBe(0) + }) + + it('should return empty array if provided string is empty', () => { + const result = letterCombinations(null) + expect(Array.isArray(result)).toBe(true) + expect(result.length).toBe(0) + }) + + it('should return letter combination of 234', () => { + const result = letterCombinations('234') + expect(result).toEqual([ + 'adg', + 'adh', + 'adi', + 'aeg', + 'aeh', + 'aei', + 'afg', + 'afh', + 'afi', + 'bdg', + 'bdh', + 'bdi', + 'beg', + 'beh', + 'bei', + 'bfg', + 'bfh', + 'bfi', + 'cdg', + 'cdh', + 'cdi', + 'ceg', + 'ceh', + 'cei', + 'cfg', + 'cfh', + 'cfi' + ]) + }) +}) From 8c27d86afccc691eb0baed6f932e55586a14d05f Mon Sep 17 00:00:00 2001 From: Psytew Date: Sun, 23 Oct 2022 03:19:19 -0400 Subject: [PATCH 14/44] enhancement: FindLCM algorithm (#1222) Co-authored-by: patrickwestervelt --- Maths/FindLcm.js | 23 ++++++++++++++++++++--- Maths/test/FindLcm.test.js | 29 ++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/Maths/FindLcm.js b/Maths/FindLcm.js index 101da8565..c20c867a4 100644 --- a/Maths/FindLcm.js +++ b/Maths/FindLcm.js @@ -11,16 +11,18 @@ 'use strict' +import { findHCF } from './findHcf' + // Find the LCM of two numbers. const findLcm = (num1, num2) => { // If the input numbers are less than 1 return an error message. if (num1 < 1 || num2 < 1) { - return 'Please enter values greater than zero.' + throw Error('Numbers must be positive.') } // If the input numbers are not integers return an error message. if (num1 !== Math.round(num1) || num2 !== Math.round(num2)) { - return 'Please enter whole numbers.' + throw Error('Numbers must be whole.') } // Get the larger number between the two @@ -33,4 +35,19 @@ const findLcm = (num1, num2) => { } } -export { findLcm } +// Typically, but not always, more efficient +const findLcmWithHcf = (num1, num2) => { + // If the input numbers are less than 1 return an error message. + if (num1 < 1 || num2 < 1) { + throw Error('Numbers must be positive.') + } + + // If the input numbers are not integers return an error message. + if (num1 !== Math.round(num1) || num2 !== Math.round(num2)) { + throw Error('Numbers must be whole.') + } + + return num1 * num2 / findHCF(num1, num2) +} + +export { findLcm, findLcmWithHcf } diff --git a/Maths/test/FindLcm.test.js b/Maths/test/FindLcm.test.js index 1e5c0905c..0a744cf5a 100644 --- a/Maths/test/FindLcm.test.js +++ b/Maths/test/FindLcm.test.js @@ -1,20 +1,39 @@ -import { findLcm } from '../FindLcm' +import { findLcm, findLcmWithHcf } from '../FindLcm' describe('findLcm', () => { it('should throw a statement for values less than 1', () => { - expect(findLcm(0, 0)).toBe('Please enter values greater than zero.') + expect(() => { findLcm(0, 0) }).toThrow(Error) }) it('should throw a statement for one value less than 1', () => { - expect(findLcm(1, 0)).toBe('Please enter values greater than zero.') - expect(findLcm(0, 1)).toBe('Please enter values greater than zero.') + expect(() => { findLcm(1, 0) }).toThrow(Error) + expect(() => { findLcm(0, 1) }).toThrow(Error) }) it('should return an error for values non-integer values', () => { - expect(findLcm(4.564, 7.39)).toBe('Please enter whole numbers.') + expect(() => { findLcm(4.564, 7.39) }).toThrow(Error) }) it('should return the LCM of two given integers', () => { expect(findLcm(27, 36)).toBe(108) }) }) + +describe('findLcmWithHcf', () => { + it('should throw a statement for values less than 1', () => { + expect(() => { findLcmWithHcf(0, 0) }).toThrow(Error) + }) + + it('should throw a statement for one value less than 1', () => { + expect(() => { findLcmWithHcf(1, 0) }).toThrow(Error) + expect(() => { findLcmWithHcf(0, 1) }).toThrow(Error) + }) + + it('should return an error for values non-integer values', () => { + expect(() => { findLcmWithHcf(4.564, 7.39) }).toThrow(Error) + }) + + it('should return the LCM of two given integers', () => { + expect(findLcmWithHcf(27, 36)).toBe(108) + }) +}) From 72ee63ca0fe606b2c745e79e9ba98ce5d14309d3 Mon Sep 17 00:00:00 2001 From: Psytew Date: Mon, 24 Oct 2022 07:54:45 -0400 Subject: [PATCH 15/44] fix: build error due to FindLcm (#1223) Co-authored-by: patrickwestervelt --- Maths/FindLcm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Maths/FindLcm.js b/Maths/FindLcm.js index c20c867a4..e75a89fd7 100644 --- a/Maths/FindLcm.js +++ b/Maths/FindLcm.js @@ -11,7 +11,7 @@ 'use strict' -import { findHCF } from './findHcf' +import { findHCF } from './FindHcf' // Find the LCM of two numbers. const findLcm = (num1, num2) => { From 0529e194267d069374329594d5ba5e221ab53b95 Mon Sep 17 00:00:00 2001 From: ddaniel27 <67126972+ddaniel27@users.noreply.github.com> Date: Mon, 24 Oct 2022 07:00:33 -0500 Subject: [PATCH 16/44] solution: Project Euler 35 (#1201) * [CREATE] Problem 28 solution for Project Euler * [UPDATE] Added an explanation for the formula used in the algorithm * [CREATE] Added Problem 35 for Project-Euler * [UPDATE] Little typo in the error string * [UPDATE] Some algorithm changes * [UPDATE] Fix test string * [UPDATE] Change prime numbers generator to import a standard sieve algorithm. * [UPDATE] Change sieve algorithm implementation and now the solution works well. Also added some optimizations --- Project-Euler/Problem035.js | 34 +++++++++++++++++++++++++++ Project-Euler/test/Problem035.test.js | 18 ++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 Project-Euler/Problem035.js create mode 100644 Project-Euler/test/Problem035.test.js diff --git a/Project-Euler/Problem035.js b/Project-Euler/Problem035.js new file mode 100644 index 000000000..b62a8f031 --- /dev/null +++ b/Project-Euler/Problem035.js @@ -0,0 +1,34 @@ +/** + * Problem 35 - Circular primes + * + * @see {@link https://projecteuler.net/problem=35} + * + * The number, 197, is called a circular prime because all rotations of the digits: 197, 971, and 719, are themselves prime. + * There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, and 97. + * How many circular primes are there below one million? + * + * @author ddaniel27 + */ +import { sieveOfEratosthenes } from '../Maths/SieveOfEratosthenesIntArray' + +function problem35 (n) { + if (n < 2) { + throw new Error('Invalid input') + } + const list = sieveOfEratosthenes(n).filter(prime => !prime.toString().match(/[024568]/)) // Get a list of primes without 0, 2, 4, 5, 6, 8 + + const result = list.filter((number, _idx, arr) => { + const str = String(number) + for (let i = 0; i < str.length; i++) { // Get all rotations of the number + const rotation = str.slice(i) + str.slice(0, i) + if (!arr.includes(Number(rotation))) { // Check if the rotation is prime + return false + } + } + return true // If all rotations are prime, then the number is circular prime + }) + + return result.length + 1 // Add 2 to the result because 2 is a circular prime +} + +export { problem35 } diff --git a/Project-Euler/test/Problem035.test.js b/Project-Euler/test/Problem035.test.js new file mode 100644 index 000000000..ebaa4ac46 --- /dev/null +++ b/Project-Euler/test/Problem035.test.js @@ -0,0 +1,18 @@ +import { problem35 } from '../Problem035.js' + +describe('checking circular primes', () => { + it('should be invalid input if number is negative', () => { + expect(() => problem35(-3)).toThrowError('Invalid input') + }) + it('should be invalid input if number is 0', () => { + expect(() => problem35(0)).toThrowError('Invalid input') + }) + // Project Euler Condition Check + test('if the number is equal to 100 result should be 13', () => { + expect(problem35(100)).toBe(13) + }) + // Project Euler Challenge Check + test('if the number is equal to one million result should be 55', () => { + expect(problem35(1000000)).toBe(55) + }) +}) From b24f1f61cb8f0e2b136c0432208cad84c8b23bb3 Mon Sep 17 00:00:00 2001 From: Eddie Nuno Date: Wed, 26 Oct 2022 23:11:39 -0700 Subject: [PATCH 17/44] chore: use internal queue definition in BFS (#1228) --- Graphs/BreadthFirstSearch.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Graphs/BreadthFirstSearch.js b/Graphs/BreadthFirstSearch.js index 1bee2e3cd..abac118ac 100644 --- a/Graphs/BreadthFirstSearch.js +++ b/Graphs/BreadthFirstSearch.js @@ -1,3 +1,5 @@ +import Queue from '../Data-Structures/Queue/Queue' + /** * Breadth-first search is an algorithm for traversing a graph. * @@ -12,11 +14,12 @@ export function breadthFirstSearch (graph, startingNode) { const visited = new Set() // queue contains the nodes to be explored in the future - const queue = [startingNode] + const queue = new Queue() + queue.enqueue(startingNode) - while (queue.length > 0) { + while (!queue.isEmpty()) { // start with the queue's first node - const node = queue.shift() + const node = queue.dequeue() if (!visited.has(node)) { // mark the node as visited @@ -25,7 +28,7 @@ export function breadthFirstSearch (graph, startingNode) { // put all its neighbors into the queue for (let i = 0; i < neighbors.length; i++) { - queue.push(neighbors[i]) + queue.enqueue(neighbors[i]) } } } From 945657a98f9efb951a76c74276257726dbc8a4bf Mon Sep 17 00:00:00 2001 From: Eddie Nuno Date: Thu, 27 Oct 2022 06:15:33 -0700 Subject: [PATCH 18/44] chore: use internal queue definition in BFS Shortest Path (#1230) --- Graphs/BreadthFirstShortestPath.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Graphs/BreadthFirstShortestPath.js b/Graphs/BreadthFirstShortestPath.js index 39a23c75b..0e29b6440 100644 --- a/Graphs/BreadthFirstShortestPath.js +++ b/Graphs/BreadthFirstShortestPath.js @@ -1,3 +1,4 @@ +import Queue from '../Data-Structures/Queue/Queue' /** * Breadth-first approach can be applied to determine the shortest path between two nodes in an equi-weighted graph. * @@ -18,11 +19,12 @@ export function breadthFirstShortestPath (graph, startNode, targetNode) { // queue contains the paths to be explored in the future const initialPath = [startNode] - const queue = [initialPath] + const queue = new Queue() + queue.enqueue(initialPath) - while (queue.length > 0) { + while (!queue.isEmpty()) { // start with the queue's first path - const path = queue.shift() + const path = queue.dequeue() const node = path[path.length - 1] // explore this node if it hasn't been visited yet @@ -42,7 +44,7 @@ export function breadthFirstShortestPath (graph, startNode, targetNode) { } // queue the new path - queue.push(newPath) + queue.enqueue(newPath) } } } From d9d085faa70ceea1630e7e7eab66bbcb473ad5e0 Mon Sep 17 00:00:00 2001 From: Adrito Mukherjee <98008131+Adrito-M@users.noreply.github.com> Date: Thu, 27 Oct 2022 19:26:13 +0530 Subject: [PATCH 19/44] algorithm: binary lifting (#1218) * Algorithm: BinaryLifting * Update BinaryLifting.js * made the requested changes * added more comments --- Graphs/BinaryLifting.js | 82 +++++++++++++++++++++++++++++++ Graphs/test/BinaryLifting.test.js | 82 +++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 Graphs/BinaryLifting.js create mode 100644 Graphs/test/BinaryLifting.test.js diff --git a/Graphs/BinaryLifting.js b/Graphs/BinaryLifting.js new file mode 100644 index 000000000..594a5b667 --- /dev/null +++ b/Graphs/BinaryLifting.js @@ -0,0 +1,82 @@ +/** + * Author: Adrito Mukherjee + * Binary Lifting implementation in Javascript + * Binary Lifting is a technique that is used to find the kth ancestor of a node in a rooted tree with N nodes + * The technique requires preprocessing the tree in O(N log N) using dynamic programming + * The techniqe can answer Q queries about kth ancestor of any node in O(Q log N) + * It is faster than the naive algorithm that answers Q queries with complexity O(Q K) + * It can be used to find Lowest Common Ancestor of two nodes in O(log N) + * Tutorial on Binary Lifting: https://codeforces.com/blog/entry/100826 + */ + +class BinaryLifting { + constructor (root, tree) { + this.root = root + this.connections = new Map() + this.up = new Map() // up[node][i] stores the 2^i-th parent of node + for (const [i, j] of tree) { + this.addEdge(i, j) + } + this.log = Math.ceil(Math.log2(this.connections.size)) + this.dfs(root, root) + } + + addNode (node) { + // Function to add a node to the tree (connection represented by set) + this.connections.set(node, new Set()) + } + + addEdge (node1, node2) { + // Function to add an edge (adds the node too if they are not present in the tree) + if (!this.connections.has(node1)) { + this.addNode(node1) + } + if (!this.connections.has(node2)) { + this.addNode(node2) + } + this.connections.get(node1).add(node2) + this.connections.get(node2).add(node1) + } + + dfs (node, parent) { + // The dfs function calculates 2^i-th ancestor of all nodes for i ranging from 0 to this.log + // We make use of the fact the two consecutive jumps of length 2^(i-1) make the total jump length 2^i + this.up.set(node, new Map()) + this.up.get(node).set(0, parent) + for (let i = 1; i < this.log; i++) { + this.up + .get(node) + .set(i, this.up.get(this.up.get(node).get(i - 1)).get(i - 1)) + } + for (const child of this.connections.get(node)) { + if (child !== parent) this.dfs(child, node) + } + } + + kthAncestor (node, k) { + // if value of k is more than or equal to the number of total nodes, we return the root of the graph + if (k >= this.connections.size) { + return this.root + } + // if i-th bit is set in the binary representation of k, we jump from a node to its 2^i-th ancestor + // so after checking all bits of k, we will have made jumps of total length k, in just log k steps + for (let i = 0; i < this.log; i++) { + if (k & (1 << i)) { + node = this.up.get(node).get(i) + } + } + return node + } +} + +function binaryLifting (root, tree, queries) { + const graphObject = new BinaryLifting(root, tree) + const ancestors = [] + for (const [node, k] of queries) { + const ancestor = graphObject.kthAncestor(node, k) + ancestors.push(ancestor) + } + return ancestors +} + +export default binaryLifting diff --git a/Graphs/test/BinaryLifting.test.js b/Graphs/test/BinaryLifting.test.js new file mode 100644 index 000000000..769b877e0 --- /dev/null +++ b/Graphs/test/BinaryLifting.test.js @@ -0,0 +1,82 @@ +import binaryLifting from '../BinaryLifting' + +// The graph for Test Case 1 looks like this: +// +// 0 +// /|\ +// / | \ +// 1 3 5 +// / \ \ +// 2 4 6 +// \ +// 7 +// / \ +// 11 8 +// \ +// 9 +// \ +// 10 + +test('Test case 1', () => { + const root = 0 + const graph = [ + [0, 1], + [0, 3], + [0, 5], + [5, 6], + [1, 2], + [1, 4], + [4, 7], + [7, 11], + [7, 8], + [8, 9], + [9, 10] + ] + const queries = [ + [2, 1], + [6, 1], + [7, 2], + [8, 2], + [10, 2], + [10, 3], + [10, 5], + [11, 3] + ] + const kthAncestors = binaryLifting(root, graph, queries) + expect(kthAncestors).toEqual([1, 5, 1, 4, 8, 7, 1, 1]) +}) + +// The graph for Test Case 2 looks like this: +// +// 0 +// / \ +// 1 2 +// / \ \ +// 3 4 5 +// / / \ +// 6 7 8 + +test('Test case 2', () => { + const root = 0 + const graph = [ + [0, 1], + [0, 2], + [1, 3], + [1, 4], + [2, 5], + [3, 6], + [5, 7], + [5, 8] + ] + const queries = [ + [2, 1], + [3, 1], + [3, 2], + [6, 2], + [7, 3], + [8, 2], + [8, 3] + ] + const kthAncestors = binaryLifting(root, graph, queries) + expect(kthAncestors).toEqual([0, 1, 0, 1, 0, 2, 0]) +}) From 5364a1c31be7d787a10a9fa0ed287844ff35c618 Mon Sep 17 00:00:00 2001 From: Abdulrazak Yahuza <40579655+abdullyahuza@users.noreply.github.com> Date: Thu, 27 Oct 2022 19:45:53 +0100 Subject: [PATCH 20/44] chore: check for invalid input to factorial (#1229) --- Recursive/Factorial.js | 8 ++++++++ Recursive/test/Factorial.test.js | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/Recursive/Factorial.js b/Recursive/Factorial.js index 5d882e5ea..5a0d56051 100644 --- a/Recursive/Factorial.js +++ b/Recursive/Factorial.js @@ -9,6 +9,14 @@ */ const factorial = (n) => { + if (!Number.isInteger(n)) { + throw new RangeError('Not a Whole Number') + } + + if (n < 0) { + throw new RangeError('Not a Positive Number') + } + if (n === 0) { return 1 } diff --git a/Recursive/test/Factorial.test.js b/Recursive/test/Factorial.test.js index b124efea5..5f32a4433 100644 --- a/Recursive/test/Factorial.test.js +++ b/Recursive/test/Factorial.test.js @@ -8,4 +8,12 @@ describe('Factorial', () => { it('should return factorial 120 for value "5"', () => { expect(factorial(5)).toBe(120) }) + + it('Throw Error for Invalid Input', () => { + expect(() => factorial('-')).toThrow('Not a Whole Number') + expect(() => factorial(null)).toThrow('Not a Whole Number') + expect(() => factorial(undefined)).toThrow('Not a Whole Number') + expect(() => factorial(3.142)).toThrow('Not a Whole Number') + expect(() => factorial(-1)).toThrow('Not a Positive Number') + }) }) From 0fab492cebdec8c9d1596943813218bbac95bc16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Fri, 28 Oct 2022 18:51:54 +0200 Subject: [PATCH 21/44] fix: Project Euler P35 off-by-one error (#1238) * fix: Project Euler P35 off-by-one error * Updated Documentation in README.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 11 ++++++++++- Project-Euler/Problem035.js | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index a32addb9d..0c12cc96d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -57,6 +57,7 @@ * [LocalMaximomPoint](Data-Structures/Array/LocalMaximomPoint.js) * [NumberOfLocalMaximumPoints](Data-Structures/Array/NumberOfLocalMaximumPoints.js) * [QuickSelect](Data-Structures/Array/QuickSelect.js) + * [Reverse](Data-Structures/Array/Reverse.js) * **Graph** * [Graph](Data-Structures/Graph/Graph.js) * [Graph2](Data-Structures/Graph/Graph2.js) @@ -81,6 +82,7 @@ * **Tree** * [AVLTree](Data-Structures/Tree/AVLTree.js) * [BinarySearchTree](Data-Structures/Tree/BinarySearchTree.js) + * [SegmentTree](Data-Structures/Tree/SegmentTree.js) * [Trie](Data-Structures/Tree/Trie.js) * **Vectors** * [Vector2](Data-Structures/Vectors/Vector2.js) @@ -105,18 +107,19 @@ * [RodCutting](Dynamic-Programming/RodCutting.js) * [Shuf](Dynamic-Programming/Shuf.js) * [SieveOfEratosthenes](Dynamic-Programming/SieveOfEratosthenes.js) - * [UniquePaths](Dynamic-Programming/UniquePaths.js) * **Sliding-Window** * [LongestSubstringWithoutRepeatingCharacters](Dynamic-Programming/Sliding-Window/LongestSubstringWithoutRepeatingCharacters.js) * [PermutationinString](Dynamic-Programming/Sliding-Window/PermutationinString.js) * [SudokuSolver](Dynamic-Programming/SudokuSolver.js) * [TrappingRainWater](Dynamic-Programming/TrappingRainWater.js) * [TribonacciNumber](Dynamic-Programming/TribonacciNumber.js) + * [UniquePaths](Dynamic-Programming/UniquePaths.js) * [ZeroOneKnapsack](Dynamic-Programming/ZeroOneKnapsack.js) * **Geometry** * [ConvexHullGraham](Geometry/ConvexHullGraham.js) * **Graphs** * [BellmanFord](Graphs/BellmanFord.js) + * [BinaryLifting](Graphs/BinaryLifting.js) * [BreadthFirstSearch](Graphs/BreadthFirstSearch.js) * [BreadthFirstShortestPath](Graphs/BreadthFirstShortestPath.js) * [ConnectedComponents](Graphs/ConnectedComponents.js) @@ -126,6 +129,7 @@ * [Dijkstra](Graphs/Dijkstra.js) * [DijkstraSmallestPath](Graphs/DijkstraSmallestPath.js) * [FloydWarshall](Graphs/FloydWarshall.js) + * [Kosaraju](Graphs/Kosaraju.js) * [KruskalMST](Graphs/KruskalMST.js) * [NodeNeighbors](Graphs/NodeNeighbors.js) * [NumberOfIslands](Graphs/NumberOfIslands.js) @@ -158,6 +162,7 @@ * [EulerMethod](Maths/EulerMethod.js) * [EulersTotient](Maths/EulersTotient.js) * [EulersTotientFunction](Maths/EulersTotientFunction.js) + * [ExponentialFunction](Maths/ExponentialFunction.js) * [ExtendedEuclideanGCD](Maths/ExtendedEuclideanGCD.js) * [Factorial](Maths/Factorial.js) * [Factors](Maths/Factors.js) @@ -190,6 +195,7 @@ * [MeanSquareError](Maths/MeanSquareError.js) * [MidpointIntegration](Maths/MidpointIntegration.js) * [MobiusFunction](Maths/MobiusFunction.js) + * [ModularArithmetic](Maths/ModularArithmetic.js) * [ModularBinaryExponentiationRecursive](Maths/ModularBinaryExponentiationRecursive.js) * [NumberOfDigits](Maths/NumberOfDigits.js) * [Palindrome](Maths/Palindrome.js) @@ -209,6 +215,7 @@ * [ReversePolishNotation](Maths/ReversePolishNotation.js) * [ShorsAlgorithm](Maths/ShorsAlgorithm.js) * [SieveOfEratosthenes](Maths/SieveOfEratosthenes.js) + * [SieveOfEratosthenesIntArray](Maths/SieveOfEratosthenesIntArray.js) * [SimpsonIntegration](Maths/SimpsonIntegration.js) * [Softmax](Maths/Softmax.js) * [SquareRoot](Maths/SquareRoot.js) @@ -243,6 +250,7 @@ * [Problem023](Project-Euler/Problem023.js) * [Problem025](Project-Euler/Problem025.js) * [Problem028](Project-Euler/Problem028.js) + * [Problem035](Project-Euler/Problem035.js) * [Problem044](Project-Euler/Problem044.js) * **Recursive** * [BinaryEquivalent](Recursive/BinaryEquivalent.js) @@ -317,6 +325,7 @@ * [CheckRearrangePalindrome](String/CheckRearrangePalindrome.js) * [CheckSnakeCase](String/CheckSnakeCase.js) * [CheckWordOccurrence](String/CheckWordOccurrence.js) + * [CountLetters](String/CountLetters.js) * [CountSubstrings](String/CountSubstrings.js) * [CountVowels](String/CountVowels.js) * [CreatePermutations](String/CreatePermutations.js) diff --git a/Project-Euler/Problem035.js b/Project-Euler/Problem035.js index b62a8f031..f422defcd 100644 --- a/Project-Euler/Problem035.js +++ b/Project-Euler/Problem035.js @@ -15,7 +15,8 @@ function problem35 (n) { if (n < 2) { throw new Error('Invalid input') } - const list = sieveOfEratosthenes(n).filter(prime => !prime.toString().match(/[024568]/)) // Get a list of primes without 0, 2, 4, 5, 6, 8 + // Get a list of primes without 0, 2, 4, 5, 6, 8; this discards the circular primes 2 & 5 + const list = sieveOfEratosthenes(n).filter(prime => !prime.toString().match(/[024568]/)) const result = list.filter((number, _idx, arr) => { const str = String(number) @@ -28,7 +29,7 @@ function problem35 (n) { return true // If all rotations are prime, then the number is circular prime }) - return result.length + 1 // Add 2 to the result because 2 is a circular prime + return result.length + 2 // Add 2 to the result because the circular primes 2 & 5 were discarded } export { problem35 } From b88128dd97f0f3112660567b9a61c4a8222b604a Mon Sep 17 00:00:00 2001 From: Adrito Mukherjee <98008131+Adrito-M@users.noreply.github.com> Date: Sun, 30 Oct 2022 14:42:40 +0530 Subject: [PATCH 22/44] algorithm: LCA by binary lifting (#1237) * algorithm: LCA by binary lifting * removed trailing spaces * reduced code duplication by importing code from other file * made requested changes --- Graphs/BinaryLifting.js | 2 +- Graphs/LCABinaryLifting.js | 61 +++++++++++++++++++++ Graphs/test/LCABinaryLifting.test.js | 80 ++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 Graphs/LCABinaryLifting.js create mode 100644 Graphs/test/LCABinaryLifting.test.js diff --git a/Graphs/BinaryLifting.js b/Graphs/BinaryLifting.js index 594a5b667..40d664ba3 100644 --- a/Graphs/BinaryLifting.js +++ b/Graphs/BinaryLifting.js @@ -9,7 +9,7 @@ * Tutorial on Binary Lifting: https://codeforces.com/blog/entry/100826 */ -class BinaryLifting { +export class BinaryLifting { constructor (root, tree) { this.root = root this.connections = new Map() diff --git a/Graphs/LCABinaryLifting.js b/Graphs/LCABinaryLifting.js new file mode 100644 index 000000000..211f22f24 --- /dev/null +++ b/Graphs/LCABinaryLifting.js @@ -0,0 +1,61 @@ +/** + * Author: Adrito Mukherjee + * Findind Lowest Common Ancestor By Binary Lifting implementation in JavaScript + * The technique requires preprocessing the tree in O(N log N) using dynamic programming) + * It can be used to find Lowest Common Ancestor of two nodes in O(log N) + * Tutorial on Lowest Common Ancestor: https://www.geeksforgeeks.org/lca-in-a-tree-using-binary-lifting-technique + */ + +import { BinaryLifting } from './BinaryLifting' + +class LCABinaryLifting extends BinaryLifting { + constructor (root, tree) { + super(root, tree) + this.depth = new Map() // depth[node] stores the depth of node from root + this.depth.set(root, 1) + this.dfsDepth(root, root) + } + + dfsDepth (node, parent) { + // DFS to find depth of every node in the tree + for (const child of this.connections.get(node)) { + if (child !== parent) { + this.depth.set(child, this.depth.get(node) + 1) + this.dfsDepth(child, node) + } + } + } + + getLCA (node1, node2) { + // We make sure that node1 is the deeper node among node1 and node2 + if (this.depth.get(node1) < this.depth.get(node2)) { + [node1, node2] = [node2, node1] + } + // We check if node1 is the ancestor of node2, and if so, then return node1 + const k = this.depth.get(node1) - this.depth.get(node2) + node1 = this.kthAncestor(node1, k) + if (node1 === node2) { + return node1 + } + + for (let i = this.log - 1; i >= 0; i--) { + if (this.up.get(node1).get(i) !== this.up.get(node2).get(i)) { + node1 = this.up.get(node1).get(i) + node2 = this.up.get(node2).get(i) + } + } + return this.up.get(node1).get(0) + } +} + +function lcaBinaryLifting (root, tree, queries) { + const graphObject = new LCABinaryLifting(root, tree) + const lowestCommonAncestors = [] + for (const [node1, node2] of queries) { + const lca = graphObject.getLCA(node1, node2) + lowestCommonAncestors.push(lca) + } + return lowestCommonAncestors +} + +export default lcaBinaryLifting diff --git a/Graphs/test/LCABinaryLifting.test.js b/Graphs/test/LCABinaryLifting.test.js new file mode 100644 index 000000000..db77c9135 --- /dev/null +++ b/Graphs/test/LCABinaryLifting.test.js @@ -0,0 +1,80 @@ +import lcaBinaryLifting from '../LCABinaryLifting' + +// The graph for Test Case 1 looks like this: +// +// 0 +// /|\ +// / | \ +// 1 3 5 +// / \ \ +// 2 4 6 +// \ +// 7 +// / \ +// 11 8 +// \ +// 9 +// \ +// 10 + +test('Test case 1', () => { + const root = 0 + const graph = [ + [0, 1], + [0, 3], + [0, 5], + [5, 6], + [1, 2], + [1, 4], + [4, 7], + [7, 11], + [7, 8], + [8, 9], + [9, 10] + ] + const queries = [ + [1, 3], + [6, 5], + [3, 6], + [7, 10], + [8, 10], + [11, 2], + [11, 10] + ] + const lowestCommonAncestors = lcaBinaryLifting(root, graph, queries) + expect(lowestCommonAncestors).toEqual([0, 5, 0, 7, 8, 1, 7]) +}) + +// The graph for Test Case 2 looks like this: +// +// 0 +// / \ +// 1 2 +// / \ \ +// 3 4 5 +// / / \ +// 6 7 8 + +test('Test case 2', () => { + const root = 0 + const graph = [ + [0, 1], + [0, 2], + [1, 3], + [1, 4], + [2, 5], + [3, 6], + [5, 7], + [5, 8] + ] + const queries = [ + [1, 2], + [3, 4], + [5, 4], + [6, 7], + [6, 8], + [7, 8] + ] + const lowestCommonAncestors = lcaBinaryLifting(root, graph, queries) + expect(lowestCommonAncestors).toEqual([0, 1, 0, 0, 0, 5]) +}) From c5101e3e2b6f05240b7da3182e3739585dd7b073 Mon Sep 17 00:00:00 2001 From: Khushi Shukla Date: Sun, 30 Oct 2022 14:46:56 +0530 Subject: [PATCH 23/44] chore: add reverse to singly linked list (#1241) * Fix: Reverse Singly Linked List * Added reverse in SinglyLinkedList * Added reverse in SinglyLinkedList * Changes made --- .../Linked-List/ReverseSinglyLinkedList.js | 16 ++++++++++++++++ Data-Structures/Linked-List/SinglyLinkedList.js | 14 ++++++++++++++ .../test/ReverseSinglyLinkedList.test.js | 14 ++++++++++++++ .../Linked-List/test/SinglyLinkedList.test.js | 6 ++++++ 4 files changed, 50 insertions(+) create mode 100644 Data-Structures/Linked-List/ReverseSinglyLinkedList.js create mode 100644 Data-Structures/Linked-List/test/ReverseSinglyLinkedList.test.js diff --git a/Data-Structures/Linked-List/ReverseSinglyLinkedList.js b/Data-Structures/Linked-List/ReverseSinglyLinkedList.js new file mode 100644 index 000000000..db4e7ef6d --- /dev/null +++ b/Data-Structures/Linked-List/ReverseSinglyLinkedList.js @@ -0,0 +1,16 @@ +/** A LinkedList based solution to reverse a number +Problem Statement: Given a number such that each of its digit is stored in a singly linked list. Reverse the linked list and return the head of the linked list Link for the Problem: https://leetcode.com/problems/reverse-linked-list/ */ +class ReverseSinglyLinkedList { + solution (head) { + let prev = null + let next = null + while (head) { + next = head.next + head.next = prev + prev = head + head = next + } + return prev + }; +} +export { ReverseSinglyLinkedList } diff --git a/Data-Structures/Linked-List/SinglyLinkedList.js b/Data-Structures/Linked-List/SinglyLinkedList.js index d55e0b14b..605ede401 100644 --- a/Data-Structures/Linked-List/SinglyLinkedList.js +++ b/Data-Structures/Linked-List/SinglyLinkedList.js @@ -274,6 +274,20 @@ class LinkedList { log () { console.log(JSON.stringify(this.headNode, null, 2)) } + + // Method to reverse the LinkedList + reverse () { + let head = this.headNode + let prev = null + let next = null + while (head) { + next = head.next + head.next = prev + prev = head + head = next + } + this.headNode = prev + }; } export { Node, LinkedList } diff --git a/Data-Structures/Linked-List/test/ReverseSinglyLinkedList.test.js b/Data-Structures/Linked-List/test/ReverseSinglyLinkedList.test.js new file mode 100644 index 000000000..847026db8 --- /dev/null +++ b/Data-Structures/Linked-List/test/ReverseSinglyLinkedList.test.js @@ -0,0 +1,14 @@ +import { ReverseSinglyLinkedList } from '../ReverseSinglyLinkedList' +import { Node } from '../SinglyLinkedList' +describe('ReverseSinglyLinkedList', () => { + it('Reverse a Number Represented as Linked List', () => { + const headNode = new Node(3) + headNode.next = new Node(4) + headNode.next.next = new Node(1) + const expected = new Node(1) + expected.next = new Node(4) + expected.next.next = new Node(3) + const reverseSinglyLinkedList = new ReverseSinglyLinkedList() + expect(reverseSinglyLinkedList.solution(headNode)).toEqual(expected) + }) +}) diff --git a/Data-Structures/Linked-List/test/SinglyLinkedList.test.js b/Data-Structures/Linked-List/test/SinglyLinkedList.test.js index 764cac7aa..a04d06ba1 100644 --- a/Data-Structures/Linked-List/test/SinglyLinkedList.test.js +++ b/Data-Structures/Linked-List/test/SinglyLinkedList.test.js @@ -247,4 +247,10 @@ describe('SinglyLinkedList', () => { headNode.rotateListRight(5) expect(headNode.get()).toEqual([20, 30, 40, 50, 10]) }) + + it('Reverse a Linked List', () => { + const list = new LinkedList([4, 3, 1]) + list.reverse() + expect(list.get()).toEqual([1, 3, 4]) + }) }) From f1ef64cc2db64bb1a3b021622d2422325b0b9fd2 Mon Sep 17 00:00:00 2001 From: Gustavo Kamihara <49957327+Japoncio3k@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:01:20 -0300 Subject: [PATCH 24/44] algorithm class: cone (#1253) --- Geometry/Cone.js | 25 +++++++++++++++++++++++++ Geometry/Test/Cone.test.js | 11 +++++++++++ 2 files changed, 36 insertions(+) create mode 100644 Geometry/Cone.js create mode 100644 Geometry/Test/Cone.test.js diff --git a/Geometry/Cone.js b/Geometry/Cone.js new file mode 100644 index 000000000..742a527f4 --- /dev/null +++ b/Geometry/Cone.js @@ -0,0 +1,25 @@ +/** + * This class represents a circular cone and can calculate its volume and surface area + * https://en.wikipedia.org/wiki/Cone + * @constructor + * @param {number} baseRadius - The radius of the base of the cone. + * @param {number} height - The height of the cone + */ +export default class Cone { + constructor (baseRadius, height) { + this.baseRadius = baseRadius + this.height = height + } + + baseArea = () => { + return Math.pow(this.baseRadius, 2) * Math.PI + } + + volume = () => { + return this.baseArea() * this.height * 1 / 3 + } + + surfaceArea = () => { + return this.baseArea() + Math.PI * this.baseRadius * Math.sqrt(Math.pow(this.baseRadius, 2) + Math.pow(this.height, 2)) + } +} diff --git a/Geometry/Test/Cone.test.js b/Geometry/Test/Cone.test.js new file mode 100644 index 000000000..3ab001039 --- /dev/null +++ b/Geometry/Test/Cone.test.js @@ -0,0 +1,11 @@ +import Cone from '../Cone' + +const cone = new Cone(3, 5) + +test('The Volume of a cone with base radius equal to 3 and height equal to 5', () => { + expect(parseFloat(cone.volume().toFixed(2))).toEqual(47.12) +}) + +test('The Surface Area of a cone with base radius equal to 3 and height equal to 5', () => { + expect(parseFloat(cone.surfaceArea().toFixed(2))).toEqual(83.23) +}) From 7256e5313f2905af40d90be3fce1a16e5179d980 Mon Sep 17 00:00:00 2001 From: Gustavo Kamihara <49957327+Japoncio3k@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:02:35 -0300 Subject: [PATCH 25/44] algorithm class: circle (#1252) --- Geometry/Circle.js | 19 +++++++++++++++++++ Geometry/Test/Circle.test.js | 11 +++++++++++ 2 files changed, 30 insertions(+) create mode 100644 Geometry/Circle.js create mode 100644 Geometry/Test/Circle.test.js diff --git a/Geometry/Circle.js b/Geometry/Circle.js new file mode 100644 index 000000000..4b4f8e3b9 --- /dev/null +++ b/Geometry/Circle.js @@ -0,0 +1,19 @@ +/** + * This class represents a circle and can calculate it's perimeter and area + * https://en.wikipedia.org/wiki/Circle + * @constructor + * @param {number} radius - The radius of the circule. + */ +export default class Circle { + constructor (radius) { + this.radius = radius + } + + perimeter = () => { + return this.radius * 2 * Math.PI + } + + area = () => { + return Math.pow(this.radius, 2) * Math.PI + } +} diff --git a/Geometry/Test/Circle.test.js b/Geometry/Test/Circle.test.js new file mode 100644 index 000000000..b2332df43 --- /dev/null +++ b/Geometry/Test/Circle.test.js @@ -0,0 +1,11 @@ +import Circle from '../Circle' + +const circle = new Circle(3) + +test('The area of a circle with radius equal to 3', () => { + expect(parseFloat(circle.area().toFixed(2))).toEqual(28.27) +}) + +test('The perimeter of a circle with radius equal to 3', () => { + expect(parseFloat(circle.perimeter().toFixed(2))).toEqual(18.85) +}) From 8cbaf2e2994b5f17c0589a38414c67081015c3b1 Mon Sep 17 00:00:00 2001 From: Lcvieira2001 <114815013+Lcvieira2001@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:35:58 -0300 Subject: [PATCH 26/44] convertion: ounce to kilogram (#1248) * Added ounces to kilograms convertion * Added PR suggestions * changed to export default --- Conversions/OuncesToKilograms.js | 11 +++++++++++ Conversions/test/OuncesToKilogram.test.js | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 Conversions/OuncesToKilograms.js create mode 100644 Conversions/test/OuncesToKilogram.test.js diff --git a/Conversions/OuncesToKilograms.js b/Conversions/OuncesToKilograms.js new file mode 100644 index 000000000..d39de8564 --- /dev/null +++ b/Conversions/OuncesToKilograms.js @@ -0,0 +1,11 @@ +/** + * This function converts ounces to kilograms + * https://en.wikipedia.org/wiki/Ounce + * @constructor + * @param {number} oz - Amount of ounces to convert to kilograms + */ +const ouncesToKilograms = (oz) => { + return oz * 28.3498 / 1000 +} + +export default ouncesToKilograms diff --git a/Conversions/test/OuncesToKilogram.test.js b/Conversions/test/OuncesToKilogram.test.js new file mode 100644 index 000000000..e72e06958 --- /dev/null +++ b/Conversions/test/OuncesToKilogram.test.js @@ -0,0 +1,5 @@ +import ouncesToKilograms from '../OuncesToKilograms' + +test('Convert 60 ounces to kilograms', () => { + expect(parseFloat(ouncesToKilograms(60).toFixed(3))).toBe(1.701) +}) From b634aa581c1472a9b530f2607bbeff33278b693c Mon Sep 17 00:00:00 2001 From: Lcvieira2001 <114815013+Lcvieira2001@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:37:48 -0300 Subject: [PATCH 27/44] conversion: liters to US gallons (#1255) --- Conversions/LitersToUSGallons.js | 11 +++++++++++ Conversions/test/LitersToUSGallons.test.js | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 Conversions/LitersToUSGallons.js create mode 100644 Conversions/test/LitersToUSGallons.test.js diff --git a/Conversions/LitersToUSGallons.js b/Conversions/LitersToUSGallons.js new file mode 100644 index 000000000..62941a2c6 --- /dev/null +++ b/Conversions/LitersToUSGallons.js @@ -0,0 +1,11 @@ +/** + * This function converts liters to US gallons + * https://en.wikipedia.org/wiki/Gallon + * @constructor + * @param {number} liters - Amount of liters to convert to gallons + */ +const litersToUSGallons = (liters) => { + return liters / 3.785411784 +} + +export default litersToUSGallons diff --git a/Conversions/test/LitersToUSGallons.test.js b/Conversions/test/LitersToUSGallons.test.js new file mode 100644 index 000000000..a1ad26b75 --- /dev/null +++ b/Conversions/test/LitersToUSGallons.test.js @@ -0,0 +1,5 @@ +import litersToUSGallons from '../LitersToUSGallons' + +test('Convert 50 liters to US gallons', () => { + expect(parseFloat(litersToUSGallons(50).toFixed(2))).toBe(13.21) +}) From 014a38b2d42968805552dc4b6d355ac90bb859f7 Mon Sep 17 00:00:00 2001 From: Lcvieira2001 <114815013+Lcvieira2001@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:39:41 -0300 Subject: [PATCH 28/44] algorithm: volume of sphere (#1249) * Added sphere volume * Fixed indentation * Added PR Suggestions --- Geometry/Sphere.js | 19 +++++++++++++++++++ Geometry/Test/Sphere.test.js | 11 +++++++++++ 2 files changed, 30 insertions(+) create mode 100644 Geometry/Sphere.js create mode 100644 Geometry/Test/Sphere.test.js diff --git a/Geometry/Sphere.js b/Geometry/Sphere.js new file mode 100644 index 000000000..82f539b91 --- /dev/null +++ b/Geometry/Sphere.js @@ -0,0 +1,19 @@ +/** + * This class represents a sphere and can calculate its volume and surface area + * @constructor + * @param {number} radius - The radius of the sphere + * @see https://en.wikipedia.org/wiki/Sphere + */ +export default class Sphere { + constructor (radius) { + this.radius = radius + } + + volume = () => { + return Math.pow(this.radius, 3) * Math.PI * 4 / 3 + } + + surfaceArea = () => { + return Math.pow(this.radius, 2) * Math.PI * 4 + } +} diff --git a/Geometry/Test/Sphere.test.js b/Geometry/Test/Sphere.test.js new file mode 100644 index 000000000..18f8333a7 --- /dev/null +++ b/Geometry/Test/Sphere.test.js @@ -0,0 +1,11 @@ +import Sphere from '../Sphere' + +const sphere = new Sphere(3) + +test('The Volume of a sphere with base radius equal to 3 and height equal to 5', () => { + expect(parseFloat(sphere.volume().toFixed(2))).toEqual(113.1) +}) + +test('The Surface Area of a sphere with base radius equal to 3 and height equal to 5', () => { + expect(parseFloat(sphere.surfaceArea().toFixed(2))).toEqual(113.1) +}) From 863185d2b424113fcd30d7856645a45a12c3e399 Mon Sep 17 00:00:00 2001 From: Gustavo Kamihara <49957327+Japoncio3k@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:40:36 -0300 Subject: [PATCH 29/44] algorithm class: pyramid (#1254) * Added pyramid * Removed * 1 * Change class name --- Geometry/Pyramid.js | 25 +++++++++++++++++++++++++ Geometry/Test/Pyramid.test.js | 11 +++++++++++ 2 files changed, 36 insertions(+) create mode 100644 Geometry/Pyramid.js create mode 100644 Geometry/Test/Pyramid.test.js diff --git a/Geometry/Pyramid.js b/Geometry/Pyramid.js new file mode 100644 index 000000000..759b7376e --- /dev/null +++ b/Geometry/Pyramid.js @@ -0,0 +1,25 @@ +/** + * This class represents a regular pyramid and can calculate its volume and surface area + * https://en.wikipedia.org/wiki/Pyramid_(geometry) + * @constructor + * @param {number} bsl - The side length of the base of the pyramid. + * @param {number} height - The height of the pyramid + */ +export default class Pyramid { + constructor (bsl, height) { + this.bsl = bsl + this.height = height + } + + baseArea = () => { + return Math.pow(this.bsl, 2) + } + + volume = () => { + return this.baseArea() * this.height / 3 + } + + surfaceArea = () => { + return this.baseArea() + this.bsl * 4 / 2 * Math.sqrt(Math.pow(this.bsl / 2, 2) + Math.pow(this.height, 2)) + } +} diff --git a/Geometry/Test/Pyramid.test.js b/Geometry/Test/Pyramid.test.js new file mode 100644 index 000000000..a6d2b16d1 --- /dev/null +++ b/Geometry/Test/Pyramid.test.js @@ -0,0 +1,11 @@ +import Pyramid from '../Pyramid' + +const pyramid = new Pyramid(3, 5) + +test('The Volume of a cone with base radius equal to 3 and height equal to 5', () => { + expect(parseFloat(pyramid.volume().toFixed(2))).toEqual(15) +}) + +test('The Surface Area of a cone with base radius equal to 3 and height equal to 5', () => { + expect(parseFloat(pyramid.surfaceArea().toFixed(2))).toEqual(40.32) +}) From cc0700f1220e9e0f80337104cf77b9414641817c Mon Sep 17 00:00:00 2001 From: Lcvieira2001 <114815013+Lcvieira2001@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:41:29 -0300 Subject: [PATCH 30/44] conversion: liters to imperial gallons (#1256) * Added liters to imperial gallons conversion * Fixed documentation @see * Pr suggestion --- Conversions/LitersToImperialGallons.js | 11 +++++++++++ Conversions/test/LitersToImperialGallons.test.js | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 Conversions/LitersToImperialGallons.js create mode 100644 Conversions/test/LitersToImperialGallons.test.js diff --git a/Conversions/LitersToImperialGallons.js b/Conversions/LitersToImperialGallons.js new file mode 100644 index 000000000..5e8008ecb --- /dev/null +++ b/Conversions/LitersToImperialGallons.js @@ -0,0 +1,11 @@ +/** + * This function converts liters to imperial gallons + * @constructor + * @param {number} liters - Amount of liters to convert to gallons + * @see https://en.wikipedia.org/wiki/Gallon + */ +const litersToImperialGallons = (liters) => { + return liters / 4.54609 +} + +export default litersToImperialGallons diff --git a/Conversions/test/LitersToImperialGallons.test.js b/Conversions/test/LitersToImperialGallons.test.js new file mode 100644 index 000000000..3a019887d --- /dev/null +++ b/Conversions/test/LitersToImperialGallons.test.js @@ -0,0 +1,5 @@ +import litersToImperialGallons from '../LitersToImperialGallons' + +test('Convert 25 liters to imperial gallons', () => { + expect(parseFloat(litersToImperialGallons(25).toFixed(2))).toBe(5.5) +}) From 7fb121508d0dfd702a3c9424bf11d0e5243fe5d2 Mon Sep 17 00:00:00 2001 From: Adrito Mukherjee <98008131+Adrito-M@users.noreply.github.com> Date: Mon, 31 Oct 2022 22:12:06 +0530 Subject: [PATCH 31/44] algorithm: ZFunction (#1239) * algorithm: ZFunction * made requested changes * corrected spelling mistakes * made requested changes --- String/ZFunction.js | 41 +++++++++++++++++++++++++++++++++++ String/test/ZFunction.test.js | 8 +++++++ 2 files changed, 49 insertions(+) create mode 100644 String/ZFunction.js create mode 100644 String/test/ZFunction.test.js diff --git a/String/ZFunction.js b/String/ZFunction.js new file mode 100644 index 000000000..0d1ff2b12 --- /dev/null +++ b/String/ZFunction.js @@ -0,0 +1,41 @@ +/** + * @author: Adrito Mukherjee + * Implementation of ZFunction in JavaScript + * ZFunction at an index i gives the length of the longest substring starting at i, that is also a prefix of the whole string + * ZFunction for all indices in a string can be calculated in O(N) + * @see https://cp-algorithms.com/string/z-function.html + * @param {String} text The string whose Z Function is to be calculated + * @return {Array} Returns an array whose i-th index is the value of Z Function for text at index i + */ + +function zFunction (text) { + const length = text.length + const zArray = Array(length).fill(0) + // Initializing left and right variable to zero + let left = 0 + let right = 0 + for (let index = 0; index < length; index++) { + // If index is less than or equal to right, we reuse the values of zFunction at index right-index+1 + // It is made sure that value of zFunction at index is not greater than maximum possible value at index + if (index <= right) { + zArray[index] = Math.min(right - index + 1, zArray[index - left]) + } + + // After zArray[index] is initialized, we see if we can increase its value by trivially comparing character by character + while ( + index + zArray[index] < length && + text[zArray[index]] === text[index + zArray[index]] + ) { + zArray[index]++ + } + + // If index + zArray[index] - 1 is greater than right, we update values of variables left and right + if (index + zArray[index] - 1 > right) { + left = index + right = index + zArray[index] - 1 + } + } + return zArray +} + +export default zFunction diff --git a/String/test/ZFunction.test.js b/String/test/ZFunction.test.js new file mode 100644 index 000000000..a945f2804 --- /dev/null +++ b/String/test/ZFunction.test.js @@ -0,0 +1,8 @@ +import zFunction from '../ZFunction' + +test('Testing zFunction', () => { + expect(zFunction('aabxaayaab')).toEqual([10, 1, 0, 0, 2, 1, 0, 3, 1, 0]) + expect(zFunction('aabxaabxcaabxaabxay')).toEqual([ + 19, 1, 0, 0, 4, 1, 0, 0, 0, 8, 1, 0, 0, 5, 1, 0, 0, 1, 0 + ]) +}) From 35e1fe68d0d6e8be2c2cfdc4dd581faa5128b7e2 Mon Sep 17 00:00:00 2001 From: Alex Popov Date: Mon, 31 Oct 2022 19:49:14 +0300 Subject: [PATCH 32/44] algorithm: add IntToBase algo and a test for it (#1258) --- Maths/IntToBase.js | 40 ++++++++++++++++++++++++++++++++++++ Maths/test/IntToBase.test.js | 25 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 Maths/IntToBase.js create mode 100644 Maths/test/IntToBase.test.js diff --git a/Maths/IntToBase.js b/Maths/IntToBase.js new file mode 100644 index 000000000..6ec8a0bab --- /dev/null +++ b/Maths/IntToBase.js @@ -0,0 +1,40 @@ +/** + * @function intToBase + * @description Convert a number from decimal system to another (till decimal) + * @param {Number} number Number to be converted + * @param {Number} base Base of new number system + * @returns {String} Converted Number + * @see [HornerMethod](https://en.wikipedia.org/wiki/Horner%27s_method) + * @example + * const num1 = 125 // Needs to be converted to the binary number system + * gornerScheme(num, 2); // ===> 1111101 + * @example + * const num2 = 125 // Needs to be converted to the octal number system + * gornerScheme(num, 8); // ===> 175 + */ +const intToBase = (number, base) => { + if (typeof number !== 'number' || typeof base !== 'number') { + throw new Error('Input data must be numbers') + } + // Zero in any number system is zero + if (number === 0) { + return '0' + } + let absoluteValue = Math.abs(number) + let convertedNumber = '' + while (absoluteValue > 0) { + // Every iteration last digit is taken away + // and added to the previous one + const lastDigit = absoluteValue % base + convertedNumber = lastDigit + convertedNumber + absoluteValue = Math.trunc(absoluteValue / base) + } + // Result is whether negative or positive, + // depending on the original value + if (number < 0) { + convertedNumber = '-' + convertedNumber + } + return convertedNumber +} + +export { intToBase } diff --git a/Maths/test/IntToBase.test.js b/Maths/test/IntToBase.test.js new file mode 100644 index 000000000..002dde6af --- /dev/null +++ b/Maths/test/IntToBase.test.js @@ -0,0 +1,25 @@ +import { intToBase } from '../intToBase' + +describe('Int to Base', () => { + test('Conversion to the binary system', () => { + expect(intToBase(210, 2)).toEqual('11010010') + expect(intToBase(-210, 2)).toEqual('-11010010') + }) + test('Conversion to the system with base 5', () => { + expect(intToBase(210, 5)).toEqual('1320') + expect(intToBase(-210, 5)).toEqual('-1320') + }) + test('Conversion to the octal system', () => { + expect(intToBase(210, 8)).toEqual('322') + expect(intToBase(-210, 8)).toEqual('-322') + }) + test('Output is 0', () => { + expect(intToBase(0, 8)).toEqual('0') + expect(intToBase(0, 8)).toEqual('0') + }) + test('Throwing an exception', () => { + expect(() => intToBase('string', 2)).toThrow() + expect(() => intToBase(10, 'base')).toThrow() + expect(() => intToBase(true, false)).toThrow() + }) +}) From c39d6665ce90f5eaaec352bef4e83e346ceac30d Mon Sep 17 00:00:00 2001 From: Alex Popov Date: Mon, 31 Oct 2022 19:50:33 +0300 Subject: [PATCH 33/44] algorithm: logarithmic square root (#1259) * algorithm: add SquareRootLogarithmic algo and a test for it * fix: fix spelling errors * refactor: rename a variable "e" --> "edge" --- Maths/SquareRootLogarithmic.js | 41 ++++++++++++++++++++++++ Maths/test/SquareRootLogarithmic.test.js | 13 ++++++++ 2 files changed, 54 insertions(+) create mode 100644 Maths/SquareRootLogarithmic.js create mode 100644 Maths/test/SquareRootLogarithmic.test.js diff --git a/Maths/SquareRootLogarithmic.js b/Maths/SquareRootLogarithmic.js new file mode 100644 index 000000000..e9b54aed3 --- /dev/null +++ b/Maths/SquareRootLogarithmic.js @@ -0,0 +1,41 @@ +/** + * @function squareRootLogarithmic + * @description + * Return the square root of 'num' rounded down + * to the nearest integer. + * More info: https://leetcode.com/problems/sqrtx/ + * @param {Number} num Number whose square of root is to be found + * @returns {Number} Square root + * @see [BinarySearch](https://en.wikipedia.org/wiki/Binary_search_algorithm) + * @example + * const num1 = 4 + * logarithmicSquareRoot(num1) // ====> 2 + * @example + * const num2 = 8 + * logarithmicSquareRoot(num1) // ====> 2 + * + */ +const squareRootLogarithmic = (num) => { + if (typeof num !== 'number') { + throw new Error('Input data must be numbers') + } + let answer = 0 + let sqrt = 0 + let edge = num + + while (sqrt <= edge) { + const mid = Math.trunc((sqrt + edge) / 2) + if (mid * mid === num) { + return mid + } else if (mid * mid < num) { + sqrt = mid + 1 + answer = mid + } else { + edge = mid - 1 + } + } + + return answer +} + +export { squareRootLogarithmic } diff --git a/Maths/test/SquareRootLogarithmic.test.js b/Maths/test/SquareRootLogarithmic.test.js new file mode 100644 index 000000000..6eec49d23 --- /dev/null +++ b/Maths/test/SquareRootLogarithmic.test.js @@ -0,0 +1,13 @@ +import { squareRootLogarithmic } from '../SquareRootLogarithmic' + +describe('SquareRootLogarithmic', () => { + test('Finding the square root of a positive integer', () => { + expect(squareRootLogarithmic(4)).toEqual(2) + expect(squareRootLogarithmic(16)).toEqual(4) + expect(squareRootLogarithmic(8)).toEqual(2) + }) + test('Throwing an exception', () => { + expect(() => squareRootLogarithmic('not a number')).toThrow() + expect(() => squareRootLogarithmic(true)).toThrow() + }) +}) From 5668e64b29049ef7b2da24621f17f8c6eeb3b664 Mon Sep 17 00:00:00 2001 From: Alex Popov Date: Mon, 31 Oct 2022 20:07:26 +0300 Subject: [PATCH 34/44] algorithm: unique paths 2 (#1257) * algorithm: add UniquePaths2 algo and a test for it * fix: fix inaccuracies and spelling errors --- Dynamic-Programming/UniquePaths2.js | 76 +++++++++++++++++++ .../tests/UniquePaths2.test.js | 19 +++++ 2 files changed, 95 insertions(+) create mode 100644 Dynamic-Programming/UniquePaths2.js create mode 100644 Dynamic-Programming/tests/UniquePaths2.test.js diff --git a/Dynamic-Programming/UniquePaths2.js b/Dynamic-Programming/UniquePaths2.js new file mode 100644 index 000000000..52bf9d08b --- /dev/null +++ b/Dynamic-Programming/UniquePaths2.js @@ -0,0 +1,76 @@ +/* + * Unique Paths 2 + * + * There is a robot on an `m x n` grid. + * The robot is initially located at the top-left corner + * The robot tries to move to the bottom-right corner. + * The robot can only move either down or right at any point in time. + * + * Given grid with obstacles + * An obstacle and space are marked as 1 or 0 respectively in grid. + * A path that the robot takes cannot include any square that is an obstacle. + * Return the number of possible unique paths that the robot can take to reach the bottom-right corner. + * + * More info: https://leetcode.com/problems/unique-paths-ii/ + */ + +/** + * @description Return 'rows x columns' grid with cells filled by 'filler' + * @param {Number} rows Number of rows in the grid + * @param {Number} columns Number of columns in the grid + * @param {String | Number | Boolean} filler The value to fill cells + * @returns {Array [][]} + */ +const generateMatrix = (rows, columns, filler = 0) => { + const matrix = [] + for (let i = 0; i < rows; i++) { + const submatrix = [] + for (let k = 0; k < columns; k++) { + submatrix[k] = filler + } + matrix[i] = submatrix + } + return matrix +} + +/** + * @description Return number of unique paths + * @param {Array [][]} obstacles Obstacles grid + * @returns {Number} + */ +const uniquePaths2 = (obstacles) => { + if (!Array.isArray(obstacles)) { + throw new Error('Input data must be type of Array') + } + // Create grid for calculating number of unique ways + const rows = obstacles.length + const columns = obstacles[0].length + const grid = generateMatrix(rows, columns) + // Fill the outermost cell with 1 b/c it has + // the only way to reach neighbor + for (let i = 0; i < rows; i++) { + // If robot encounters an obstacle in these cells, + // he cannot continue moving in that direction + if (obstacles[i][0]) { + break + } + grid[i][0] = 1 + } + for (let j = 0; j < columns; j++) { + if (obstacles[0][j]) { + break + } + grid[0][j] = 1 + } + // Fill the rest of grid by dynamic programming + // using following reccurent formula: + // K[i][j] = K[i - 1][j] + K[i][j - 1] + for (let i = 1; i < rows; i++) { + for (let j = 1; j < columns; j++) { + grid[i][j] = obstacles[i][j] ? 0 : grid[i - 1][j] + grid[i][j - 1] + } + } + return grid[rows - 1][columns - 1] +} + +export { uniquePaths2 } diff --git a/Dynamic-Programming/tests/UniquePaths2.test.js b/Dynamic-Programming/tests/UniquePaths2.test.js new file mode 100644 index 000000000..e34b9a330 --- /dev/null +++ b/Dynamic-Programming/tests/UniquePaths2.test.js @@ -0,0 +1,19 @@ +import { uniquePaths2 } from '../UniquePaths2' + +describe('Unique Paths2', () => { + // Should return number of ways, taken into account the obstacles + test('There are obstacles in the way', () => { + expect(uniquePaths2([[0, 0, 0], [0, 1, 0], [0, 0, 0]])).toEqual(2) + expect(uniquePaths2([[0, 0, 0], [0, 1, 0], [0, 0, 0], [1, 0, 0]])).toEqual(3) + }) + // Should return number of all possible ways to reach right-bottom corner + test('There are no obstacles in the way', () => { + expect(uniquePaths2([[0, 0, 0], [0, 0, 0], [0, 0, 0]])).toEqual(6) + expect(uniquePaths2([[0, 0, 0], [0, 0, 0]])).toEqual(3) + }) + // Should throw an exception b/c input data has wrong type + test('There are wrong type of input data', () => { + expect(() => uniquePaths2('wrong input')).toThrow() + expect(() => uniquePaths2(100)).toThrow() + }) +}) From 00a97d5e1b0ec14f68eab46c606bdf41bb9a0fc0 Mon Sep 17 00:00:00 2001 From: Alex Popov Date: Tue, 1 Nov 2022 06:45:02 +0300 Subject: [PATCH 35/44] algorithm: percentage of letter (#1261) --- String/PercentageOfLetters.js | 27 +++++++++++++++++++++++++ String/test/PercentageOfLetters.test.js | 16 +++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 String/PercentageOfLetters.js create mode 100644 String/test/PercentageOfLetters.test.js diff --git a/String/PercentageOfLetters.js b/String/PercentageOfLetters.js new file mode 100644 index 000000000..fbd6a03be --- /dev/null +++ b/String/PercentageOfLetters.js @@ -0,0 +1,27 @@ +/** + * @function percentageOfLetter + * @description Return the percentage of characters in 'str' + * that equal 'letter' rounded down to the nearest whole percent. + * More info: https://leetcode.com/problems/percentage-of-letter-in-string/ + * @param {String} str + * @param {String} letter + * @returns {Number} + * @example + * const str = 'foobar', const letter = 'o' + * percentageOfLetter(str, letter) // ===> 33 + */ +const percentageOfLetter = (str, letter) => { + if (typeof str !== 'string' || typeof letter !== 'string') { + throw new Error('Input data must be strings') + } + let letterCount = 0 + // Iterate through the whole given text + for (let i = 0; i < str.length; i++) { + // Count how often the letter appears in the word + letterCount += str[i].toLowerCase() === letter.toLowerCase() ? 1 : 0 + } + const percentage = Math.floor((100 * letterCount) / str.length) + return percentage +} + +export { percentageOfLetter } diff --git a/String/test/PercentageOfLetters.test.js b/String/test/PercentageOfLetters.test.js new file mode 100644 index 000000000..7a58dd0ec --- /dev/null +++ b/String/test/PercentageOfLetters.test.js @@ -0,0 +1,16 @@ +import { percentageOfLetter } from '../PercentageOfLetters' + +describe('Percentage of Letters in a String', () => { + test('Calculate percent for lower case', () => { + expect(percentageOfLetter('foobar', 'o')).toEqual(33) + expect(percentageOfLetter('aaabcd', 'a')).toEqual(50) + }) + test('Calculate percent for upper case', () => { + expect(percentageOfLetter('foobar', 'o')).toEqual(33) + expect(percentageOfLetter('aAabcd', 'a')).toEqual(50) + }) + test('Throwing an exception', () => { + expect(() => percentageOfLetter(100, 'string')).toThrow() + expect(() => percentageOfLetter('string', true)).toThrow() + }) +}) From 640abcf468d18a2dc0d171cd80613dbeb075bdd7 Mon Sep 17 00:00:00 2001 From: Carlos Rafael Date: Wed, 2 Nov 2022 09:54:05 -0300 Subject: [PATCH 36/44] fix: exchanging wrong path that breaks pipeline (#1262) --- DIRECTORY.md | 14 ++++++++++++++ Maths/test/IntToBase.test.js | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 0c12cc96d..2d54a17f8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -42,9 +42,12 @@ * [HexToBinary](Conversions/HexToBinary.js) * [HexToDecimal](Conversions/HexToDecimal.js) * [HexToRGB](Conversions/HexToRGB.js) + * [LitersToImperialGallons](Conversions/LitersToImperialGallons.js) + * [LitersToUSGallons](Conversions/LitersToUSGallons.js) * [LowerCaseConversion](Conversions/LowerCaseConversion.js) * [MeterToFeetConversion](Conversions/MeterToFeetConversion.js) * [OctToDecimal](Conversions/OctToDecimal.js) + * [OuncesToKilograms](Conversions/OuncesToKilograms.js) * [RailwayTimeConversion](Conversions/RailwayTimeConversion.js) * [RgbHsvConversion](Conversions/RgbHsvConversion.js) * [RGBToHex](Conversions/RGBToHex.js) @@ -70,6 +73,7 @@ * [AddTwoNumbers](Data-Structures/Linked-List/AddTwoNumbers.js) * [CycleDetection](Data-Structures/Linked-List/CycleDetection.js) * [DoublyLinkedList](Data-Structures/Linked-List/DoublyLinkedList.js) + * [ReverseSinglyLinkedList](Data-Structures/Linked-List/ReverseSinglyLinkedList.js) * [SinglyCircularLinkedList](Data-Structures/Linked-List/SinglyCircularLinkedList.js) * [SinglyLinkedList](Data-Structures/Linked-List/SinglyLinkedList.js) * **Queue** @@ -114,9 +118,14 @@ * [TrappingRainWater](Dynamic-Programming/TrappingRainWater.js) * [TribonacciNumber](Dynamic-Programming/TribonacciNumber.js) * [UniquePaths](Dynamic-Programming/UniquePaths.js) + * [UniquePaths2](Dynamic-Programming/UniquePaths2.js) * [ZeroOneKnapsack](Dynamic-Programming/ZeroOneKnapsack.js) * **Geometry** + * [Circle](Geometry/Circle.js) + * [Cone](Geometry/Cone.js) * [ConvexHullGraham](Geometry/ConvexHullGraham.js) + * [Pyramid](Geometry/Pyramid.js) + * [Sphere](Geometry/Sphere.js) * **Graphs** * [BellmanFord](Graphs/BellmanFord.js) * [BinaryLifting](Graphs/BinaryLifting.js) @@ -131,6 +140,7 @@ * [FloydWarshall](Graphs/FloydWarshall.js) * [Kosaraju](Graphs/Kosaraju.js) * [KruskalMST](Graphs/KruskalMST.js) + * [LCABinaryLifting](Graphs/LCABinaryLifting.js) * [NodeNeighbors](Graphs/NodeNeighbors.js) * [NumberOfIslands](Graphs/NumberOfIslands.js) * [PrimMST](Graphs/PrimMST.js) @@ -177,6 +187,7 @@ * [FindMinIterator](Maths/FindMinIterator.js) * [GetEuclidGCD](Maths/GetEuclidGCD.js) * [GridGet](Maths/GridGet.js) + * [IntToBase](Maths/IntToBase.js) * [IsDivisible](Maths/IsDivisible.js) * [IsEven](Maths/IsEven.js) * [IsOdd](Maths/IsOdd.js) @@ -219,6 +230,7 @@ * [SimpsonIntegration](Maths/SimpsonIntegration.js) * [Softmax](Maths/Softmax.js) * [SquareRoot](Maths/SquareRoot.js) + * [SquareRootLogarithmic](Maths/SquareRootLogarithmic.js) * [SumOfDigits](Maths/SumOfDigits.js) * [SumOfGeometricProgression](Maths/SumOfGeometricProgression.js) * [TwinPrime](Maths/TwinPrime.js) @@ -341,6 +353,7 @@ * [MaxCharacter](String/MaxCharacter.js) * [MaxWord](String/MaxWord.js) * [PatternMatching](String/PatternMatching.js) + * [PercentageOfLetters](String/PercentageOfLetters.js) * [PermutateString](String/PermutateString.js) * [ReverseString](String/ReverseString.js) * [ReverseWords](String/ReverseWords.js) @@ -349,6 +362,7 @@ * [ValidateCreditCard](String/ValidateCreditCard.js) * [ValidateEmail](String/ValidateEmail.js) * [ValidateUrl](String/ValidateUrl.js) + * [ZFunction](String/ZFunction.js) * **Timing-Functions** * [GetMonthDays](Timing-Functions/GetMonthDays.js) * [IntervalTimer](Timing-Functions/IntervalTimer.js) diff --git a/Maths/test/IntToBase.test.js b/Maths/test/IntToBase.test.js index 002dde6af..9ebcbbf48 100644 --- a/Maths/test/IntToBase.test.js +++ b/Maths/test/IntToBase.test.js @@ -1,4 +1,4 @@ -import { intToBase } from '../intToBase' +import { intToBase } from '../IntToBase' describe('Int to Base', () => { test('Conversion to the binary system', () => { From 9d4adbb0b3742d81fc8950f2ada2e96b4023dfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zendran?= <37419008+zendranm@users.noreply.github.com> Date: Sun, 6 Nov 2022 17:33:11 +0100 Subject: [PATCH 37/44] docs: add new issue templates (#1206) * Adding new issue template * Adding bug issue template * Adding feature request template * Removing feature request template and adding comments * Converting bug template to yaml * Adding new feature request template * CR changes part 1 * CR changes part 2 * CR changes part 3 * CR changes part 4 * CR changes part 5 * Removing specifications section --- .github/ISSUE_TEMPLATE/bug_report.yml | 41 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 31 ++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..e2c4b49ac --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,41 @@ +name: Bug report +description: 'Create a report to help us improve' +title: '[BUG]: ' +labels: ['bug'] +body: + - type: markdown + attributes: + value: '### Before you open an issue, please verify if a similar one already exists or has been closed before. More details about the process of contributing can be found in [CONTRIBUTING.md](https://github.com/TheAlgorithms/JavaScript/blob/master/CONTRIBUTING.md).' + - type: textarea + id: description + attributes: + label: Description + description: Explain what the problem is. + validations: + required: true + - type: textarea + id: expectedbhv + attributes: + label: Expected Behavior + description: Describe what was the expected behavior. + validations: + required: true + - type: textarea + id: actualbhv + attributes: + label: Actual Behavior + description: Describe what actually happens. + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce (if applicable) + description: List steps to reproduce the behavior. + placeholder: | + 1. + 2. + 3. + 4. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..a6ee8bf45 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,31 @@ +name: Feature request +description: 'Suggest features, propose improvements, discuss new ideas' +title: '[FEATURE]: ' +labels: ['enhancement'] +body: + - type: markdown + attributes: + value: | + ## This issue template is not for requesting new algorithms. For new algorithms, PRs should be opened directly. + ## Make sure your issue isn't a duplicate and you follow our [contributing guidelines](https://github.com/TheAlgorithms/JavaScript/blob/master/CONTRIBUTING.md) + - type: textarea + id: description + attributes: + label: Motivation + description: Describe what is the motivation behind this feature. + validations: + required: true + - type: textarea + id: examples + attributes: + label: Examples + description: If possible, provide examples of how this feature can be used. + validations: + required: false + - type: textarea + id: workarounds + attributes: + label: Possible workarounds + description: If possible, describes possible workarounds to this feature. + validations: + required: false From 71d3d4470ffe7bffdb3021397813f581dfa85c6e Mon Sep 17 00:00:00 2001 From: k ho k ho? <6939499+kho-kho-kho@users.noreply.github.com> Date: Sun, 13 Nov 2022 09:41:54 -0800 Subject: [PATCH 38/44] tests: improve for GeneratePermutations (#1263) --- .../tests/GeneratePermutations.test.js | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/Backtracking/tests/GeneratePermutations.test.js b/Backtracking/tests/GeneratePermutations.test.js index 718bf2670..5aa74c72a 100644 --- a/Backtracking/tests/GeneratePermutations.test.js +++ b/Backtracking/tests/GeneratePermutations.test.js @@ -1,14 +1,33 @@ +import { factorial } from '../../Recursive/Factorial' import { permutations } from '../GeneratePermutations' describe('Permutations', () => { + it('Permutations of [a]', () => { + const perms = permutations(['a']) + expect(perms).toHaveLength(factorial(1)) + expect(perms).toContainEqual(['a']) + }) + + it('Permutations of [true, false]', () => { + const perms = permutations([true, false]) + expect(perms).toHaveLength(factorial(2)) + expect(perms).toContainEqual([true, false]) + expect(perms).toContainEqual([false, true]) + }) + it('Permutations of [1, 2, 3]', () => { - expect(permutations([1, 2, 3])).toEqual([ - [1, 2, 3], - [1, 3, 2], - [2, 1, 3], - [2, 3, 1], - [3, 1, 2], - [3, 2, 1] - ]) + const perms = permutations([1, 2, 3]) + expect(perms).toHaveLength(factorial(3)) + expect(perms).toContainEqual([1, 2, 3]) + expect(perms).toContainEqual([1, 3, 2]) + expect(perms).toContainEqual([2, 1, 3]) + expect(perms).toContainEqual([2, 3, 1]) + expect(perms).toContainEqual([3, 1, 2]) + expect(perms).toContainEqual([3, 2, 1]) + }) + + it('Permutation counts across larger input arrays', () => { + expect(permutations([1, 2, 3, 4, 5, 6, 7, 8])).toHaveLength(factorial(8)) + expect(permutations([1, 2, 3, 4, 5, 6, 7, 8, 9])).toHaveLength(factorial(9)) }) }) From d6be3a4e96fcf8e5397485617be90e51d47a25b7 Mon Sep 17 00:00:00 2001 From: Akshay Dubey <38462415+itsAkshayDubey@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:50:38 +0530 Subject: [PATCH 39/44] algorithm: Hexagonal number (#1265) --- Maths/HexagonalNumber.js | 21 +++++++++++++++++++++ Maths/test/HexagonalNumber.test.js | 19 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 Maths/HexagonalNumber.js create mode 100644 Maths/test/HexagonalNumber.test.js diff --git a/Maths/HexagonalNumber.js b/Maths/HexagonalNumber.js new file mode 100644 index 000000000..31fac7ea7 --- /dev/null +++ b/Maths/HexagonalNumber.js @@ -0,0 +1,21 @@ +/* + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * Hexagonal Number: https://en.wikipedia.org/wiki/Hexagonal_number + * The nth hexagonal number hn is the number of distinct dots in a pattern of dots + * consisting of the outlines of regular hexagons with sides up to n dots, when the + * hexagons are overlaid so that they share one vertex. + */ + +/** + * @function hexagonalNumber + * @description -> returns nth hexagonal number + * @param {Integer} number + * @returns {Integer} nth hexagonal number + */ + +export const hexagonalNumber = (number) => { + if (number <= 0) { + throw new Error('Number must be greater than zero.') + } + return number * (2 * number - 1) +} diff --git a/Maths/test/HexagonalNumber.test.js b/Maths/test/HexagonalNumber.test.js new file mode 100644 index 000000000..ebfc1cc73 --- /dev/null +++ b/Maths/test/HexagonalNumber.test.js @@ -0,0 +1,19 @@ +import { hexagonalNumber } from '../HexagonalNumber' + +const expectedValuesArray = [1, 6, 15, 28, 45, 66, 91, 120, 153, 190, 231, 276, 325, 378, 435, 496, 561, 630, 703, 780, 861, 946] + +describe('Testing hexagonalNumber', () => { + for (let i = 1; i <= 22; i++) { + it('Testing for number = ' + i + ', should return ' + expectedValuesArray[i], () => { + expect(hexagonalNumber(i)).toBe(expectedValuesArray[i - 1]) + }) + } + + it('should throw error when supplied negative numbers', () => { + expect(() => { hexagonalNumber(-1) }).toThrow(Error) + }) + + it('should throw error when supplied zero', () => { + expect(() => { hexagonalNumber(0) }).toThrow(Error) + }) +}) From 18a91573c3f0bcf891463009dd550a5dd8acf7a7 Mon Sep 17 00:00:00 2001 From: fun-guava <118941851+fun-guava@users.noreply.github.com> Date: Wed, 30 Nov 2022 12:21:08 +0100 Subject: [PATCH 40/44] Added a new Maths algorithm to determine if two non-null integers are "friendly numbers" (#1267) --- Maths/FriendlyNumbers.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Maths/FriendlyNumbers.js diff --git a/Maths/FriendlyNumbers.js b/Maths/FriendlyNumbers.js new file mode 100644 index 000000000..a9b6b37ec --- /dev/null +++ b/Maths/FriendlyNumbers.js @@ -0,0 +1,32 @@ +/* + 'In number theory, friendly numbers are two or more natural numbers with a common abundancy index, the + ratio between the sum of divisors of a number and the number itself.' + Source: https://en.wikipedia.org/wiki/Friendly_number + See also: https://mathworld.wolfram.com/FriendlyNumber.html#:~:text=The%20numbers%20known%20to%20be,numbers%20have%20a%20positive%20density. +*/ + +export const FriendlyNumbers = (firstNumber, secondNumber) => { + // input: two integers + // output: true if the two integers are friendly numbers, false if they are not friendly numbers + + // First, check that the parameters are valid + if (!Number.isInteger(firstNumber) || !Number.isInteger(secondNumber) || firstNumber === 0 || secondNumber === 0 || firstNumber === secondNumber) { + throw new Error('The two parameters must be distinct, non-null integers') + } + + return abundancyIndex(firstNumber) === abundancyIndex(secondNumber) +} + +function abundancyIndex (number) { + return sumDivisors(number) / number +} + +function sumDivisors (number) { + let runningSumDivisors = number + for (let i = 0; i < number / 2; i++) { + if (Number.isInteger(number / i)) { + runningSumDivisors += i + } + } + return runningSumDivisors +} From f954fb1ff72ed00b8a53a282e081798213bb40f7 Mon Sep 17 00:00:00 2001 From: Jesus Salomon <80434233+salomonj11@users.noreply.github.com> Date: Thu, 8 Dec 2022 07:26:10 -0600 Subject: [PATCH 41/44] Update README.md (#1268) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 25e23a695..8d857bda8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ JavaScript Repository of TheAlgorithms, which implements various algorithms and Before contributing to this repository, make sure to read our [Contribution Guidelines](CONTRIBUTING.md). You can look at other [TheAlgorithms Repositories][repositories] or the [issues with a "help wanted" label][help-wanted] for inspiration regarding what to implement. Our maintainers will guide you through how to make your contribution properly -if you make any mistakes. The names of the maintainers of this repository is listed in the +if you make any mistakes. The names of the maintainers of this repository are listed in the [CODEOWNERS file](.github/CODEOWNERS). You can find a list of the algorithms currently in the repository in the [directory](DIRECTORY.md). Explanations of From 65cceae0befbae1c3edd6bc5cd4d3cedb767600f Mon Sep 17 00:00:00 2001 From: Defective Detective <71999854+SpiderMath@users.noreply.github.com> Date: Thu, 8 Dec 2022 18:57:42 +0530 Subject: [PATCH 42/44] algorithm: signum (#1266) --- Maths/Signum.js | 25 +++++++++++++++++++++++++ Maths/test/Signum.test.js | 19 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 Maths/Signum.js create mode 100644 Maths/test/Signum.test.js diff --git a/Maths/Signum.js b/Maths/Signum.js new file mode 100644 index 000000000..35047cac1 --- /dev/null +++ b/Maths/Signum.js @@ -0,0 +1,25 @@ +/* + A program to demonstrate the implementation of the signum function, + also known as the sign function, in JavaScript. + + The signum function is an odd mathematical function, which returns the + sign of the provided real number. + It can return 3 values: 1 for values greater than zero, 0 for zero itself, + and -1 for values less than zero + + Wikipedia: https://en.wikipedia.org/wiki/Sign_function +*/ + +/** + * @param {Number} input + * @returns {-1 | 0 | 1 | NaN} sign of input (and NaN if the input is not a number) + */ +function signum (input) { + if (input === 0) return 0 + if (input > 0) return 1 + if (input < 0) return -1 + + return NaN +} + +export { signum } diff --git a/Maths/test/Signum.test.js b/Maths/test/Signum.test.js new file mode 100644 index 000000000..ac00676a0 --- /dev/null +++ b/Maths/test/Signum.test.js @@ -0,0 +1,19 @@ +import { signum } from '../Signum' + +describe('The sign of a number', () => { + it('Sign of 10', () => { + expect(signum(10)).toBe(1) + }) + + it('Sign of 0', () => { + expect(signum(0)).toBe(0) + }) + + it('Sign of -420', () => { + expect(signum(-420)).toBe(-1) + }) + + it('Sign of NaN', () => { + expect(signum(NaN)).toBe(NaN) + }) +}) From b36b45888ea5210ea130c3ebf073607ed230c9e2 Mon Sep 17 00:00:00 2001 From: David Leal Date: Sat, 17 Dec 2022 00:22:15 -0600 Subject: [PATCH 43/44] docs: fix CI and remove LGTM badges (#1269) * Updated Documentation in README.md * docs: fix CI and remove LGTM badges LGTM is no longer a supported service and has been integrated with GitHub (which is now CodeQL). We can create a CodeQL workflow and integrate it into the repository, however, that fits for another PR. The shields badge API was updated, thus, causing an invalid badge: https://github.com/badges/shields/issues/8671 Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 3 +++ README.md | 7 ++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 2d54a17f8..ca30d6582 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -185,8 +185,10 @@ * [FindMaxRecursion](Maths/FindMaxRecursion.js) * [FindMin](Maths/FindMin.js) * [FindMinIterator](Maths/FindMinIterator.js) + * [FriendlyNumbers](Maths/FriendlyNumbers.js) * [GetEuclidGCD](Maths/GetEuclidGCD.js) * [GridGet](Maths/GridGet.js) + * [HexagonalNumber](Maths/HexagonalNumber.js) * [IntToBase](Maths/IntToBase.js) * [IsDivisible](Maths/IsDivisible.js) * [IsEven](Maths/IsEven.js) @@ -227,6 +229,7 @@ * [ShorsAlgorithm](Maths/ShorsAlgorithm.js) * [SieveOfEratosthenes](Maths/SieveOfEratosthenes.js) * [SieveOfEratosthenesIntArray](Maths/SieveOfEratosthenesIntArray.js) + * [Signum](Maths/Signum.js) * [SimpsonIntegration](Maths/SimpsonIntegration.js) * [Softmax](Maths/Softmax.js) * [SquareRoot](Maths/SquareRoot.js) diff --git a/README.md b/README.md index 8d857bda8..758bb5a04 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ JavaScript Repository of TheAlgorithms, which implements various algorithms and [![JavaScript Banner][banner]](DIRECTORY.md) - [![Language grade: JavaScript][grade]][lgtm] [![Checks][checks]][actions] [![Contributions Welcome][welcome]](CONTRIBUTING.md) [![standard.js][standard-logo]][standard-js] @@ -47,8 +46,8 @@ many of the algorithms can be found in the [wiki][explanation]. [standard-logo]: https://img.shields.io/badge/code%20style-standardjs-%23f3df49 [chat]: https://img.shields.io/discord/808045925556682782.svg?logo=discord&colorB=7289DA [welcome]: https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3 -[checks]: https://img.shields.io/github/workflow/status/TheAlgorithms/JavaScript/Node%20CI?label=checks -[grade]: https://img.shields.io/lgtm/grade/javascript/g/TheAlgorithms/Javascript.svg?logo=lgtm&logoWidth=18 +[checks]: https://img.shields.io/github/actions/workflow/status/TheAlgorithms/JavaScript/Ci.yml?branch=master&label=checks +[grade]: https://img.shields.io/lgtm/grade/javascript/g/TheAlgorithms/JavaScript.svg?logo=lgtm&logoWidth=18 [standard-js]: https://standardjs.com/ @@ -56,6 +55,4 @@ many of the algorithms can be found in the [wiki][explanation]. [actions]: https://github.com/TheAlgorithms/JavaScript/actions [explanation]: https://github.com/TheAlgorithms/JavaScript/wiki [repositories]: https://github.com/orgs/TheAlgorithms/repositories -[lgtm]: https://lgtm.com/projects/g/TheAlgorithms/Javascript/context:javascript [help-wanted]: https://github.com/TheAlgorithms/JavaScript/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22 - From cb4c40e95c58dff028a32532c283b22298f0638e Mon Sep 17 00:00:00 2001 From: David Leal Date: Sun, 18 Dec 2022 04:20:06 -0600 Subject: [PATCH 44/44] docs: improve the contributing guidelines (#1271) --- CONTRIBUTING.md | 60 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8415cfe5e..3b85427d0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,9 @@ ## Before contributing -Welcome to [TheAlgorithms/Javascript](https://github.com/TheAlgorithms/Javascript)! Before sending your pull requests, -make sure that you **read the whole guidelines**. If you have any doubt on the contributing guide, please feel free to -[state it clearly in an issue](https://github.com/TheAlgorithms/Javascript/issues/new). +Welcome to [TheAlgorithms/JavaScript](https://github.com/TheAlgorithms/JavaScript)! Before sending your pull requests, +make sure that you **read the whole guidelines**. If you have any doubts about the contributing guide, please feel free to +[state them clearly in an issue](https://github.com/TheAlgorithms/JavaScript/issues/new) or by joining our [Discord community](https://the-algorithms.com/discord). ## Contributing @@ -15,11 +15,11 @@ referenced and used by learners from around the globe. Being one of our contribu - You did your work - plagiarism is not allowed. - Any plagiarized work will not be merged. -- Your work will be distributed under [GNU License](LICENSE) once your pull request is merged. +- Your work will be distributed under the [GNU GPLv3.0](https://github.com/TheAlgorithms/JavaScript/blob/master/LICENSE) once your pull request is merged. - Your submitted work must fulfill our styles and standards. -**New implementation** is welcome! For example, new solutions to a problem, different representations of a graph data -structure or algorithm designs with different complexity. +**New implementations** are welcome! For example, new solutions to a problem, different representations of a graph data +structure, or algorithm designs with different complexity. **Improving comments** and **writing proper tests** are also highly welcome. @@ -52,18 +52,38 @@ Algorithms should: Algorithms in this repo should not be how-to examples for existing JavaScript packages. Instead, they should perform internal calculations or manipulations to convert input values into different output values. Those calculations or manipulations can use data types, classes, or functions of existing JavaScript packages but each algorithm in this repo -should add unique value. +should add a unique value. + +#### Commit guidelines + +- Follow [**Conventional Commits**](https://www.conventionalcommits.org/en/v1.0.0/) guidelines at all times. +- Use one of the following prefixes (there might be other miscellaneous prefixes, though). + - fix: A bug fix in an algorithm, workflow, configuration/settings, etc.. + - feat: A new feature, such as new algorithms, new workflows, etc.. + - docs: Documentation changes or fixes, like improving the contributing guidelines, fixing a typo, etc.. + - test: Correct existing tests or add new ones. + - chore: Miscellaneous changes that do not match any of the above. + +Examples of best commit messages. + +```txt +fix: fixed error in XYZ algorithm +feat: re-work the CI workflow +docs: improve the contributing guidelines +test: add self-tests for XYZ algorithm +chore: update readme badges +``` #### File Naming Convention -- filenames should use the UpperCamelCase (PascalCase) style. +- Filenames should use the UpperCamelCase (PascalCase) style. - There should be no spaces in filenames. - **Example:** `UserProfile.js` is allowed but `userprofile.js`,`Userprofile.js`,`user-Profile.js`,`userProfile.js` are not. #### Module System -We use the [ES Module](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) system, which bring an +We use the [ES Module](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) system, which brings an official, standardized module system to JavaScript. It roughly means you will need to use `export` and `import` statements instead of `module.exports` and `require()`. @@ -83,34 +103,34 @@ and inspect the outcome. Example: [RatInAMaze.test.js](Backtracking/tests/RatInA Please refrain from using `console` in your implementation AND test code. -First you should install all dependencies using: +First, you should install all dependencies using: -```shell +```bash npm install ``` You can (and should!) run all tests locally before committing your changes: -```shell +```bash npm test ``` -If you want save some time and just run a specific test: +If you want to save some time and just run a specific test: -```shell +```bash # This will run any test file where the filename contains "koch" (no need to specify folder path) npm test -- koch ``` You can also start Jest in "watch" mode: -```shell +```bash npm test -- --watchAll ``` We also prepared a helper script that runs tests only for changed files: -```shell +```bash npm run test-changed ``` @@ -121,9 +141,9 @@ This will run all tests and watch source and test files for changes. When a chan To maximize the readability and correctness of our code, we require that new submissions follow the [JavaScript Standard Style](https://standardjs.com/). -Before committing, please run +Before committing, please run: -```shell +```bash npm run style ``` @@ -134,7 +154,7 @@ A few (but not all) of the things to keep in mind: - Use camelCase with the leading character as lowercase for identifier names (variables and functions). - Names start with a letter. -- Follow code indentation: Always use 2 spaces for indentation of code blocks. +- Follow code indentation: Always use 2 spaces for code-block indentation. ```js function sumOfArray(arrayOfNumbers) { @@ -158,4 +178,4 @@ function sumOfArray(arrayOfNumbers) { - **Be consistent in the use of these guidelines when submitting.** - Happy coding! -Writer [@itsvinayak](https://github.com/itsvinayak), May 2020. +Writer [@itsvinayak](https://github.com/itsvinayak) and contributors, May 2020.