From c7d91359da1b9abbfdda520838f045de6731b235 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Mon, 27 May 2024 20:41:41 -0400 Subject: [PATCH 01/23] Create multibit_manipulation.py --- bit_manipulation/multibit_manipulation.py | 236 ++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 bit_manipulation/multibit_manipulation.py diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py new file mode 100644 index 000000000000..8a03601ebe7d --- /dev/null +++ b/bit_manipulation/multibit_manipulation.py @@ -0,0 +1,236 @@ +"""Bit integer manipulation, both single bit and multi-bit list-like + slicing functions ( get, set, insert, remove ) implemented with + builtin bitwise operations. + +See: +https://high-python-ext-3-algorithms.readthedocs.io/ko/latest/chapter5.html#insert-bit +https://en.wikipedia.org/wiki/Bit_manipulation#Bit_manipulation_operations +https://github.com/billbreit/BitWiseApps + +All parameters must be must be int >= 0, referred to as a 'bit integer'. + + bint:int + The bit integer to be accessed or returned as modified. + + index:int + The offset into the bit position from right, + 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian + For inserts, index is the position to the right of index, + index 0 -> right of rightmost bit. + For gets, sets and removes, it is the position of the bit itself. + + value:int + Either [0,1] for single bit, or bit mask, bit_length(value) <= bitlen. + + bitlen:int + The effective mask length, spec. leading zeros + ( bitlen 4 value 1 -> 0001 ) + +The bitwise expressions may look convoluted, but basically, there are +just three parts: left-hand side, value, right-hand side. + +For example, say you want to insert two ones in the middle of 0b101101, +that is -> 0b10111101. Index is 2 ( 0 ,1, 2 from the right ) and the +value is 3 (0b11) with a bit length of 2. + +- Shift >> index right to produce 0b101 +- Shift left << bit length to produce 0b10100. +- OR in the ones producing 0b10111. +- Left shift << index producing 0b10111000. +- Using a bit mask (1 << index)-1 -> 0b111, AND with the original bint, + ( 0b101101 & 0b111 ) -> 0b101. +- OR that into the working 0b10111000, that is, ( 0b10111000 | 0b101 ) + -> 0b10111101. + +To remove the center two bits of 0b101101 -> 0b1001, the proces is mostly +the same, but ... + +- The initial right shift is index(2) + bit_length(2), taking out the two + middle bits and producing 0b10. +- The left shift of index produces 0b1000. +- The original bint is ANDed with bitmask 0b11 producing 0b01 which is + ORed with 0b1000 yielding the target 0b1001. + +It's not so bad once you get the hang of it. + +Various bit insert/remove solutions exist using bin() string functions +and slicing, but this bitwise implementation is significantly faster +(about 3x) on Python for big ints (2^100). + +See https://github.com/billbreit/BitWiseApps/blob/main/dev/time_ops.py + + """ + +bit_length = int.bit_length + +"""The only consistent error checking is for bint < 0 or index < 0 etc., + and for bit_length(value) > bit_len, which can cause silent errors. + Anything like int(None) is going to cause a loud error. """ + + +def bit_get(bint: int, index: int): + """Get value of bit at index in bint. + + >>> bit_get(15, 0) + 1 + >>> bit_get(15, 4) + 0 + >>> bit_get(0, 4) + 0 + >>> bit_get(-1, 2) is None + True + >>> bit_get(0, -1) is None + True + """ + + return multibit_get(bint, index, 1) + + +def bit_set(bint: int, index: int, value: int = 1): + """Set bit at index to value 1 or 0, like set() or unset(). + + >>> bit_set(15, 0, 0) + 14 + >>> bit_set(15, 4, 1) + 31 + >>> bit_set(31, 6, 0) + 31 + >>> bit_set(31, 6, 3) is None + True + """ + + if value not in [0, 1]: + return None # error + + return multibit_set(bint, index, 1, value) + + +def bit_insert(bint: int, index: int, value: int = 1): + """Insert bit value before index. + + >>> bit_insert(15, 0, 0) + 30 + >>> bit_insert(15, 0, 1) + 31 + >>> bit_insert(15, 4, 1) + 31 + >>> bit_insert(31, 6, 0) + 31 + """ + + if value not in [0, 1]: + return None # error + + return multibit_insert(bint, index, 1, value) + + +def bit_remove(bint: int, index: int) -> int: + """Remove the bit at index from bint. + + >>> bit_remove(15, 0) + 7 + >>> bit_remove(15, 1) + 7 + >>> bit_remove(31, 4) + 15 + >>> bit_remove(31, 6) + 31 + """ + + return multibit_remove(bint, index, 1) + + +def multibit_get(bint: int, index: int, bit_len: int) -> int: + """Get bit_len number of bits starting from index. + 819 = 1100110011. + + >>> multibit_get(0, 1, 1) + 0 + >>> multibit_get(15, 0, 3) + 7 + >>> multibit_get(819, 2, 4) + 12 + >>> multibit_get(819, 4, 6) + 51 + """ + + if bint < 0 or index < 0 or bit_len < 0: + return None # error + + return (bint >> index) & ((1 << bit_len) - 1) + + +def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: + """Overlay bint at index with value for bit_len bits. + + >>> multibit_set(0, 1, 1, 0) + 0 + >>> multibit_set(15, 0, 2, 0) + 12 + >>> multibit_set(22, 0, 1, 1) + 23 + >>> multibit_set(22, 2, 1, 0) + 18 + >>> multibit_set(22, 2, 1, 3) is None + True + """ + + if bint < 0 or index < 0 or bit_len < 0 or value < 0: + return None # error + if bit_length(value) > bit_len: + return None + + return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( + bint & (1 << index) - 1 + ) + + +def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: + """Insert before index-th slot + + >>> multibit_insert(0, 1, 1, 1) + 2 + >>> multibit_insert(15, 1, 2, 0) + 57 + >>> multibit_insert(22, 0, 1, 1) + 45 + >>> multibit_set(22, 2, 1, 0) + 18 + >>> multibit_set(22, 2, 1, 3) is None + True + """ + + if bint < 0 or index < 0 or bit_len < 0 or value < 0: + return None # error + if bit_length(value) > bit_len: + return None + + return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) + + +def multibit_remove(bint: int, index: int, bit_len: int) -> int: + """Remove bits in bint from index to index+bit_len. + + >>> multibit_remove(3, 1, 1) + 1 + >>> multibit_remove(15, 1, 2) + 3 + >>> multibit_remove(22, 0, 1) + 11 + >>> multibit_remove(22, 2, 2) + 6 + >>> multibit_remove(22, 2, 6) + 2 + """ + + if bint < 0 or index < 0: + return None # error + + return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) + + +if __name__ == "__main__": + + import doctest + + doctest.testmod() From bb46e1af8bbaf85adb827937e5d347cdfeff8e65 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Tue, 28 May 2024 10:07:21 -0400 Subject: [PATCH 02/23] Fix errrors in multibit_manipulation Few typos, doctest error --- bit_manipulation/multibit_manipulation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 8a03601ebe7d..0dfaeeba6307 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -42,8 +42,8 @@ - OR that into the working 0b10111000, that is, ( 0b10111000 | 0b101 ) -> 0b10111101. -To remove the center two bits of 0b101101 -> 0b1001, the proces is mostly -the same, but ... +To remove the center two bits of 0b101101 -> 0b1001, the process is mostly +the same. - The initial right shift is index(2) + bit_length(2), taking out the two middle bits and producing 0b10. @@ -194,9 +194,9 @@ def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: 57 >>> multibit_insert(22, 0, 1, 1) 45 - >>> multibit_set(22, 2, 1, 0) - 18 - >>> multibit_set(22, 2, 1, 3) is None + >>> multibit_insert(22, 2, 1, 0) + 42 + >>> multibit_insert(22, 2, 1, 3) is None True """ From 305f4449b868bdfdc86ade8445b73e8c84c33d02 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 23:44:11 +0000 Subject: [PATCH 03/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bit_manipulation/multibit_manipulation.py | 35 +++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 0dfaeeba6307..03a05c92214c 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -1,7 +1,7 @@ """Bit integer manipulation, both single bit and multi-bit list-like slicing functions ( get, set, insert, remove ) implemented with builtin bitwise operations. - + See: https://high-python-ext-3-algorithms.readthedocs.io/ko/latest/chapter5.html#insert-bit https://en.wikipedia.org/wiki/Bit_manipulation#Bit_manipulation_operations @@ -11,22 +11,22 @@ bint:int The bit integer to be accessed or returned as modified. - + index:int The offset into the bit position from right, - 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian + 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian For inserts, index is the position to the right of index, - index 0 -> right of rightmost bit. + index 0 -> right of rightmost bit. For gets, sets and removes, it is the position of the bit itself. - + value:int Either [0,1] for single bit, or bit mask, bit_length(value) <= bitlen. - + bitlen:int The effective mask length, spec. leading zeros - ( bitlen 4 value 1 -> 0001 ) - -The bitwise expressions may look convoluted, but basically, there are + ( bitlen 4 value 1 -> 0001 ) + +The bitwise expressions may look convoluted, but basically, there are just three parts: left-hand side, value, right-hand side. For example, say you want to insert two ones in the middle of 0b101101, @@ -41,7 +41,7 @@ ( 0b101101 & 0b111 ) -> 0b101. - OR that into the working 0b10111000, that is, ( 0b10111000 | 0b101 ) -> 0b10111101. - + To remove the center two bits of 0b101101 -> 0b1001, the process is mostly the same. @@ -50,16 +50,16 @@ - The left shift of index produces 0b1000. - The original bint is ANDed with bitmask 0b11 producing 0b01 which is ORed with 0b1000 yielding the target 0b1001. - -It's not so bad once you get the hang of it. - + +It's not so bad once you get the hang of it. + Various bit insert/remove solutions exist using bin() string functions and slicing, but this bitwise implementation is significantly faster (about 3x) on Python for big ints (2^100). - -See https://github.com/billbreit/BitWiseApps/blob/main/dev/time_ops.py - - """ + +See https://github.com/billbreit/BitWiseApps/blob/main/dev/time_ops.py + +""" bit_length = int.bit_length @@ -230,7 +230,6 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: if __name__ == "__main__": - import doctest doctest.testmod() From b501f227f5e995d1756110803128be472dae2c5f Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Tue, 28 May 2024 20:14:29 -0400 Subject: [PATCH 04/23] Fix Ruff formatting Blank lines with spaces, trailing spaces at end of line, etc. --- bit_manipulation/multibit_manipulation.py | 270 +++++++++++----------- 1 file changed, 132 insertions(+), 138 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 0dfaeeba6307..674df401f02e 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -1,7 +1,7 @@ """Bit integer manipulation, both single bit and multi-bit list-like slicing functions ( get, set, insert, remove ) implemented with builtin bitwise operations. - + See: https://high-python-ext-3-algorithms.readthedocs.io/ko/latest/chapter5.html#insert-bit https://en.wikipedia.org/wiki/Bit_manipulation#Bit_manipulation_operations @@ -11,22 +11,22 @@ bint:int The bit integer to be accessed or returned as modified. - + index:int The offset into the bit position from right, 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian For inserts, index is the position to the right of index, index 0 -> right of rightmost bit. For gets, sets and removes, it is the position of the bit itself. - + value:int Either [0,1] for single bit, or bit mask, bit_length(value) <= bitlen. - + bitlen:int The effective mask length, spec. leading zeros ( bitlen 4 value 1 -> 0001 ) - -The bitwise expressions may look convoluted, but basically, there are + +The bitwise expressions may look convoluted, but basically, there are just three parts: left-hand side, value, right-hand side. For example, say you want to insert two ones in the middle of 0b101101, @@ -41,7 +41,7 @@ ( 0b101101 & 0b111 ) -> 0b101. - OR that into the working 0b10111000, that is, ( 0b10111000 | 0b101 ) -> 0b10111101. - + To remove the center two bits of 0b101101 -> 0b1001, the process is mostly the same. @@ -50,14 +50,14 @@ - The left shift of index produces 0b1000. - The original bint is ANDed with bitmask 0b11 producing 0b01 which is ORed with 0b1000 yielding the target 0b1001. - -It's not so bad once you get the hang of it. + +It's not so bad once you get the hang of it. Various bit insert/remove solutions exist using bin() string functions and slicing, but this bitwise implementation is significantly faster (about 3x) on Python for big ints (2^100). -See https://github.com/billbreit/BitWiseApps/blob/main/dev/time_ops.py +See https://github.com/billbreit/BitWiseApps/blob/main/dev/time_ops.py """ @@ -67,170 +67,164 @@ and for bit_length(value) > bit_len, which can cause silent errors. Anything like int(None) is going to cause a loud error. """ - def bit_get(bint: int, index: int): - """Get value of bit at index in bint. - - >>> bit_get(15, 0) - 1 - >>> bit_get(15, 4) - 0 - >>> bit_get(0, 4) - 0 - >>> bit_get(-1, 2) is None - True - >>> bit_get(0, -1) is None - True - """ - - return multibit_get(bint, index, 1) - + """Get value of bit at index in bint. + + >>> bit_get(15, 0) + 1 + >>> bit_get(15, 4) + 0 + >>> bit_get(0, 4) + 0 + >>> bit_get(-1, 2) is None + True + >>> bit_get(0, -1) is None + True + """ + + return multibit_get(bint, index, 1) def bit_set(bint: int, index: int, value: int = 1): - """Set bit at index to value 1 or 0, like set() or unset(). - - >>> bit_set(15, 0, 0) - 14 - >>> bit_set(15, 4, 1) - 31 - >>> bit_set(31, 6, 0) - 31 - >>> bit_set(31, 6, 3) is None - True - """ + """Set bit at index to value 1 or 0, like set() or unset(). - if value not in [0, 1]: - return None # error + >>> bit_set(15, 0, 0) + 14 + >>> bit_set(15, 4, 1) + 31 + >>> bit_set(31, 6, 0) + 31 + >>> bit_set(31, 6, 3) is None + True + """ - return multibit_set(bint, index, 1, value) + if value not in [0, 1]: + return None # error + return multibit_set(bint, index, 1, value) def bit_insert(bint: int, index: int, value: int = 1): - """Insert bit value before index. + """Insert bit value before index. - >>> bit_insert(15, 0, 0) - 30 - >>> bit_insert(15, 0, 1) - 31 - >>> bit_insert(15, 4, 1) - 31 - >>> bit_insert(31, 6, 0) - 31 - """ + >>> bit_insert(15, 0, 0) + 30 + >>> bit_insert(15, 0, 1) + 31 + >>> bit_insert(15, 4, 1) + 31 + >>> bit_insert(31, 6, 0) + 31 + """ - if value not in [0, 1]: - return None # error - - return multibit_insert(bint, index, 1, value) + if value not in [0, 1]: + return None # error + return multibit_insert(bint, index, 1, value) def bit_remove(bint: int, index: int) -> int: - """Remove the bit at index from bint. - - >>> bit_remove(15, 0) - 7 - >>> bit_remove(15, 1) - 7 - >>> bit_remove(31, 4) - 15 - >>> bit_remove(31, 6) - 31 - """ + """Remove the bit at index from bint. - return multibit_remove(bint, index, 1) + >>> bit_remove(15, 0) + 7 + >>> bit_remove(15, 1) + 7 + >>> bit_remove(31, 4) + 15 + >>> bit_remove(31, 6) + 31 + """ + return multibit_remove(bint, index, 1) def multibit_get(bint: int, index: int, bit_len: int) -> int: - """Get bit_len number of bits starting from index. - 819 = 1100110011. - - >>> multibit_get(0, 1, 1) - 0 - >>> multibit_get(15, 0, 3) - 7 - >>> multibit_get(819, 2, 4) - 12 - >>> multibit_get(819, 4, 6) - 51 - """ + """Get bit_len number of bits starting from index. + 819 = 1100110011. - if bint < 0 or index < 0 or bit_len < 0: - return None # error + >>> multibit_get(0, 1, 1) + 0 + >>> multibit_get(15, 0, 3) + 7 + >>> multibit_get(819, 2, 4) + 12 + >>> multibit_get(819, 4, 6) + 51 + """ - return (bint >> index) & ((1 << bit_len) - 1) + if bint < 0 or index < 0 or bit_len < 0: + return None # error + return (bint >> index) & ((1 << bit_len) - 1) def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: - """Overlay bint at index with value for bit_len bits. - - >>> multibit_set(0, 1, 1, 0) - 0 - >>> multibit_set(15, 0, 2, 0) - 12 - >>> multibit_set(22, 0, 1, 1) - 23 - >>> multibit_set(22, 2, 1, 0) - 18 - >>> multibit_set(22, 2, 1, 3) is None - True - """ - - if bint < 0 or index < 0 or bit_len < 0 or value < 0: - return None # error - if bit_length(value) > bit_len: - return None - - return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( - bint & (1 << index) - 1 - ) + """Overlay bint at index with value for bit_len bits. + + >>> multibit_set(0, 1, 1, 0) + 0 + >>> multibit_set(15, 0, 2, 0) + 12 + >>> multibit_set(22, 0, 1, 1) + 23 + >>> multibit_set(22, 2, 1, 0) + 18 + >>> multibit_set(22, 2, 1, 3) is None + True + """ + + if bint < 0 or index < 0 or bit_len < 0 or value < 0: + return None # error + if bit_length(value) > bit_len: + return None + + return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( + bint & (1 << index) - 1 + ) def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: - """Insert before index-th slot + """Insert before index-th slot - >>> multibit_insert(0, 1, 1, 1) - 2 - >>> multibit_insert(15, 1, 2, 0) - 57 - >>> multibit_insert(22, 0, 1, 1) - 45 - >>> multibit_insert(22, 2, 1, 0) - 42 - >>> multibit_insert(22, 2, 1, 3) is None - True - """ + >>> multibit_insert(0, 1, 1, 1) + 2 + >>> multibit_insert(15, 1, 2, 0) + 57 + >>> multibit_insert(22, 0, 1, 1) + 45 + >>> multibit_insert(22, 2, 1, 0) + 42 + >>> multibit_insert(22, 2, 1, 3) is None + True + """ - if bint < 0 or index < 0 or bit_len < 0 or value < 0: - return None # error - if bit_length(value) > bit_len: - return None + if bint < 0 or index < 0 or bit_len < 0 or value < 0: + return None # error + if bit_length(value) > bit_len: + return None - return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) + return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) def multibit_remove(bint: int, index: int, bit_len: int) -> int: - """Remove bits in bint from index to index+bit_len. + """Remove bits in bint from index to index+bit_len. - >>> multibit_remove(3, 1, 1) - 1 - >>> multibit_remove(15, 1, 2) - 3 - >>> multibit_remove(22, 0, 1) - 11 - >>> multibit_remove(22, 2, 2) - 6 - >>> multibit_remove(22, 2, 6) - 2 - """ + >>> multibit_remove(3, 1, 1) + 1 + >>> multibit_remove(15, 1, 2) + 3 + >>> multibit_remove(22, 0, 1) + 11 + >>> multibit_remove(22, 2, 2) + 6 + >>> multibit_remove(22, 2, 6) + 2 + """ - if bint < 0 or index < 0: - return None # error + if bint < 0 or index < 0: + return None # error - return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) + return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) if __name__ == "__main__": - import doctest + import doctest - doctest.testmod() + doctest.testmod() From 01be0441d6ee6af4a1356d1a2e50fbbb2b7898f9 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Tue, 28 May 2024 21:49:27 -0400 Subject: [PATCH 05/23] Struggling Is it or isn't it ? --- bit_manipulation/multibit_manipulation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 674df401f02e..f40f0a29d4af 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -2,6 +2,7 @@ slicing functions ( get, set, insert, remove ) implemented with builtin bitwise operations. +Struggling with GitHub commits ... See: https://high-python-ext-3-algorithms.readthedocs.io/ko/latest/chapter5.html#insert-bit https://en.wikipedia.org/wiki/Bit_manipulation#Bit_manipulation_operations From 23956f55f3d9e3720fa6d680d49a81ee349d5308 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 01:51:06 +0000 Subject: [PATCH 06/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bit_manipulation/multibit_manipulation.py | 267 +++++++++++----------- 1 file changed, 136 insertions(+), 131 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index f40f0a29d4af..415600bdd49c 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -15,9 +15,9 @@ index:int The offset into the bit position from right, - 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian + 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian For inserts, index is the position to the right of index, - index 0 -> right of rightmost bit. + index 0 -> right of rightmost bit. For gets, sets and removes, it is the position of the bit itself. value:int @@ -25,7 +25,7 @@ bitlen:int The effective mask length, spec. leading zeros - ( bitlen 4 value 1 -> 0001 ) + ( bitlen 4 value 1 -> 0001 ) The bitwise expressions may look convoluted, but basically, there are just three parts: left-hand side, value, right-hand side. @@ -51,16 +51,16 @@ - The left shift of index produces 0b1000. - The original bint is ANDed with bitmask 0b11 producing 0b01 which is ORed with 0b1000 yielding the target 0b1001. - + It's not so bad once you get the hang of it. - + Various bit insert/remove solutions exist using bin() string functions and slicing, but this bitwise implementation is significantly faster (about 3x) on Python for big ints (2^100). - + See https://github.com/billbreit/BitWiseApps/blob/main/dev/time_ops.py - - """ + +""" bit_length = int.bit_length @@ -68,164 +68,169 @@ and for bit_length(value) > bit_len, which can cause silent errors. Anything like int(None) is going to cause a loud error. """ + def bit_get(bint: int, index: int): - """Get value of bit at index in bint. - - >>> bit_get(15, 0) - 1 - >>> bit_get(15, 4) - 0 - >>> bit_get(0, 4) - 0 - >>> bit_get(-1, 2) is None - True - >>> bit_get(0, -1) is None - True - """ - - return multibit_get(bint, index, 1) + """Get value of bit at index in bint. + + >>> bit_get(15, 0) + 1 + >>> bit_get(15, 4) + 0 + >>> bit_get(0, 4) + 0 + >>> bit_get(-1, 2) is None + True + >>> bit_get(0, -1) is None + True + """ + + return multibit_get(bint, index, 1) + def bit_set(bint: int, index: int, value: int = 1): - """Set bit at index to value 1 or 0, like set() or unset(). + """Set bit at index to value 1 or 0, like set() or unset(). + + >>> bit_set(15, 0, 0) + 14 + >>> bit_set(15, 4, 1) + 31 + >>> bit_set(31, 6, 0) + 31 + >>> bit_set(31, 6, 3) is None + True + """ - >>> bit_set(15, 0, 0) - 14 - >>> bit_set(15, 4, 1) - 31 - >>> bit_set(31, 6, 0) - 31 - >>> bit_set(31, 6, 3) is None - True - """ + if value not in [0, 1]: + return None # error - if value not in [0, 1]: - return None # error + return multibit_set(bint, index, 1, value) - return multibit_set(bint, index, 1, value) def bit_insert(bint: int, index: int, value: int = 1): - """Insert bit value before index. + """Insert bit value before index. + + >>> bit_insert(15, 0, 0) + 30 + >>> bit_insert(15, 0, 1) + 31 + >>> bit_insert(15, 4, 1) + 31 + >>> bit_insert(31, 6, 0) + 31 + """ - >>> bit_insert(15, 0, 0) - 30 - >>> bit_insert(15, 0, 1) - 31 - >>> bit_insert(15, 4, 1) - 31 - >>> bit_insert(31, 6, 0) - 31 - """ + if value not in [0, 1]: + return None # error - if value not in [0, 1]: - return None # error + return multibit_insert(bint, index, 1, value) - return multibit_insert(bint, index, 1, value) def bit_remove(bint: int, index: int) -> int: - """Remove the bit at index from bint. + """Remove the bit at index from bint. - >>> bit_remove(15, 0) - 7 - >>> bit_remove(15, 1) - 7 - >>> bit_remove(31, 4) - 15 - >>> bit_remove(31, 6) - 31 - """ + >>> bit_remove(15, 0) + 7 + >>> bit_remove(15, 1) + 7 + >>> bit_remove(31, 4) + 15 + >>> bit_remove(31, 6) + 31 + """ + + return multibit_remove(bint, index, 1) - return multibit_remove(bint, index, 1) def multibit_get(bint: int, index: int, bit_len: int) -> int: - """Get bit_len number of bits starting from index. - 819 = 1100110011. + """Get bit_len number of bits starting from index. + 819 = 1100110011. + + >>> multibit_get(0, 1, 1) + 0 + >>> multibit_get(15, 0, 3) + 7 + >>> multibit_get(819, 2, 4) + 12 + >>> multibit_get(819, 4, 6) + 51 + """ - >>> multibit_get(0, 1, 1) - 0 - >>> multibit_get(15, 0, 3) - 7 - >>> multibit_get(819, 2, 4) - 12 - >>> multibit_get(819, 4, 6) - 51 - """ + if bint < 0 or index < 0 or bit_len < 0: + return None # error - if bint < 0 or index < 0 or bit_len < 0: - return None # error + return (bint >> index) & ((1 << bit_len) - 1) - return (bint >> index) & ((1 << bit_len) - 1) def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: - """Overlay bint at index with value for bit_len bits. - - >>> multibit_set(0, 1, 1, 0) - 0 - >>> multibit_set(15, 0, 2, 0) - 12 - >>> multibit_set(22, 0, 1, 1) - 23 - >>> multibit_set(22, 2, 1, 0) - 18 - >>> multibit_set(22, 2, 1, 3) is None - True - """ - - if bint < 0 or index < 0 or bit_len < 0 or value < 0: - return None # error - if bit_length(value) > bit_len: - return None - - return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( - bint & (1 << index) - 1 - ) + """Overlay bint at index with value for bit_len bits. + + >>> multibit_set(0, 1, 1, 0) + 0 + >>> multibit_set(15, 0, 2, 0) + 12 + >>> multibit_set(22, 0, 1, 1) + 23 + >>> multibit_set(22, 2, 1, 0) + 18 + >>> multibit_set(22, 2, 1, 3) is None + True + """ + + if bint < 0 or index < 0 or bit_len < 0 or value < 0: + return None # error + if bit_length(value) > bit_len: + return None + + return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( + bint & (1 << index) - 1 + ) def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: - """Insert before index-th slot + """Insert before index-th slot - >>> multibit_insert(0, 1, 1, 1) - 2 - >>> multibit_insert(15, 1, 2, 0) - 57 - >>> multibit_insert(22, 0, 1, 1) - 45 - >>> multibit_insert(22, 2, 1, 0) - 42 - >>> multibit_insert(22, 2, 1, 3) is None - True - """ + >>> multibit_insert(0, 1, 1, 1) + 2 + >>> multibit_insert(15, 1, 2, 0) + 57 + >>> multibit_insert(22, 0, 1, 1) + 45 + >>> multibit_insert(22, 2, 1, 0) + 42 + >>> multibit_insert(22, 2, 1, 3) is None + True + """ - if bint < 0 or index < 0 or bit_len < 0 or value < 0: - return None # error - if bit_length(value) > bit_len: - return None + if bint < 0 or index < 0 or bit_len < 0 or value < 0: + return None # error + if bit_length(value) > bit_len: + return None - return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) + return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) def multibit_remove(bint: int, index: int, bit_len: int) -> int: - """Remove bits in bint from index to index+bit_len. + """Remove bits in bint from index to index+bit_len. - >>> multibit_remove(3, 1, 1) - 1 - >>> multibit_remove(15, 1, 2) - 3 - >>> multibit_remove(22, 0, 1) - 11 - >>> multibit_remove(22, 2, 2) - 6 - >>> multibit_remove(22, 2, 6) - 2 - """ + >>> multibit_remove(3, 1, 1) + 1 + >>> multibit_remove(15, 1, 2) + 3 + >>> multibit_remove(22, 0, 1) + 11 + >>> multibit_remove(22, 2, 2) + 6 + >>> multibit_remove(22, 2, 6) + 2 + """ - if bint < 0 or index < 0: - return None # error + if bint < 0 or index < 0: + return None # error - return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) + return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) if __name__ == "__main__": + import doctest - import doctest - - doctest.testmod() + doctest.testmod() From c32638fb3199be3eceeab22a9ee17834784d5475 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Tue, 28 May 2024 21:52:16 -0400 Subject: [PATCH 07/23] Recommit Seems to be OK so far ... --- bit_manipulation/multibit_manipulation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index f40f0a29d4af..674df401f02e 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -2,7 +2,6 @@ slicing functions ( get, set, insert, remove ) implemented with builtin bitwise operations. -Struggling with GitHub commits ... See: https://high-python-ext-3-algorithms.readthedocs.io/ko/latest/chapter5.html#insert-bit https://en.wikipedia.org/wiki/Bit_manipulation#Bit_manipulation_operations From 93610d97445fccda7f76e19e230fcc19788e4b7b Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Wed, 29 May 2024 11:31:58 -0400 Subject: [PATCH 08/23] Fix type hints, validation errors Add type hints and fix leading spaces issues. --- bit_manipulation/multibit_manipulation.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 03a05c92214c..37f6365bc92c 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -14,9 +14,9 @@ index:int The offset into the bit position from right, - 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian + 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian For inserts, index is the position to the right of index, - index 0 -> right of rightmost bit. + index 0 -> right of rightmost bit. For gets, sets and removes, it is the position of the bit itself. value:int @@ -24,7 +24,7 @@ bitlen:int The effective mask length, spec. leading zeros - ( bitlen 4 value 1 -> 0001 ) + ( bitlen 4 value 1 -> 0001 ) The bitwise expressions may look convoluted, but basically, there are just three parts: left-hand side, value, right-hand side. @@ -68,7 +68,7 @@ Anything like int(None) is going to cause a loud error. """ -def bit_get(bint: int, index: int): +def bit_get(bint: int, index: int) -> int: """Get value of bit at index in bint. >>> bit_get(15, 0) @@ -86,7 +86,7 @@ def bit_get(bint: int, index: int): return multibit_get(bint, index, 1) -def bit_set(bint: int, index: int, value: int = 1): +def bit_set(bint: int, index: int, value: int = 1) -> int: """Set bit at index to value 1 or 0, like set() or unset(). >>> bit_set(15, 0, 0) @@ -105,7 +105,7 @@ def bit_set(bint: int, index: int, value: int = 1): return multibit_set(bint, index, 1, value) -def bit_insert(bint: int, index: int, value: int = 1): +def bit_insert(bint: int, index: int, value: int = 1) -> int: """Insert bit value before index. >>> bit_insert(15, 0, 0) @@ -223,13 +223,14 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: 2 """ - if bint < 0 or index < 0: + if bint < 0 or index < 0 or bit_len < 0: return None # error return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) if __name__ == "__main__": + import doctest doctest.testmod() From 0ef3e6b3025600d7950cf5effd4f48a8dc178347 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 15:32:43 +0000 Subject: [PATCH 09/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bit_manipulation/multibit_manipulation.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 37f6365bc92c..470e6033e29c 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -14,9 +14,9 @@ index:int The offset into the bit position from right, - 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian + 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian For inserts, index is the position to the right of index, - index 0 -> right of rightmost bit. + index 0 -> right of rightmost bit. For gets, sets and removes, it is the position of the bit itself. value:int @@ -24,7 +24,7 @@ bitlen:int The effective mask length, spec. leading zeros - ( bitlen 4 value 1 -> 0001 ) + ( bitlen 4 value 1 -> 0001 ) The bitwise expressions may look convoluted, but basically, there are just three parts: left-hand side, value, right-hand side. @@ -230,7 +230,6 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: if __name__ == "__main__": - import doctest doctest.testmod() From 29b6e8ff7d305f2635d2782d170d279326eb010b Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Wed, 29 May 2024 11:56:38 -0400 Subject: [PATCH 10/23] Fix tabs instead of spaces in comments Fixing tabs instead of spaces in comments --- bit_manipulation/multibit_manipulation.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 470e6033e29c..3c183f30ba4d 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -14,17 +14,18 @@ index:int The offset into the bit position from right, - 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian + 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian For inserts, index is the position to the right of index, - index 0 -> right of rightmost bit. + index 0 -> right of rightmost bit. For gets, sets and removes, it is the position of the bit itself. value:int - Either [0,1] for single bit, or bit mask, bit_length(value) <= bitlen. + Either [0,1] for single bit, or int value for multibit, + bit_length(value) <= bitlen. bitlen:int The effective mask length, spec. leading zeros - ( bitlen 4 value 1 -> 0001 ) + ( bitlen 4 value 1 -> 0001 ) The bitwise expressions may look convoluted, but basically, there are just three parts: left-hand side, value, right-hand side. @@ -230,6 +231,7 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: if __name__ == "__main__": + import doctest doctest.testmod() From c9d6a618dc840c28c8c90f77cd3321b230abb694 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 15:57:04 +0000 Subject: [PATCH 11/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bit_manipulation/multibit_manipulation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 3c183f30ba4d..14d3bf2e3d22 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -231,7 +231,6 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: if __name__ == "__main__": - import doctest doctest.testmod() From 9002513e46080ff0f969131fb5811af370930cef Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Wed, 29 May 2024 12:16:01 -0400 Subject: [PATCH 12/23] Fix type hint for returns Fixed int | None return hints, mypy error --- bit_manipulation/multibit_manipulation.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 14d3bf2e3d22..256ca51fbacf 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -69,7 +69,7 @@ Anything like int(None) is going to cause a loud error. """ -def bit_get(bint: int, index: int) -> int: +def bit_get(bint: int, index: int) -> int | None: """Get value of bit at index in bint. >>> bit_get(15, 0) @@ -87,7 +87,7 @@ def bit_get(bint: int, index: int) -> int: return multibit_get(bint, index, 1) -def bit_set(bint: int, index: int, value: int = 1) -> int: +def bit_set(bint: int, index: int, value: int = 1) -> int | None: """Set bit at index to value 1 or 0, like set() or unset(). >>> bit_set(15, 0, 0) @@ -106,7 +106,7 @@ def bit_set(bint: int, index: int, value: int = 1) -> int: return multibit_set(bint, index, 1, value) -def bit_insert(bint: int, index: int, value: int = 1) -> int: +def bit_insert(bint: int, index: int, value: int = 1) -> int | None: """Insert bit value before index. >>> bit_insert(15, 0, 0) @@ -125,7 +125,7 @@ def bit_insert(bint: int, index: int, value: int = 1) -> int: return multibit_insert(bint, index, 1, value) -def bit_remove(bint: int, index: int) -> int: +def bit_remove(bint: int, index: int) -> int | None: """Remove the bit at index from bint. >>> bit_remove(15, 0) @@ -141,7 +141,7 @@ def bit_remove(bint: int, index: int) -> int: return multibit_remove(bint, index, 1) -def multibit_get(bint: int, index: int, bit_len: int) -> int: +def multibit_get(bint: int, index: int, bit_len: int) -> int | None: """Get bit_len number of bits starting from index. 819 = 1100110011. @@ -161,7 +161,7 @@ def multibit_get(bint: int, index: int, bit_len: int) -> int: return (bint >> index) & ((1 << bit_len) - 1) -def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: +def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int | None: """Overlay bint at index with value for bit_len bits. >>> multibit_set(0, 1, 1, 0) @@ -186,7 +186,7 @@ def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: ) -def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: +def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int | None: """Insert before index-th slot >>> multibit_insert(0, 1, 1, 1) @@ -209,7 +209,7 @@ def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) -def multibit_remove(bint: int, index: int, bit_len: int) -> int: +def multibit_remove(bint: int, index: int, bit_len: int) -> int | None: """Remove bits in bint from index to index+bit_len. >>> multibit_remove(3, 1, 1) @@ -231,6 +231,7 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: if __name__ == "__main__": + import doctest doctest.testmod() From 27aebea54f338f353f1c57996ffa056c3f24c978 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 16:17:14 +0000 Subject: [PATCH 13/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bit_manipulation/multibit_manipulation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 256ca51fbacf..a90c6f190983 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -231,7 +231,6 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int | None: if __name__ == "__main__": - import doctest doctest.testmod() From 82695f895e264b987d11977bb7192debb790ed66 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Thu, 30 May 2024 08:09:18 -0400 Subject: [PATCH 14/23] Clean up return types Changed return type from None to Exceptions and fix doctests --- bit_manipulation/multibit_manipulation.py | 64 ++++++++++++++--------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index a90c6f190983..b2723b051cce 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -69,7 +69,7 @@ Anything like int(None) is going to cause a loud error. """ -def bit_get(bint: int, index: int) -> int | None: +def bit_get(bint: int, index: int) -> int: """Get value of bit at index in bint. >>> bit_get(15, 0) @@ -78,16 +78,20 @@ def bit_get(bint: int, index: int) -> int | None: 0 >>> bit_get(0, 4) 0 - >>> bit_get(-1, 2) is None - True - >>> bit_get(0, -1) is None - True + >>> bit_get(-1, 2) + Traceback (most recent call last): + ... + ValueError: All input values must be positive integers. + >>> bit_get(0, -1) + Traceback (most recent call last): + ... + ValueError: All input values must be positive integers. """ return multibit_get(bint, index, 1) -def bit_set(bint: int, index: int, value: int = 1) -> int | None: +def bit_set(bint: int, index: int, value: int = 1) -> int: """Set bit at index to value 1 or 0, like set() or unset(). >>> bit_set(15, 0, 0) @@ -96,17 +100,19 @@ def bit_set(bint: int, index: int, value: int = 1) -> int | None: 31 >>> bit_set(31, 6, 0) 31 - >>> bit_set(31, 6, 3) is None - True + >>> bit_set(31, 6, 3) + Traceback (most recent call last): + ... + ValueError: Input value must be 1 or 0. """ if value not in [0, 1]: - return None # error + raise ValueError("Input value must be 1 or 0.") return multibit_set(bint, index, 1, value) -def bit_insert(bint: int, index: int, value: int = 1) -> int | None: +def bit_insert(bint: int, index: int, value: int = 1) -> int: """Insert bit value before index. >>> bit_insert(15, 0, 0) @@ -120,12 +126,12 @@ def bit_insert(bint: int, index: int, value: int = 1) -> int | None: """ if value not in [0, 1]: - return None # error + raise ValueError("Input value must be 1 or 0.") return multibit_insert(bint, index, 1, value) -def bit_remove(bint: int, index: int) -> int | None: +def bit_remove(bint: int, index: int) -> int: """Remove the bit at index from bint. >>> bit_remove(15, 0) @@ -141,7 +147,7 @@ def bit_remove(bint: int, index: int) -> int | None: return multibit_remove(bint, index, 1) -def multibit_get(bint: int, index: int, bit_len: int) -> int | None: +def multibit_get(bint: int, index: int, bit_len: int) -> int: """Get bit_len number of bits starting from index. 819 = 1100110011. @@ -156,12 +162,12 @@ def multibit_get(bint: int, index: int, bit_len: int) -> int | None: """ if bint < 0 or index < 0 or bit_len < 0: - return None # error + raise ValueError("All input values must be positive integers.") return (bint >> index) & ((1 << bit_len) - 1) -def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int | None: +def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: """Overlay bint at index with value for bit_len bits. >>> multibit_set(0, 1, 1, 0) @@ -173,20 +179,24 @@ def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int | None: >>> multibit_set(22, 2, 1, 0) 18 >>> multibit_set(22, 2, 1, 3) is None - True + Traceback (most recent call last): + ... + ValueError: Bit length of value can not be greater than specified bit length. """ if bint < 0 or index < 0 or bit_len < 0 or value < 0: - return None # error + raise ValueError("All input values must be positive integers.") if bit_length(value) > bit_len: - return None + raise ValueError( + "Bit length of value can not be greater than specified bit length." + ) return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( bint & (1 << index) - 1 ) -def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int | None: +def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: """Insert before index-th slot >>> multibit_insert(0, 1, 1, 1) @@ -197,19 +207,23 @@ def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int | No 45 >>> multibit_insert(22, 2, 1, 0) 42 - >>> multibit_insert(22, 2, 1, 3) is None - True + >>> multibit_insert(22, 2, 1, 3) + Traceback (most recent call last): + ... + ValueError: Bit length of value can not be greater than specified bit length. """ if bint < 0 or index < 0 or bit_len < 0 or value < 0: - return None # error + raise ValueError("All input values must be positive integers.") if bit_length(value) > bit_len: - return None + raise ValueError( + "Bit length of value can not be greater than specified bit length." + ) return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) -def multibit_remove(bint: int, index: int, bit_len: int) -> int | None: +def multibit_remove(bint: int, index: int, bit_len: int) -> int: """Remove bits in bint from index to index+bit_len. >>> multibit_remove(3, 1, 1) @@ -225,7 +239,7 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int | None: """ if bint < 0 or index < 0 or bit_len < 0: - return None # error + raise ValueError("All input values must be positive integers.") return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) From 6e81a516202c377d4194d40bfcabbe5338671db9 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Fri, 31 May 2024 09:04:55 -0400 Subject: [PATCH 15/23] Cleanup comments Few typos, debugging advice --- bit_manipulation/multibit_manipulation.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index b2723b051cce..bac74722af31 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -31,7 +31,7 @@ just three parts: left-hand side, value, right-hand side. For example, say you want to insert two ones in the middle of 0b101101, -that is -> 0b10111101. Index is 2 ( 0 ,1, 2 from the right ) and the +that is -> 0b10111101. Index is 3 ( 0 ,1, 2, 3 from the right ) and the value is 3 (0b11) with a bit length of 2. - Shift >> index right to produce 0b101 @@ -44,15 +44,21 @@ -> 0b10111101. To remove the center two bits of 0b101101 -> 0b1001, the process is mostly -the same. +the same. Index is 2 for the remove operation on the right-center bit +rather than 3 for inserting, because we are referring to the bit itself +rather the position to the right of the bit index. -- The initial right shift is index(2) + bit_length(2), taking out the two +- The initial right shift is index (2 ) + bit_length(2), taking out the two middle bits and producing 0b10. - The left shift of index produces 0b1000. - The original bint is ANDed with bitmask 0b11 producing 0b01 which is ORed with 0b1000 yielding the target 0b1001. -It's not so bad once you get the hang of it. +It's not so bad once you get the hang of it, although it can still be a +bear to debug. In the insert example above, the result of inserting 0b11 +in the center ( index=3 ) or to the right ( index=2 ) produces the same +correct result despite the misspecification. These algorithms are very +fast but can be touchy at times. Various bit insert/remove solutions exist using bin() string functions and slicing, but this bitwise implementation is significantly faster @@ -64,10 +70,6 @@ bit_length = int.bit_length -"""The only consistent error checking is for bint < 0 or index < 0 etc., - and for bit_length(value) > bit_len, which can cause silent errors. - Anything like int(None) is going to cause a loud error. """ - def bit_get(bint: int, index: int) -> int: """Get value of bit at index in bint. @@ -197,7 +199,7 @@ def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: - """Insert before index-th slot + """Insert value before index-th slot >>> multibit_insert(0, 1, 1, 1) 2 @@ -245,6 +247,7 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: if __name__ == "__main__": + import doctest doctest.testmod() From 5a6275bf5e0ba033fc97ff95268be12f6294152f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 13:05:26 +0000 Subject: [PATCH 16/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bit_manipulation/multibit_manipulation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index bac74722af31..4a943e3eb3d5 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -57,7 +57,7 @@ It's not so bad once you get the hang of it, although it can still be a bear to debug. In the insert example above, the result of inserting 0b11 in the center ( index=3 ) or to the right ( index=2 ) produces the same -correct result despite the misspecification. These algorithms are very +correct result despite the misspecification. These algorithms are very fast but can be touchy at times. Various bit insert/remove solutions exist using bin() string functions @@ -247,7 +247,6 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: if __name__ == "__main__": - import doctest doctest.testmod() From cb65485d809ae0ffb02e5143f0a6941bbb506bdb Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Fri, 31 May 2024 09:10:25 -0400 Subject: [PATCH 17/23] Fix Ruff error Fix trailing spaces --- bit_manipulation/multibit_manipulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index bac74722af31..49739d9fd688 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -57,7 +57,7 @@ It's not so bad once you get the hang of it, although it can still be a bear to debug. In the insert example above, the result of inserting 0b11 in the center ( index=3 ) or to the right ( index=2 ) produces the same -correct result despite the misspecification. These algorithms are very +correct result despite the misspecification. These algorithms are very fast but can be touchy at times. Various bit insert/remove solutions exist using bin() string functions From db1466309a125be3fff2633f4e9da51925ce1f26 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Sat, 1 Jun 2024 12:47:58 -0400 Subject: [PATCH 18/23] Fix details Better comments, errors, check bit_length=0 conditions --- bit_manipulation/multibit_manipulation.py | 39 ++++++++++++----------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 4a943e3eb3d5..db7643dbca90 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -54,11 +54,11 @@ - The original bint is ANDed with bitmask 0b11 producing 0b01 which is ORed with 0b1000 yielding the target 0b1001. -It's not so bad once you get the hang of it, although it can still be a -bear to debug. In the insert example above, the result of inserting 0b11 -in the center ( index=3 ) or to the right ( index=2 ) produces the same -correct result despite the misspecification. These algorithms are very -fast but can be touchy at times. +Bit manipulation operations can be tricky to debug. In the insert example +above, the result of inserting 0b11 in the center ( index=3 ) or to the +right ( index=2 ) produces the same correct result despite the unintended +misspecification. Why is it worling sometimes and not others ? Frequently, +it's the result of inserting at the wrong index, for the hundredth time ! Various bit insert/remove solutions exist using bin() string functions and slicing, but this bitwise implementation is significantly faster @@ -83,11 +83,11 @@ def bit_get(bint: int, index: int) -> int: >>> bit_get(-1, 2) Traceback (most recent call last): ... - ValueError: All input values must be positive integers. + ValueError: multi_get -> All input values must be positive integers. >>> bit_get(0, -1) Traceback (most recent call last): ... - ValueError: All input values must be positive integers. + ValueError: multi_get -> All input values must be positive integers. """ return multibit_get(bint, index, 1) @@ -105,11 +105,11 @@ def bit_set(bint: int, index: int, value: int = 1) -> int: >>> bit_set(31, 6, 3) Traceback (most recent call last): ... - ValueError: Input value must be 1 or 0. + ValueError: bit_set -> Input value must be 1 or 0. """ if value not in [0, 1]: - raise ValueError("Input value must be 1 or 0.") + raise ValueError("bit_set -> Input value must be 1 or 0.") return multibit_set(bint, index, 1, value) @@ -128,7 +128,7 @@ def bit_insert(bint: int, index: int, value: int = 1) -> int: """ if value not in [0, 1]: - raise ValueError("Input value must be 1 or 0.") + raise ValueError("bit_insert -> Input value must be 1 or 0.") return multibit_insert(bint, index, 1, value) @@ -164,7 +164,7 @@ def multibit_get(bint: int, index: int, bit_len: int) -> int: """ if bint < 0 or index < 0 or bit_len < 0: - raise ValueError("All input values must be positive integers.") + raise ValueError("multi_get -> All input values must be positive integers.") return (bint >> index) & ((1 << bit_len) - 1) @@ -183,14 +183,14 @@ def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: >>> multibit_set(22, 2, 1, 3) is None Traceback (most recent call last): ... - ValueError: Bit length of value can not be greater than specified bit length. + ValueError: multi_set -> Bit length of value can not be greater than specified bit length. """ if bint < 0 or index < 0 or bit_len < 0 or value < 0: - raise ValueError("All input values must be positive integers.") + raise ValueError("multi_set -> All input values must be positive integers.") if bit_length(value) > bit_len: raise ValueError( - "Bit length of value can not be greater than specified bit length." + "multi_set -> Bit length of value can not be greater than specified bit length." ) return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( @@ -209,17 +209,19 @@ def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: 45 >>> multibit_insert(22, 2, 1, 0) 42 + >>> multibit_insert(22, 2, 0, 0) + 22 >>> multibit_insert(22, 2, 1, 3) Traceback (most recent call last): ... - ValueError: Bit length of value can not be greater than specified bit length. + ValueError: multi_insert -> Bit length of value can not be greater than specified bit length. """ if bint < 0 or index < 0 or bit_len < 0 or value < 0: - raise ValueError("All input values must be positive integers.") + raise ValueError("multi_insert -> All input values must be positive integers.") if bit_length(value) > bit_len: raise ValueError( - "Bit length of value can not be greater than specified bit length." + "multi_insert -> Bit length of value can not be greater than specified bit length." ) return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) @@ -241,12 +243,13 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: """ if bint < 0 or index < 0 or bit_len < 0: - raise ValueError("All input values must be positive integers.") + raise ValueError("multi_remove -> All input values must be positive integers.") return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) if __name__ == "__main__": + import doctest doctest.testmod() From 13f6fdb59f46c83147c5bf365cbf0447da40eb02 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 16:49:00 +0000 Subject: [PATCH 19/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bit_manipulation/multibit_manipulation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index db7643dbca90..6d982a62fa31 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -249,7 +249,6 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: if __name__ == "__main__": - import doctest doctest.testmod() From c05cc652a0ce20a703a16ac5961d29e29bea95c3 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Sat, 1 Jun 2024 13:03:14 -0400 Subject: [PATCH 20/23] Strings too long Revert to old --- bit_manipulation/multibit_manipulation.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 6d982a62fa31..ef430032ad73 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -109,7 +109,7 @@ def bit_set(bint: int, index: int, value: int = 1) -> int: """ if value not in [0, 1]: - raise ValueError("bit_set -> Input value must be 1 or 0.") + raise ValueError("Input value must be 1 or 0.") return multibit_set(bint, index, 1, value) @@ -128,7 +128,7 @@ def bit_insert(bint: int, index: int, value: int = 1) -> int: """ if value not in [0, 1]: - raise ValueError("bit_insert -> Input value must be 1 or 0.") + raise ValueError("Input value must be 1 or 0.") return multibit_insert(bint, index, 1, value) @@ -164,7 +164,7 @@ def multibit_get(bint: int, index: int, bit_len: int) -> int: """ if bint < 0 or index < 0 or bit_len < 0: - raise ValueError("multi_get -> All input values must be positive integers.") + raise ValueError("All input values must be positive integers.") return (bint >> index) & ((1 << bit_len) - 1) @@ -187,10 +187,11 @@ def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: """ if bint < 0 or index < 0 or bit_len < 0 or value < 0: - raise ValueError("multi_set -> All input values must be positive integers.") + raise ValueError("All input values must be positive integers.") if bit_length(value) > bit_len: + msg = raise ValueError( - "multi_set -> Bit length of value can not be greater than specified bit length." + "Bit length of value can not be greater than specified bit length." ) return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( @@ -218,10 +219,10 @@ def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: """ if bint < 0 or index < 0 or bit_len < 0 or value < 0: - raise ValueError("multi_insert -> All input values must be positive integers.") + raise ValueError("All input values must be positive integers.") if bit_length(value) > bit_len: raise ValueError( - "multi_insert -> Bit length of value can not be greater than specified bit length." + "Bit length of value can not be greater than specified bit length." ) return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) @@ -243,7 +244,7 @@ def multibit_remove(bint: int, index: int, bit_len: int) -> int: """ if bint < 0 or index < 0 or bit_len < 0: - raise ValueError("multi_remove -> All input values must be positive integers.") + raise ValueError("All input values must be positive integers.") return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) From b7d8d7f2ce538e7011089342229960206790e32e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 17:03:31 +0000 Subject: [PATCH 21/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bit_manipulation/multibit_manipulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index ef430032ad73..5c5f1cd875b6 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -189,7 +189,7 @@ def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: if bint < 0 or index < 0 or bit_len < 0 or value < 0: raise ValueError("All input values must be positive integers.") if bit_length(value) > bit_len: - msg = + msg = raise ValueError( "Bit length of value can not be greater than specified bit length." ) From d8654c25238ef0ca711c7401d5be922bf1ba3df2 Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Sat, 1 Jun 2024 13:21:45 -0400 Subject: [PATCH 22/23] Keep trying Loooong lines --- bit_manipulation/multibit_manipulation.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 5c5f1cd875b6..74507f833158 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -83,11 +83,11 @@ def bit_get(bint: int, index: int) -> int: >>> bit_get(-1, 2) Traceback (most recent call last): ... - ValueError: multi_get -> All input values must be positive integers. + ValueError: All input values must be positive integers. >>> bit_get(0, -1) Traceback (most recent call last): ... - ValueError: multi_get -> All input values must be positive integers. + ValueError: All input values must be positive integers. """ return multibit_get(bint, index, 1) @@ -105,7 +105,7 @@ def bit_set(bint: int, index: int, value: int = 1) -> int: >>> bit_set(31, 6, 3) Traceback (most recent call last): ... - ValueError: bit_set -> Input value must be 1 or 0. + ValueError: Input value must be 1 or 0. """ if value not in [0, 1]: @@ -180,16 +180,15 @@ def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: 23 >>> multibit_set(22, 2, 1, 0) 18 - >>> multibit_set(22, 2, 1, 3) is None + >>> multibit_set(22, 2, 1, 3) Traceback (most recent call last): ... - ValueError: multi_set -> Bit length of value can not be greater than specified bit length. + ValueError: Bit length of value can not be greater than specified bit length. """ if bint < 0 or index < 0 or bit_len < 0 or value < 0: raise ValueError("All input values must be positive integers.") if bit_length(value) > bit_len: - msg = raise ValueError( "Bit length of value can not be greater than specified bit length." ) @@ -215,7 +214,7 @@ def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: >>> multibit_insert(22, 2, 1, 3) Traceback (most recent call last): ... - ValueError: multi_insert -> Bit length of value can not be greater than specified bit length. + ValueError: Bit length of value can not be greater than specified bit length. """ if bint < 0 or index < 0 or bit_len < 0 or value < 0: From 50dfbce2dbacce65d43fd74b0ceb018a4949ed0a Mon Sep 17 00:00:00 2001 From: Bill Breitmayer Date: Sat, 1 Jun 2024 13:29:54 -0400 Subject: [PATCH 23/23] Spelling Coming down to the finish ... --- bit_manipulation/multibit_manipulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py index 74507f833158..67bcc96e0dd8 100644 --- a/bit_manipulation/multibit_manipulation.py +++ b/bit_manipulation/multibit_manipulation.py @@ -57,7 +57,7 @@ Bit manipulation operations can be tricky to debug. In the insert example above, the result of inserting 0b11 in the center ( index=3 ) or to the right ( index=2 ) produces the same correct result despite the unintended -misspecification. Why is it worling sometimes and not others ? Frequently, +misspecification. Why is it working sometimes and not others ? Frequently, it's the result of inserting at the wrong index, for the hundredth time ! Various bit insert/remove solutions exist using bin() string functions