Python で疑似 n-bit int クラスを自作してみた.
背景
Cracking the Coding Interview を読んでいて,Bit Manipulation の章で実際にコードを書くとき Python の int が扱いづらかった.
Python の int には値の大きさに制限がないので.それに起因して整数値を 01 の並びとして扱うときに,下にあるようにいろいろ不便があった.
例 1. 負数のバイナリ表記が見づらい
Python のバイナリ表記は [-]絶対値のバイナリ表記
がフォーマット.
簡単のため 8 bit の int 型を扱っていると想定.
0 は 8 bit で b00000000
なので ~0
を計算すると b11111111
となる.
これを Python で ~0
を計算して表示すると -0000001
となる.
扱う bit 数に制限がないのでこういうフォーマットになるのも当然なんだけど,今回自分が使いたい用途ではちょっと見づらい.
n = ~0
print(f"{n:08b}") # -0000001
例 2. 左シフトがトランケートされない
これも扱う bit 数に制限がないから当たり前なんだけど,左シフトしたときに扱いたい bit 数からはみ出た分がそのまま残る.
15 = b1111
を左に 2 だけシフトさせることを考える.
8 bit の int 型の場合,15 を左に 2 だけシフトさせると 15 << 2 = b1100
となり,8 bit 分からあふれた先頭の 11
がトランケートされる.
一方 Python の場合は 15 << 2 = b111100
のまま.
決まった bit 数での int の計算を想定している場合,都合が悪いことがある.
// C++
int8_t n = 15 // b1111
n <<= 2 // b1100
int8_t m = 1 // b0001
int8_t res = n | m // b1101
# Python
n = 15 # b1111
n <<= 2 # b111100
m = 1 # b000001
res = n | m # b111101
Python にも int32 や int64 みたいなのがないか調べたけど,見当たらなかったので自作してみた.
実行環境
- Python 3.8.5
- Docker Container: Ubuntu 20.04
32 bit 型 (Int32) だけはテストした(ソースコードとテスト: https://github.com/e5pe0n/algorithm-training/tree/master/cracking_the_coding_interview_6th/chapter05/python/bitint )
自作整数型クラス Int の設計
欲しい機能
- 扱う bit 数を 32 bit や 64 bit に制限できる
-
<<
での先頭ビットのトランケート
-
- 01 の並びが 2 の補数でアサインされたバイナリ表記を表示できる
- 組込み関数
format()
でd
やb
などフォーマットを指定できる
- 組込み関数
- Built-in の int と併用できる
-
+
や-
といった算術演算子を int と組み合わせて適用できる -
&
や|
といったビット演算子を int と組み合わせて適用できる
-
Int の実装
コンストラクタ
コンストラクタ部分の全体
class Int:
def __init__(self, value: Union[int, str], bit: int):
if bit <= 0:
raise ValueError("bit must be greater than 0.")
self.__bit: int = bit
self.__max: int = (1 << (self.__bit - 1)) - 1
self.__min: int = -self.__max - 1
if isinstance(value, int):
# range check
if value < self.min:
raise ValueError(
f"underflow in {self.__bit}-bit integer value. \
value must be between [{self.__min}, {self.__max}]")
elif self.max < value:
raise ValueError(
f"overflow in {self.bit}-bit integer value. \
value must be between [{self.__min}, {self.__max}]")
if value >= 0:
self.__value: int = value
self.__bin: int = self.__value
else:
self.__value: int = value
self.__bin: int = self.__cmpl(abs(self.__value))
elif isinstance(value, str):
if len(value) > self.__bit:
raise ValueError(f"length of value must be between [1, {self.__bit}].")
if not pat.match(value):
raise ValueError("value must be a sequence of 0 and 1.")
if len(value) == self.__bit and value[0] == '1':
# MSB is 1 so we must calculate the complement
self.__bin: int = int(value, 2)
self.__value: int = -self.__cmpl(self.__bin)
else:
self.__value: int = int(value, 2)
self.__bin: int = self.__value
else:
raise TypeError("value must be an int or a str object.")
@property
def bit(self) -> int:
return self.__bit
@property
def bin(self) -> int:
return self.__bin
@property
def value(self) -> int:
return self.__value
@property
def max(self) -> int:
return self.__max
@property
def min(self) -> int:
return self.__min
def __cmpl(self, n: int) -> int:
return ((self.__max + 1) << 1) - n
...
def Int16(value):
return Int(value, 16)
def Int32(value):
return Int(value, 32)
def Int64(value):
return Int(value, 64)
int とバイナリ表記の str (value
) から,与えられた bit 数 (bit
) の Int オブジェクトを作れるようにする.
bit
が 0 以下の場合は ValueError を投げる.
__max
は与えられた bit 数で扱える最大値,__min
は最小値.
例えば 4 bit の場合は __max = 1 << (4 - 1) - 1 = b1000 - 1 = 8 - 1 = 7
,__min = -7 - 1 = -8
になる.
0 を表す分だけ __max
の方が絶対値が小さい.
positive integers | negative integers |
---|---|
7 (b0111) | -1 (b1111) |
6 (b0110) | -2 (b1110) |
5 (b0101) | -3 (b1101) |
4 (b0100) | -4 (b1100) |
3 (b0011) | -5 (b1011) |
2 (b0010) | -6 (b1010) |
1 (b0001) | -7 (b1001) |
0 (b0000) | -8 (b1000) |
bit 数や最大値,最小値はオブジェクトの外部からも参照したいが代入されたくない.
ダンダー (Double Underscore: __
) でマングリングして外部から隠し,代わりに @propety
をつけてゲッターを用意しておく.
def __init__(self, value: Union[int, str], bit: int):
if bit <= 0:
raise ValueError("bit must be greater than 0.")
self.__bit: int = bit
self.__max: int = (1 << (self.__bit - 1)) - 1
self.__min: int = -self.__max - 1
@property
def bit(self) -> int:
return self.__bit
@property
def max(self) -> int:
return self.__max
@property
def min(self) -> int:
return self.__min
value
が int の場合,value
の値が bit
型整数値の最大値と最小値の範囲におさまっているかチェックする.
if isinstance(value, int):
# range check
if value < self.min:
raise ValueError(
f"underflow in {self.__bit}-bit integer value. \
value must be between [{self.__min}, {self.__max}]")
elif self.max < value:
raise ValueError(
f"overflow in {self.bit}-bit integer value. \
value must be between [{self.__min}, {self.__max}]")
__bin
はバイナリ表記用の整数値を保持する.
4-bit int で -5 を扱うことを考える.
Python で -5 をバイナリ表記すると -0101
となるが,2 の補数表現を使う場合は 1011
となる.
Int オブジェクトでは -5 をバイナリで表示する場合 1011
と表示したい.
-5 + 5 = 0 i.e. bxxxx + b0101 = b0000
から -5 = b1011
が得られる.
トランケートされた 1 bit を考慮すると b0xxxx + b00101 = b10000
i.e. b0xxxx = b10000 - b00101 = 2^4 - 5 = 11 (b01011)
となり,b1011 の 10 進整数値は bit 数 4 と -5 の絶対値 5 から直接計算できることがわかる.
一般化して,__bin = 2**__bit - abs(__value)
.
補数を計算する補助関数 __cmpl()
では計算しておいた最大値 __max
に 1 を足して 2 倍することで 2**__bit
を計算してる.
if value >= 0:
self.__value: int = value
self.__bin: int = self.__value
else:
self.__value: int = value
self.__bin: int = self.__cmpl(abs(self.__value))
def __cmpl(self, n: int) -> int:
return ((self.__max + 1) << 1) - n
value
が str の場合,文字数が bit 数以下であること,0 と 1 からなり,必ず 0 か 1 を含む文字列であることをチェックする.
import re
pat = re.compile(r"[01]+")
...
elif isinstance(value, str):
if len(value) > self.__bit:
raise ValueError(f"length of value must be between [1, {self.__bit}].")
if not pat.match(value):
raise ValueError("value must be a sequence of 0 and 1.")
文字数が bit 数と同じで先頭が 1 の場合は負数なので補数を計算する必要がある.
4-bit int で考える.
1011 はそのまま 10 進数にすると 11 だが,先頭が 1 なので負数,つまり補数表現だと 10 進数で x + (-x) = 0
となる何かしら 0 以上の整数 x にマイナスがついたものを表す.
b01011 + b0yyyy = b10000
から b0yyyy = b00101 = 5
なので 1011 は補数表現で -5 を表すことがわかる.
10 進数で考えると 5 = 2^4 - 11
なので,これにマイナスをつけると補数表現での値が得られる.
一般化して __value = -(2**__bit - __bin)
.
実装では補助関数 __cmpl()
を使っている.
if len(value) == self.__bit and value[0] == '1':
# MSB is 1 so we must calculate the complement
self.__bin: int = int(value, 2)
self.__value: int = -self.__cmpl(self.__bin)
else:
self.__value: int = int(value, 2)
self.__bin: int = self.__value
__value
,__bin
とも外部から参照したいが値の変更はされたくないので,変数名をマングリングしてゲッターを作る.
@property
def bin(self) -> int:
return self.__bin
@property
def value(self) -> int:
return self.__value
16-bit,32-bit,64-bit はよく使うかと思って個別にオブジェクト作成用の関数を用意しといた.
def Int16(value):
return Int(value, 16)
def Int32(value):
return Int(value, 32)
def Int64(value):
return Int(value, 64)
文字列用のスペシャルメソッドの実装
組込み関数の str()
や print()
で表示される文字列を定義するため __str__()
を実装する.
シンプルに整数値だけのほうが見やすい気がするので __str__()
では Built-in の int と同じ表示にする.
n = Int32(5)
print(n) # 5
def __str__(self) -> str:
return str(self.value)
__str__()
とは別に,デバッグ用に __repr__()
を実装する.
どんなオブジェクトかがわかるように Int(bit=32, value=5, bin=5)
のように表示する.
def __repr__(self) -> str:
return f"Int(bit={self.__bit}, value={self.__value}, bin={self.__bin})"
フォーマット用のスペシャルメソッドとして __format__()
を実装する.
これを定義しておくことで,Built-in の int と同じように f 文字列や format 関数で d
や b
などを指定して表示することができる.
n = Int32(5)
print(f"{n:05d}") # 00005
print(f"{n:.5f}") # 5.00000
print(f"{n:05b}") # 00101
def __format__(self, format_spec: str) -> str:
t = format_spec[-1]
if t in ('b', 'o', 'x', 'X'):
return format(self.bin, format_spec)
else:
return format(self.value, format_spec)
算術演算子用のスペシャルメソッドの実装
オブジェクトに演算子を適用できるようにスペシャルメソッドを実装する.
例えば __add__()
を定義しておくと次のように +
が使えるようになる.
実際,a + b
は a.__add__(b)
を実行している.
__add__()
の引数として自作の Int と int を扱えるようにしておけば,Int + Int
,Int + int
が計算できる.
ope1 = Int32(1)
ope2 = Int32(2)
res = ope1 + ope2 # Int(3)
ope1 = Int32(1)
ope2 = 2
res = ope1 + 2 # Int(3)
補助関数 __validate_opes()
で引数のバリデーションチェックを行う.
仕様として +
や -
の結果はより大きい bit 数の Int オブジェクトを返すようにした(わかりやすさ,実装のしやすさから).
def __validate_opes(self, other: Union[int, Int]) -> Tuple[Int, Int]:
if isinstance(other, int):
return (self, Int(other, self.__bit))
elif isinstance(other, Int):
greater_bit = max(self.__bit, other.bit)
return (Int(self.value, greater_bit), Int(other.value, greater_bit))
else:
raise TypeError("operands must be int or Int objects")
def __apply_num_op(self, ope1: Int, ope2: Int, op: Callable[[int, int], int]) -> Int:
res = op(ope1.value, ope2.value)
return Int(res, ope1.bit)
...
def __add__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope1, ope2, operator.add)
__add__()
だけだと int + Int
が計算できないので __radd__()
を実装する.
a + b
を計算するとき,a に __add__()
が定義されていない場合は b の __radd__()
が実行される(b に __radd__()
も定義されてない場合は TypeError になる).
+
は交換法則によってオペランドの順序は計算結果に影響しないので,__radd__()
ではそのまま __add__()
を使っている.
ope1 = 1
ope2 = Int32(2)
res = ope1 + ope2 # Int(3)
def __radd__(self, other: Union[int, Int]) -> Int:
return self + other # self.__add__(other) と同じ
+=
も使えるように __iadd__()
を実装する.
__riadd__()
とかはないっぽい.
ope1 = Int32(1)
ope2 = Int32(2)
ope1 += ope2 # Int(3)
ope1 = Int32(1)
ope2 = 2
ope1 += ope2 # Int(3)
ope1 = 1
ope2 = Int32(2)
ope1 += ope2 # Int(3)
def __iadd__(self, other: Union[int, Int]) -> Int:
return self + other
ビット演算子用のスペシャルメソッドの実装
算術演算子と同じように __and__()
,__rand__()
,__iand__()
などを実装する.
ビット演算ではオペランドとして value
プロパティではなく bin
プロパティを使う.
ope1 = Int(-2, 4) # 1110
ope2 = Int(6, 4) # 0110
res = ope1 & ope2 # 0110
ope1 = Int(-2, 4) # 1110
ope2 = 6 # 0110
res = ope1 & ope2 # 0110
ope1 = -2 # 1110
ope2 = Int(6, 4) # 0110
res = ope1 & ope2 # 0110
ope1 = Int(-2, 4) # 1110
ope2 = Int(6, 4) # 0110
ope1 &= ope2 # 0110
ope1 = Int(-2, 4) # 1110
ope2 = 6 # 0110
ope1 &= ope2 # 0110
ope1 = -2 # 1110
ope2 = Int(6, 4) # 0110
ope1 &= ope2 # 0110
def __apply_bit_op(self, ope1: Int, ope2: Int, op: Callable[[int, int], int]) -> Int:
res = op(ope1.bin, ope2.bin)
return Int(f"{res:0{ope1.bit}b}", ope1.bit)
...
def __and__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_bit_op(ope1, ope2, operator.and_)
def __rand__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_bit_op(ope2, ope1, operator.and_)
def __iand__(self, other: Union[int, Int]) -> Int:
return self & other
左シフト用のスペシャルメソッド __lshift__()
の実装.
bit 数からはみ出た分をトランケートする処理はバイナリ表記用の整数値 __bin
からバイナリ表記を得て,文字列として処理することで行う.
ope1_s[shift:]
でシフト分バイナリ文字列をスライスし,('0' * cnt_0)
で 0 埋めする.
def __lshift__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
ope1_s = f"{ope1.__bin:0{ope1.__bit}b}"
shift = ope2.value
cnt_0 = min(ope1.__bit, shift)
res_s = ope1_s[shift:] + ('0' * cnt_0)
return Int(res_s, ope1.__bit)
右シフトも同様.
算術右シフトとして実装する (>>>
ではなく >>
) ので,シフト分のパディングにはバイナリ文字列の MSB を使う (ope1_s[0] * cnt
).
def __rshift__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
ope1_s = f"{ope1.__bin:0{ope1.__bit}b}"
shift = ope2.value
cnt = min(ope1.__bit, shift)
res_s = (ope1_s[0] * cnt) + ope1_s[:-shift]
return Int(res_s, ope1.__bit)
~
が適用できるように __invert__()
を定義する.
ビット反転させたときのバイナリ表記の 10 進数の値は 補数 - 1
で計算できる.
4 bit int で考える.
x = b0101
をビット反転させると y = b1010
となり,x + y = b1111
だから y = b1111 - b0101
.
一方,x の補数は b10000 - b00101 = (b01111 + b00001) - b00101
.
よって,y = x の補数 - 1
.
n = ~Int(0, 4) # 0000
print(f"{n:04b}") # 1111
m = ~Int(5, 4) # 0101
print(f"{m:04b}") # 1010
def __invert__(self) -> Int:
return Int(f"{self.__cmpl(self.__bin) - 1:0{self.__bit}b}", self.__bit)
比較演算子用のスペシャルメソッドの実装
Int オブジェクトどうしはプロパティ value
の値の大小で比較する.Built-in の int と遜色なく取り扱えるようにするため,Int オブジェクトどうし以外は value
をそのまま比較のオペランドに置く.value
の値は int オブジェクトなので int の比較の定義がそのまま使われる.
例として <
用のスペシャルメソッド __lt__()
の実装.
print(Int32(-1) < Int32(5)) # True
print(Int32(-1) < 5) # True
print(-1 < Int32(5)) # True
print(Int32(-1) < 5.0) # True
print(-1.0 < Int32(5)) # True
def __lt__(self, other) -> bool:
if isinstance(other, Int):
return self.value < other.value
else:
return self.value < other
コード全体
ソースコードとテスト (https://github.com/e5pe0n/algorithm-training/tree/master/cracking_the_coding_interview_6th/chapter05/python/bitint)
from __future__ import annotations
from typing import Callable, Union, Tuple
import re
import operator
pat = re.compile(r"[01]+")
class Int:
def __init__(self, value: Union[int, str], bit: int):
if bit <= 0:
raise ValueError("bit must be greater than 0.")
self.__bit: int = bit
self.__max: int = (1 << (self.__bit - 1)) - 1
self.__min: int = -self.__max - 1
if isinstance(value, int):
# range check
if value < self.min:
raise ValueError(
f"underflow in {self.__bit}-bit integer value. \
value must be between [{self.__min}, {self.__max}]")
elif self.max < value:
raise ValueError(
f"overflow in {self.bit}-bit integer value. \
value must be between [{self.__min}, {self.__max}]")
if value >= 0:
self.__value: int = value
self.__bin: int = self.__value
else:
self.__value: int = value
self.__bin: int = self.__cmpl(abs(self.__value))
elif isinstance(value, str):
if len(value) > self.__bit:
raise ValueError(f"length of value must be between [1, {self.__bit}].")
if not pat.match(value):
raise ValueError("value must be a sequence of 0 and 1.")
if len(value) == self.__bit and value[0] == '1':
# MSB is 1 so we must calculate the complement
self.__bin: int = int(value, 2)
self.__value: int = -self.__cmpl(self.__bin)
else:
self.__value: int = int(value, 2)
self.__bin: int = self.__value
else:
raise TypeError("value must be an int or a str object.")
@property
def bit(self) -> int:
return self.__bit
@property
def bin(self) -> int:
return self.__bin
@property
def value(self) -> int:
return self.__value
@property
def max(self) -> int:
return self.__max
@property
def min(self) -> int:
return self.__min
def __cmpl(self, n: int) -> int:
return ((self.__max + 1) << 1) - n
def __validate_opes(self, other: Union[int, Int]) -> Tuple[Int, Int]:
if isinstance(other, int):
return (self, Int(other, self.__bit))
elif isinstance(other, Int):
greater_bit = max(self.__bit, other.bit)
return (Int(self.value, greater_bit), Int(other.value, greater_bit))
else:
raise TypeError("operands must be int or Int objects")
def __apply_num_op(self, ope1: Int, ope2: Int, op: Callable[[int, int], int]) -> Int:
res = op(ope1.value, ope2.value)
return Int(res, ope1.bit)
def __apply_bit_op(self, ope1: Int, ope2: Int, op: Callable[[int, int], int]) -> Int:
res = op(ope1.bin, ope2.bin)
return Int(f"{res:0{ope1.bit}b}", ope1.bit)
def __repr__(self) -> str:
return f"Int(bit={self.__bit}, value={self.__value}, bin={self.__bin})"
def __str__(self) -> str:
return str(self.value)
def __format__(self, format_spec: str) -> str:
t = format_spec[-1]
if t in ('b', 'o', 'x', 'X'):
return format(self.bin, format_spec)
else:
return format(self.value, format_spec)
def __abs__(self) -> Int:
return abs(self.__value)
def __neg__(self) -> Int:
return Int(-self.__value, self.__bit)
def __add__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope1, ope2, operator.add)
def __radd__(self, other: Union[int, Int]) -> Int:
return self + other
def __iadd__(self, other: Union[int, Int]) -> Int:
return self + other
def __sub__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope1, ope2, operator.sub)
def __rsub__(self, other: Union[int, Int]) -> Int:
return -self + other
def __isub__(self, other: Union[int, Int]) -> Int:
return self - other
def __mul__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope1, ope2, operator.mul)
def __rmul__(self, other: Union[int, Int]) -> Int:
return self * other
def __imul__(self, other: Union[int, Int]) -> Int:
return self * other
def __truediv__(self, other: Union[int, Int]) -> float:
ope1, ope2 = self.__validate_opes(other)
return ope1.value / ope2.value
def __rtruediv__(self, other: Union[int, Int]) -> float:
ope1, ope2 = self.__validate_opes(other)
return ope2.value / ope1.value
def __itruediv__(self, other: Union[int, Int]) -> float:
return self / other
def __floordiv__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope1, ope2, operator.floordiv)
def __rfloordiv__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope2, ope1, operator.floordiv)
def __ifloordiv__(self, other: Union[int, Int]) -> Int:
return self // other
def __mod__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope1, ope2, operator.mod)
def __rmod__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope2, ope1, operator.mod)
def __imod__(self, other: Union[int, Int]) -> Int:
return self % other
def __pow__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope1, ope2, operator.pow)
def __rpow__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_num_op(ope2, ope1, operator.pow)
def __ipow__(self, other: Union[int, Int]) -> Int:
return self ** other
def __invert__(self) -> Int:
return Int(f"{self.__cmpl(self.__bin) - 1:0{self.__bit}b}", self.__bit)
def __and__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_bit_op(ope1, ope2, operator.and_)
def __rand__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_bit_op(ope2, ope1, operator.and_)
def __iand__(self, other: Union[int, Int]) -> Int:
return self & other
def __or__(self, other):
ope1, ope2 = self.__validate_opes(other)
return self.__apply_bit_op(ope1, ope2, operator.or_)
def __ror__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_bit_op(ope2, ope1, operator.or_)
def __ior__(self, other: Union[int, Int]) -> Int:
return self | other
def __xor__(self, other):
ope1, ope2 = self.__validate_opes(other)
return self.__apply_bit_op(ope1, ope2, operator.xor)
def __rxor__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return self.__apply_bit_op(ope2, ope1, operator.xor)
def __ixor__(self, other: Union[int, Int]) -> Int:
return self ^ other
def __lshift__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
ope1_s = f"{ope1.__bin:0{ope1.__bit}b}"
shift = ope2.value
cnt_0 = min(ope1.__bit, shift)
res_s = ope1_s[shift:] + ('0' * cnt_0)
return Int(res_s, ope1.__bit)
def __rlshift__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return ope2 << ope1
def __ilshift__(self, other: Union[int, Int]) -> Int:
return self << other
def __rshift__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
ope1_s = f"{ope1.__bin:0{ope1.__bit}b}"
shift = ope2.value
cnt = min(ope1.__bit, shift)
res_s = (ope1_s[0] * cnt) + ope1_s[:-shift]
return Int(res_s, ope1.__bit)
def __rrshift__(self, other: Union[int, Int]) -> Int:
ope1, ope2 = self.__validate_opes(other)
return ope2 >> ope1
def __irshift__(self, other: Union[int, Int]) -> Int:
return self >> other
def __lt__(self, other) -> bool:
if isinstance(other, Int):
return self.value < other.value
else:
return self.value < other
def __le__(self, other) -> bool:
if isinstance(other, Int):
return self.value <= other.value
else:
return self.value <= other
def __gt__(self, other) -> bool:
if isinstance(other, Int):
return self.value > other.value
else:
return self.value > other
def __ge__(self, other) -> bool:
if isinstance(other, Int):
return self.value >= other.value
else:
return self.value >= other
def __eq__(self, other: Union[int, Int]) -> bool:
if isinstance(other, Int):
return self.value == other.value
else:
return self.value == other
def __ne__(self, other: Union[int, Int]) -> bool:
return not (self == other)
def __bool__(self) -> bool:
return bool(self.value)
def __index__(self) -> int:
return self.bin
def Int16(value):
return Int(value, 16)
def Int32(value):
return Int(value, 32)
def Int64(value):
return Int(value, 64)