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); }); }); diff --git a/src/algorithms/math/bits/isPowerOfTwo.js b/src/algorithms/math/bits/isPowerOfTwo.js index 28e3e673..e9212d12 100644 --- a/src/algorithms/math/bits/isPowerOfTwo.js +++ b/src/algorithms/math/bits/isPowerOfTwo.js @@ -1,7 +1,39 @@ /** * @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. + * + * For example for number `8` the operations will look like: + * + * 1000 + * - 0001 + * ---- + * 0111 + * + * 1000 + * & 0111 + * ---- + * 0000 + * + * References: http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 + */ return (number & (number - 1)) === 0; } diff --git a/src/algorithms/math/is-power-of-two/README.md b/src/algorithms/math/is-power-of-two/README.md index 6ed1e925..fcb40466 100644 --- a/src/algorithms/math/is-power-of-two/README.md +++ b/src/algorithms/math/is-power-of-two/README.md @@ -1,51 +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. +Given a Real number, write a function to find if it is an integer power of the number 2, or not. -**Naive solution** +**Standard solution** -In naive 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** - -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 - [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) 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..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,23 +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(-1)).toBe(false); - expect(isPowerOfTwo(0)).toBe(false); - expect(isPowerOfTwo(1)).toBe(true); - expect(isPowerOfTwo(2)).toBe(true); - expect(isPowerOfTwo(3)).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(1024)).toBe(true); - expect(isPowerOfTwo(1023)).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); }); }); 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); - }); -}); 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; 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; -}