Skip to content

Commit d12ba66

Browse files
committed
Bit manipulation: add type hints, validation, doctests; simplify bitwise operations
1 parent c79034c commit d12ba66

File tree

4 files changed

+134
-131
lines changed

4 files changed

+134
-131
lines changed

bit_manipulation/binary_and_operator.py

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,48 @@
1+
"""Bitwise AND helper.
2+
3+
Return a zero-padded binary string representing ``a & b`` where the width is the
4+
maximum bit length of the inputs. Only non-negative integers are accepted.
5+
6+
>>> binary_and(25, 32)
7+
'0b000000'
8+
>>> binary_and(37, 50)
9+
'0b100000'
10+
>>> binary_and(21, 30)
11+
'0b10100'
12+
>>> binary_and(58, 73)
13+
'0b0001000'
14+
>>> binary_and(0, 255)
15+
'0b00000000'
16+
>>> binary_and(256, 256)
17+
'0b100000000'
18+
19+
Invalid inputs raise clear exceptions:
20+
21+
>>> binary_and(0, -1)
22+
Traceback (most recent call last):
23+
...
24+
ValueError: inputs must be non-negative integers
25+
>>> binary_and(0, 1.1)
26+
Traceback (most recent call last):
27+
...
28+
TypeError: inputs must be integers
29+
>>> binary_and("0", "1")
30+
Traceback (most recent call last):
31+
...
32+
TypeError: inputs must be integers
33+
"""
34+
135
# https://www.tutorialspoint.com/python3/bitwise_operators_example.htm
236

337

438
def binary_and(a: int, b: int) -> str:
5-
"""
6-
Take in 2 integers, convert them to binary,
7-
return a binary number that is the
8-
result of a binary and operation on the integers provided.
9-
10-
>>> binary_and(25, 32)
11-
'0b000000'
12-
>>> binary_and(37, 50)
13-
'0b100000'
14-
>>> binary_and(21, 30)
15-
'0b10100'
16-
>>> binary_and(58, 73)
17-
'0b0001000'
18-
>>> binary_and(0, 255)
19-
'0b00000000'
20-
>>> binary_and(256, 256)
21-
'0b100000000'
22-
>>> binary_and(0, -1)
23-
Traceback (most recent call last):
24-
...
25-
ValueError: the value of both inputs must be positive
26-
>>> binary_and(0, 1.1)
27-
Traceback (most recent call last):
28-
...
29-
ValueError: Unknown format code 'b' for object of type 'float'
30-
>>> binary_and("0", "1")
31-
Traceback (most recent call last):
32-
...
33-
TypeError: '<' not supported between instances of 'str' and 'int'
34-
"""
39+
if not isinstance(a, int) or not isinstance(b, int):
40+
raise TypeError("inputs must be integers")
3541
if a < 0 or b < 0:
36-
raise ValueError("the value of both inputs must be positive")
37-
38-
a_binary = format(a, "b")
39-
b_binary = format(b, "b")
40-
41-
max_len = max(len(a_binary), len(b_binary))
42+
raise ValueError("inputs must be non-negative integers")
4243

43-
return "0b" + "".join(
44-
str(int(char_a == "1" and char_b == "1"))
45-
for char_a, char_b in zip(a_binary.zfill(max_len), b_binary.zfill(max_len))
46-
)
44+
max_len = max(a.bit_length(), b.bit_length())
45+
return f"0b{(a & b):0{max_len}b}"
4746

4847

4948
if __name__ == "__main__":

