From 40e117ce29c1da36b6cb3476df13c1cf27a37dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:08:08 -0400 Subject: [PATCH 01/11] Added comments, fixed negatives --- src/algorithms/math/bits/isPowerOfTwo.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/algorithms/math/bits/isPowerOfTwo.js b/src/algorithms/math/bits/isPowerOfTwo.js index 28e3e673..22d89831 100644 --- a/src/algorithms/math/bits/isPowerOfTwo.js +++ b/src/algorithms/math/bits/isPowerOfTwo.js @@ -1,7 +1,25 @@ /** * @param {number} number - * @return bool + * @return {boolean} */ export default function isPowerOfTwo(number) { + // 1 (2^0) is the smallest power of two. + if (number < 1) { + return false; + } + /* + * Powers of two in binary look like this: + * 1: 0001 + * 2: 0010 + * 4: 0100 + * 8: 1000 + * + * Note that there is always exactly 1 bit set. The only exception is with a signed integer. + * e.g. An 8-bit signed integer with a value of -128 looks like: + * 10000000 + * + * So after checking that the number is greater than zero, we can use a clever little bit + * hack to test that one and only one bit is set. + */ return (number & (number - 1)) === 0; } From a1d3904acefed4cb60bb37507ea4a40645267c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:08:45 -0400 Subject: [PATCH 02/11] Delete isPowerOfTwoBitwise.js --- .../is-power-of-two/isPowerOfTwoBitwise.js | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 src/algorithms/math/is-power-of-two/isPowerOfTwoBitwise.js diff --git a/src/algorithms/math/is-power-of-two/isPowerOfTwoBitwise.js b/src/algorithms/math/is-power-of-two/isPowerOfTwoBitwise.js deleted file mode 100644 index af117735..00000000 --- a/src/algorithms/math/is-power-of-two/isPowerOfTwoBitwise.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @param {number} number - * @return {boolean} - */ -export default function isPowerOfTwoBitwise(number) { - // 1 (2^0) is the smallest power of two. - if (number < 1) { - return false; - } - - /* - * Powers of two in binary look like this: - * 1: 0001 - * 2: 0010 - * 4: 0100 - * 8: 1000 - * - * Note that there is always exactly 1 bit set. The only exception is with a signed integer. - * e.g. An 8-bit signed integer with a value of -128 looks like: - * 10000000 - * - * So after checking that the number is greater than zero, we can use a clever little bit - * hack to test that one and only one bit is set. - */ - return (number & (number - 1)) === 0; -} From 65d012c4167072fa6e9c2870653b0d21c8c01eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:09:15 -0400 Subject: [PATCH 03/11] Delete isPowerOfTwoBitwise.test.js --- .../__test__/isPowerOfTwoBitwise.test.js | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 src/algorithms/math/is-power-of-two/__test__/isPowerOfTwoBitwise.test.js diff --git a/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwoBitwise.test.js b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwoBitwise.test.js deleted file mode 100644 index bcba3a60..00000000 --- a/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwoBitwise.test.js +++ /dev/null @@ -1,23 +0,0 @@ -import isPowerOfTwoBitwise from '../isPowerOfTwoBitwise'; - -describe('isPowerOfTwoBitwise', () => { - it('should check if the number is made by multiplying twos', () => { - expect(isPowerOfTwoBitwise(-1)).toBe(false); - expect(isPowerOfTwoBitwise(0)).toBe(false); - expect(isPowerOfTwoBitwise(1)).toBe(true); - expect(isPowerOfTwoBitwise(2)).toBe(true); - expect(isPowerOfTwoBitwise(3)).toBe(false); - expect(isPowerOfTwoBitwise(4)).toBe(true); - expect(isPowerOfTwoBitwise(5)).toBe(false); - expect(isPowerOfTwoBitwise(6)).toBe(false); - expect(isPowerOfTwoBitwise(7)).toBe(false); - expect(isPowerOfTwoBitwise(8)).toBe(true); - expect(isPowerOfTwoBitwise(10)).toBe(false); - expect(isPowerOfTwoBitwise(12)).toBe(false); - expect(isPowerOfTwoBitwise(16)).toBe(true); - expect(isPowerOfTwoBitwise(31)).toBe(false); - expect(isPowerOfTwoBitwise(64)).toBe(true); - expect(isPowerOfTwoBitwise(1024)).toBe(true); - expect(isPowerOfTwoBitwise(1023)).toBe(false); - }); -}); From bd6ca196572fb5c60e9a6795bb8e9d3af62d4dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:12:21 -0400 Subject: [PATCH 04/11] Update README.md --- src/algorithms/math/is-power-of-two/README.md | 41 ++----------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/src/algorithms/math/is-power-of-two/README.md b/src/algorithms/math/is-power-of-two/README.md index 6ed1e925..518ef85f 100644 --- a/src/algorithms/math/is-power-of-two/README.md +++ b/src/algorithms/math/is-power-of-two/README.md @@ -1,48 +1,15 @@ # Is a power of two Given a positive integer, write a function to find if it is -a power of two or not. +a power of the number 2, or not. -**Naive solution** +**Standard solution** -In naive solution we just keep dividing the number by two +We just keep dividing the number by two unless the number becomes `1` and every time we do so, we check that remainder after division is always `0`. Otherwise, the number can't be a power of two. -**Bitwise solution** - -Powers of two in binary form always have just one bit set. -The only exception is with a signed integer (e.g. an 8-bit -signed integer with a value of -128 looks like: `10000000`) - -``` -1: 0001 -2: 0010 -4: 0100 -8: 1000 -``` - -So after checking that the number is greater than zero, -we can use a bitwise hack to test that one and only one -bit is set. - -``` -number & (number - 1) -``` - -For example for number `8` that operations will look like: - -``` - 1000 -- 0001 - ---- - 0111 - - 1000 -& 0111 - ---- - 0000 -``` +**[Bitwise solution](https://github.com/Rudxain/javascript-algorithms/blob/master/src/algorithms/math/bits/isPowerOfTwo.js)** ## References From b09b3d296fe19fbef774ab1fe64cc3bf60d9da91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:18:05 -0400 Subject: [PATCH 05/11] Update isPowerOfTwo.js --- src/algorithms/math/bits/isPowerOfTwo.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/algorithms/math/bits/isPowerOfTwo.js b/src/algorithms/math/bits/isPowerOfTwo.js index 22d89831..effd04bd 100644 --- a/src/algorithms/math/bits/isPowerOfTwo.js +++ b/src/algorithms/math/bits/isPowerOfTwo.js @@ -20,6 +20,18 @@ export default function isPowerOfTwo(number) { * * So after checking that the number is greater than zero, we can use a clever little bit * hack to test that one and only one bit is set. + * + * For example for number `8` the operations will look like: + * + * 1000 + * - 0001 + * ---- + * 0111 + * + * 1000 + * & 0111 + * ---- + * 0000 */ return (number & (number - 1)) === 0; } From 6ca3055125f14c0bde6bda006200ee7c3d28defa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:21:40 -0400 Subject: [PATCH 06/11] Update README.md --- src/algorithms/math/is-power-of-two/README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/algorithms/math/is-power-of-two/README.md b/src/algorithms/math/is-power-of-two/README.md index 518ef85f..fcb40466 100644 --- a/src/algorithms/math/is-power-of-two/README.md +++ b/src/algorithms/math/is-power-of-two/README.md @@ -1,18 +1,15 @@ # Is a power of two -Given a positive integer, write a function to find if it is -a power of the number 2, or not. +Given a Real number, write a function to find if it is an integer power of the number 2, or not. **Standard solution** -We just keep dividing the number by two -unless the number becomes `1` and every time we do so, we -check that remainder after division is always `0`. Otherwise, the number can't be a power of two. +We just keep dividing the number by two, unless the number becomes `1` and every time we do so, +we check that remainder after division is always `0`. Otherwise, the number can't be a power of two. **[Bitwise solution](https://github.com/Rudxain/javascript-algorithms/blob/master/src/algorithms/math/bits/isPowerOfTwo.js)** ## References - [GeeksForGeeks](https://www.geeksforgeeks.org/program-to-find-whether-a-no-is-power-of-two/) -- [Bitwise Solution on Stanford](http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2) - [Binary number subtraction on YouTube](https://www.youtube.com/watch?v=S9LJknZTyos&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=66) From 8af284195f846644542cfa6cddbb841b66154e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:22:32 -0400 Subject: [PATCH 07/11] Update isPowerOfTwo.js --- src/algorithms/math/bits/isPowerOfTwo.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/algorithms/math/bits/isPowerOfTwo.js b/src/algorithms/math/bits/isPowerOfTwo.js index effd04bd..e9212d12 100644 --- a/src/algorithms/math/bits/isPowerOfTwo.js +++ b/src/algorithms/math/bits/isPowerOfTwo.js @@ -32,6 +32,8 @@ export default function isPowerOfTwo(number) { * & 0111 * ---- * 0000 + * + * References: http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 */ return (number & (number - 1)) === 0; } From fce580f67f7ac6d82238d16f32f761cfce94241e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:25:35 -0400 Subject: [PATCH 08/11] Added more tests --- src/algorithms/math/bits/__test__/isPowerOfTwo.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/algorithms/math/bits/__test__/isPowerOfTwo.test.js b/src/algorithms/math/bits/__test__/isPowerOfTwo.test.js index 3408f2c1..741771e3 100644 --- a/src/algorithms/math/bits/__test__/isPowerOfTwo.test.js +++ b/src/algorithms/math/bits/__test__/isPowerOfTwo.test.js @@ -1,7 +1,9 @@ import isPowerOfTwo from '../isPowerOfTwo'; describe('isPowerOfTwo', () => { - it('should detect if the number is power of two', () => { + it('should detect if the int is power of two', () => { + expect(isPowerOfTwo(-32)).toBe(false); + expect(isPowerOfTwo(-1)).toBe(false); expect(isPowerOfTwo(1)).toBe(true); expect(isPowerOfTwo(2)).toBe(true); expect(isPowerOfTwo(3)).toBe(false); @@ -16,5 +18,6 @@ describe('isPowerOfTwo', () => { expect(isPowerOfTwo(32)).toBe(true); expect(isPowerOfTwo(127)).toBe(false); expect(isPowerOfTwo(128)).toBe(true); + expect(isPowerOfTwo(2 ** 30)).toBe(true); }); }); From ef8d0672b6926593783f3df1bfdb2e7ad8bef4de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Mon, 30 May 2022 22:32:32 -0400 Subject: [PATCH 09/11] Added more tests --- .../is-power-of-two/__test__/isPowerOfTwo.test.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js index d1271bab..e0c4a5ea 100644 --- a/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js +++ b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js @@ -2,11 +2,15 @@ import isPowerOfTwo from '../isPowerOfTwo'; describe('isPowerOfTwo', () => { it('should check if the number is made by multiplying twos', () => { + expect(isPowerOfTwo(-8.5)).toBe(false); expect(isPowerOfTwo(-1)).toBe(false); expect(isPowerOfTwo(0)).toBe(false); + expect(isPowerOfTwo(0.5)).toBe(false); expect(isPowerOfTwo(1)).toBe(true); expect(isPowerOfTwo(2)).toBe(true); + expect(isPowerOfTwo(Math.E)).toBe(false); expect(isPowerOfTwo(3)).toBe(false); + expect(isPowerOfTwo(Math.PI)).toBe(false); expect(isPowerOfTwo(4)).toBe(true); expect(isPowerOfTwo(5)).toBe(false); expect(isPowerOfTwo(6)).toBe(false); @@ -17,7 +21,13 @@ describe('isPowerOfTwo', () => { expect(isPowerOfTwo(16)).toBe(true); expect(isPowerOfTwo(31)).toBe(false); expect(isPowerOfTwo(64)).toBe(true); - expect(isPowerOfTwo(1024)).toBe(true); expect(isPowerOfTwo(1023)).toBe(false); + expect(isPowerOfTwo(1024)).toBe(true); + expect(isPowerOfTwo(2 ** 32 - 1)).toBe(false); + expect(isPowerOfTwo(2 ** 32)).toBe(true); + expect(isPowerOfTwo(2 ** 64)).toBe(true); + expect(isPowerOfTwo(2 ** 69)).toBe(true); //nice, lol + expect(isPowerOfTwo(2 ** 0x3ff)).toBe(true); //closest to `Number.MAX_VALUE` + expect(isPowerOfTwo(Infinity)).toBe(false); }); }); From 3e338da514c1e17ff5084bf4bdf156204cdc240f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Sat, 11 Jun 2022 09:52:11 -0400 Subject: [PATCH 10/11] Added `BigInt` support and did some refactoring --- .../math/is-power-of-two/isPowerOfTwo.js | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/algorithms/math/is-power-of-two/isPowerOfTwo.js b/src/algorithms/math/is-power-of-two/isPowerOfTwo.js index 6f7590dd..077dc383 100644 --- a/src/algorithms/math/is-power-of-two/isPowerOfTwo.js +++ b/src/algorithms/math/is-power-of-two/isPowerOfTwo.js @@ -1,24 +1,37 @@ /** - * @param {number} number + * @param {number|bigint} numeric value to check * @return {boolean} */ -export default function isPowerOfTwo(number) { +export default function isPowerOfTwo(x) { // 1 (2^0) is the smallest power of two. - if (number < 1) { - return false; + if (x <= 1) { + return x == 1; + } + + // guard clause to dispatch BigInts ASAP + if (typeof x == "bigint") { + // the bitwise alt works for any BigInt + // because bit operators are arbitrary-precision for them + return (x & (x - 1n)) === 0n; } // Let's find out if we can divide the number by two // many times without remainder. - let dividedNumber = number; - while (dividedNumber !== 1) { - if (dividedNumber % 2 !== 0) { - // For every case when remainder isn't zero we can say that this number - // couldn't be a result of power of two. + let dividedNum = x / 2; + while (dividedNum !== 1) { + if ( !Number.isInteger(dividedNum) ) { + /* + For every case when quotient is non-int we can say that this number + couldn't be a result of power of two (with integer exponent), + because checking the fractional part of a quotient is equivalent to checking if the remainder is 0, + and the remainder is the value of the least significant bit (with fractional part included). + All powers of 2 (but `1`) must have consecutive trailing zeros. + this also immediately dispatches non-int values of `x`, because neither rem nor quotient is an int + */ return false; } - dividedNumber /= 2; + dividedNum /= 2; } return true; From 1ea5d556c73f94f3b0a7cba1991d9a766a56fb5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Sat, 11 Jun 2022 10:16:58 -0400 Subject: [PATCH 11/11] Refactored and added `BigInt`s --- .../__test__/isPowerOfTwo.test.js | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js index e0c4a5ea..d6ca5abf 100644 --- a/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js +++ b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js @@ -1,33 +1,27 @@ -import isPowerOfTwo from '../isPowerOfTwo'; +import f from '../isPowerOfTwo'; describe('isPowerOfTwo', () => { it('should check if the number is made by multiplying twos', () => { - expect(isPowerOfTwo(-8.5)).toBe(false); - expect(isPowerOfTwo(-1)).toBe(false); - expect(isPowerOfTwo(0)).toBe(false); - expect(isPowerOfTwo(0.5)).toBe(false); - expect(isPowerOfTwo(1)).toBe(true); - expect(isPowerOfTwo(2)).toBe(true); - expect(isPowerOfTwo(Math.E)).toBe(false); - expect(isPowerOfTwo(3)).toBe(false); - expect(isPowerOfTwo(Math.PI)).toBe(false); - expect(isPowerOfTwo(4)).toBe(true); - expect(isPowerOfTwo(5)).toBe(false); - expect(isPowerOfTwo(6)).toBe(false); - expect(isPowerOfTwo(7)).toBe(false); - expect(isPowerOfTwo(8)).toBe(true); - expect(isPowerOfTwo(10)).toBe(false); - expect(isPowerOfTwo(12)).toBe(false); - expect(isPowerOfTwo(16)).toBe(true); - expect(isPowerOfTwo(31)).toBe(false); - expect(isPowerOfTwo(64)).toBe(true); - expect(isPowerOfTwo(1023)).toBe(false); - expect(isPowerOfTwo(1024)).toBe(true); - expect(isPowerOfTwo(2 ** 32 - 1)).toBe(false); - expect(isPowerOfTwo(2 ** 32)).toBe(true); - expect(isPowerOfTwo(2 ** 64)).toBe(true); - expect(isPowerOfTwo(2 ** 69)).toBe(true); //nice, lol - expect(isPowerOfTwo(2 ** 0x3ff)).toBe(true); //closest to `Number.MAX_VALUE` - expect(isPowerOfTwo(Infinity)).toBe(false); + // test all pows until it gets as close as possible to `Number.MAX_VALUE` + for (let i = 0; i <= 0x3ff; i++) { + expect( f(2 ** i) && f(1n << BigInt(i)) ).toBe(true) + } + expect(f(-8.5)).toBe(false); + expect(f(-1) || f(-1n)).toBe(false); + expect(f(0) || f(0n)).toBe(false); + expect(f(0.5)).toBe(false); + expect(f(Math.E)).toBe(false); + expect(f(3) || f(3n)).toBe(false); + expect(f(Math.PI)).toBe(false); + expect(f(5) || f(5n)).toBe(false); + expect(f(6) || f(6n)).toBe(false); + expect(f(7) || f(7n)).toBe(false); + expect(f(10) || f(10n)).toBe(false); + expect(f(12) || f(12n)).toBe(false); + expect(f(31) || f(31n)).toBe(false); + expect(f(1023) || f(1023n)).toBe(false); + expect( f(2 ** 32 - 1) || f(~(-1n << 32n)) ).toBe(false); + expect(f(Infinity) || f(-Infinity)).toBe(false); + expect(f(NaN)).toBe(false); }); });