bit_manipulation/binary_or_operator.py

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,47 @@
1+
"""Bitwise OR helper.
2+
3+
Return a zero-padded binary string representing ``a | b`` where the width is the
4+
maximum bit length of the inputs. Only non-negative integers are accepted.
5+
6+
>>> binary_or(25, 32)
7+
'0b111001'
8+
>>> binary_or(37, 50)
9+
'0b110111'
10+
>>> binary_or(21, 30)
11+
'0b11111'
12+
>>> binary_or(58, 73)
13+
'0b1111011'
14+
>>> binary_or(0, 255)
15+
'0b11111111'
16+
>>> binary_or(0, 256)
17+
'0b100000000'
18+
19+
Invalid inputs raise clear exceptions:
20+
21+
>>> binary_or(0, -1)
22+
Traceback (most recent call last):
23+
...
24+
ValueError: inputs must be non-negative integers
25+
>>> binary_or(0, 1.1)
26+
Traceback (most recent call last):
27+
...
28+
TypeError: inputs must be integers
29+
>>> binary_or("0", "1")
30+
Traceback (most recent call last):
31+
...
32+
TypeError: inputs must be integers
33+
"""
34+
135
# https://www.tutorialspoint.com/python3/bitwise_operators_example.htm
236

337

438
def binary_or(a: int, b: int) -> str:
5-
"""
6-
Take in 2 integers, convert them to binary, and return a binary number that is the
7-
result of a binary or operation on the integers provided.
8-
9-
>>> binary_or(25, 32)
10-
'0b111001'
11-
>>> binary_or(37, 50)
12-
'0b110111'
13-
>>> binary_or(21, 30)
14-
'0b11111'
15-
>>> binary_or(58, 73)
16-
'0b1111011'
17-
>>> binary_or(0, 255)
18-
'0b11111111'
19-
>>> binary_or(0, 256)
20-
'0b100000000'
21-
>>> binary_or(0, -1)
22-
Traceback (most recent call last):
23-
...
24-
ValueError: the value of both inputs must be positive
25-
>>> binary_or(0, 1.1)
26-
Traceback (most recent call last):
27-
...
28-
TypeError: 'float' object cannot be interpreted as an integer
29-
>>> binary_or("0", "1")
30-
Traceback (most recent call last):
31-
...
32-
TypeError: '<' not supported between instances of 'str' and 'int'
33-
"""
39+
if not isinstance(a, int) or not isinstance(b, int):
40+
raise TypeError("inputs must be integers")
3441
if a < 0 or b < 0:
35-
raise ValueError("the value of both inputs must be positive")
36-
a_binary = str(bin(a))[2:] # remove the leading "0b"
37-
b_binary = str(bin(b))[2:]
38-
max_len = max(len(a_binary), len(b_binary))
39-
return "0b" + "".join(
40-
str(int("1" in (char_a, char_b)))
41-
for char_a, char_b in zip(a_binary.zfill(max_len), b_binary.zfill(max_len))
42-
)
42+
raise ValueError("inputs must be non-negative integers")
43+
max_len = max(a.bit_length(), b.bit_length())
44+
return f"0b{(a | b):0{max_len}b}"
4345

4446

4547
if __name__ == "__main__":

bit_manipulation/binary_xor_operator.py

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,48 @@
1+
"""Bitwise XOR helper.
2+
3+
Return a zero-padded binary string representing ``a ^ b`` where the width is the
4+
maximum bit length of the inputs. Only non-negative integers are accepted.
5+
6+
>>> binary_xor(25, 32)
7+
'0b111001'
8+
>>> binary_xor(37, 50)
9+
'0b010111'
10+
>>> binary_xor(21, 30)
11+
'0b01011'
12+
>>> binary_xor(58, 73)
13+
'0b1110011'
14+
>>> binary_xor(0, 255)
15+
'0b11111111'
16+
>>> binary_xor(256, 256)
17+
'0b000000000'
18+
19+
Invalid inputs raise clear exceptions:
20+
21+
>>> binary_xor(0, -1)
22+
Traceback (most recent call last):
23+
...
24+
ValueError: inputs must be non-negative integers
25+
>>> binary_xor(0, 1.1)
26+
Traceback (most recent call last):
27+
...
28+
TypeError: inputs must be integers
29+
>>> binary_xor("0", "1")
30+
Traceback (most recent call last):
31+
...
32+
TypeError: inputs must be integers
33+
"""
34+
135
# https://www.tutorialspoint.com/python3/bitwise_operators_example.htm
236

337

438
def binary_xor(a: int, b: int) -> str:
5-
"""
6-
Take in 2 integers, convert them to binary,
7-
return a binary number that is the
8-
result of a binary xor operation on the integers provided.
9-
10-
>>> binary_xor(25, 32)
11-
'0b111001'
12-
>>> binary_xor(37, 50)
13-
'0b010111'
14-
>>> binary_xor(21, 30)
15-
'0b01011'
16-
>>> binary_xor(58, 73)
17-
'0b1110011'
18-
>>> binary_xor(0, 255)
19-
'0b11111111'
20-
>>> binary_xor(256, 256)
21-
'0b000000000'
22-
>>> binary_xor(0, -1)
23-
Traceback (most recent call last):
24-
...
25-
ValueError: the value of both inputs must be positive
26-
>>> binary_xor(0, 1.1)
27-
Traceback (most recent call last):
28-
...
29-
TypeError: 'float' object cannot be interpreted as an integer
30-
>>> binary_xor("0", "1")
31-
Traceback (most recent call last):
32-
...
33-
TypeError: '<' not supported between instances of 'str' and 'int'
34-
"""
39+
if not isinstance(a, int) or not isinstance(b, int):
40+
raise TypeError("inputs must be integers")
3541
if a < 0 or b < 0:
36-
raise ValueError("the value of both inputs must be positive")
37-
38-
a_binary = str(bin(a))[2:] # remove the leading "0b"
39-
b_binary = str(bin(b))[2:] # remove the leading "0b"
40-
41-
max_len = max(len(a_binary), len(b_binary))
42+
raise ValueError("inputs must be non-negative integers")
4243

43-
return "0b" + "".join(
44-
str(int(char_a != char_b))
45-
for char_a, char_b in zip(a_binary.zfill(max_len), b_binary.zfill(max_len))
46-
)
44+
max_len = max(a.bit_length(), b.bit_length())
45+
return f"0b{(a ^ b):0{max_len}b}"
4746

4847

4948
if __name__ == "__main__":

bit_manipulation/is_even.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
11
def is_even(number: int) -> bool:
2-
"""
3-
return true if the input integer is even
4-
Explanation: Lets take a look at the following decimal to binary conversions
5-
2 => 10
6-
14 => 1110
7-
100 => 1100100
8-
3 => 11
9-
13 => 1101
10-
101 => 1100101
11-
from the above examples we can observe that
12-
for all the odd integers there is always 1 set bit at the end
13-
also, 1 in binary can be represented as 001, 00001, or 0000001
14-
so for any odd integer n => n&1 is always equals 1 else the integer is even
2+
"""Return True if the input integer is even using a bitwise check.
3+
4+
Explanation:
5+
In binary, even numbers always have the least significant bit cleared (0),
6+
while odd numbers have it set (1). Therefore, ``n & 1 == 0`` implies even.
157
168
>>> is_even(1)
179
False
@@ -27,8 +19,19 @@ def is_even(number: int) -> bool:
2719
True
2820
>>> is_even(101)
2921
False
22+
>>> is_even(True)
23+
Traceback (most recent call last):
24+
...
25+
TypeError: input must be an integer
26+
>>> is_even(3.14)
27+
Traceback (most recent call last):
28+
...
29+
TypeError: input must be an integer
3030
"""
31-
return number & 1 == 0
31+
if not isinstance(number, int) or isinstance(number, bool):
32+
# bool is a subclass of int; explicitly disallow it as a number here.
33+
raise TypeError("input must be an integer")
34+
return (number & 1) == 0
3235

3336

3437
if __name__ == "__main__":

0 commit comments

Comments
 (0)