11import math
2- from decimal import Decimal , ROUND_HALF_UP
2+ from collections .abc import Iterable , Reversible
3+ from decimal import ROUND_HALF_UP , Decimal
34from functools import partial
45from itertools import chain , cycle
56from operator import add
6- from typing import Dict , Iterable , List , Optional , Reversible , Tuple , Union
77
88__all__ = [
99 "SymbolTable" ,
10- "decimal " ,
10+ "alphabet " ,
1111 "base36" ,
1212 "base62" ,
13- "alphabet " ,
13+ "decimal " ,
1414]
1515
1616
@@ -29,27 +29,26 @@ def is_prefix_code(strings: Iterable[str]) -> bool:
2929
3030class SymbolTable :
3131 def __init__ (
32- self , symbols : Iterable [str ], symbol_map : Optional [ Dict [ str , int ]] = None
33- ):
32+ self , symbols : Iterable [str ], symbol_map : dict [ str , int ] | None = None
33+ ) -> None :
3434 symbols = list (symbols )
3535 if not symbol_map :
3636 symbol_map = dict ((c , i ) for i , c in enumerate (symbols ))
3737
3838 symbol_values = set (symbol_map .values ())
3939 for i in range (len (symbols )):
4040 if i not in symbol_values :
41- raise ValueError (
42- f"{ len (symbols )} symbols given but { i } not found in symbol table"
43- )
41+ msg = f"{ len (symbols )} symbols given but { i } not found in symbol table"
42+ raise ValueError (msg )
4443
4544 self .num2sym = symbols
4645 self .sym2num = symbol_map
4746 self .max_base = len (symbols )
4847 self .is_prefix_code = is_prefix_code (symbols )
4948
50- def number_to_digits (self , num : int , base : Optional [ int ] = None ) -> List [int ]:
49+ def number_to_digits (self , num : int , base : int | None = None ) -> list [int ]:
5150 base = base or self .max_base
52- digits : List [int ] = []
51+ digits : list [int ] = []
5352 while num >= 1 :
5453 digits .append (num % base )
5554 num = num // base
@@ -61,20 +60,19 @@ def number_to_digits(self, num: int, base: Optional[int] = None) -> List[int]:
6160 def digits_to_string (self , digits : Iterable [int ]) -> str :
6261 return "" .join ([self .num2sym [n ] for n in digits ])
6362
64- def string_to_digits (self , string : Iterable [str ]) -> List [int ]:
63+ def string_to_digits (self , string : Iterable [str ]) -> list [int ]:
6564 if isinstance (string , str ):
6665 if not self .is_prefix_code :
67- raise ValueError (
66+ msg = (
6867 "Parsing without prefix code is unsupported. "
6968 "Pass in array of stringy symbols?"
7069 )
70+ raise ValueError (msg )
7171 string = (c for c in string if c in self .sym2num )
7272
7373 return [self .sym2num [c ] for c in string ]
7474
75- def digits_to_number (
76- self , digits : Reversible [int ], base : Optional [int ] = None
77- ) -> int :
75+ def digits_to_number (self , digits : Reversible [int ], base : int | None = None ) -> int :
7876 base = base or self .max_base
7977 current_base = 1
8078 accum = 0
@@ -83,15 +81,15 @@ def digits_to_number(
8381 current_base *= base
8482 return accum
8583
86- def number_to_string (self , num : int , base : Optional [ int ] = None ) -> str :
84+ def number_to_string (self , num : int , base : int | None = None ) -> str :
8785 return self .digits_to_string (self .number_to_digits (num , base = base ))
8886
89- def string_to_number (self , num : Iterable [str ], base : Optional [ int ] = None ) -> int :
87+ def string_to_number (self , num : Iterable [str ], base : int | None = None ) -> int :
9088 return self .digits_to_number (self .string_to_digits (num ), base = base )
9189
9290 def round_fraction (
93- self , numerator : int , denominator : int , base : Optional [ int ] = None
94- ) -> List [int ]:
91+ self , numerator : int , denominator : int , base : int | None = None
92+ ) -> list [int ]:
9593 base = base or self .max_base
9694 places = math .ceil (math .log (denominator ) / math .log (base ))
9795 scale = pow (base , places )
@@ -105,12 +103,12 @@ def round_fraction(
105103
106104 def mudder (
107105 self ,
108- a : Union [ Iterable [str ], int ] = "" ,
106+ a : Iterable [str ] | int = "" ,
109107 b : Iterable [str ] = "" ,
110108 num_strings : int = 1 ,
111- base : Optional [ int ] = None ,
112- num_divisions : Optional [ int ] = None ,
113- ) -> List [str ]:
109+ base : int | None = None ,
110+ num_divisions : int | None = None ,
111+ ) -> list [str ]:
114112 if isinstance (a , int ):
115113 num_strings = a
116114 a = ""
@@ -135,8 +133,8 @@ def mudder(
135133
136134
137135def long_div (
138- numerator : List [int ], denominator : int , base : int
139- ) -> Tuple [ List [int ], int ]:
136+ numerator : list [int ], denominator : int , base : int
137+ ) -> tuple [ list [int ], int ]:
140138 result = []
141139 remainder = 0
142140 for current in numerator :
@@ -146,15 +144,16 @@ def long_div(
146144 return result , remainder
147145
148146
149- def long_sub_same_len ( # noqa: C901
150- a : List [int ],
151- b : List [int ],
147+ def long_sub_same_len (
148+ a : list [int ],
149+ b : list [int ],
152150 base : int ,
153- remainder : Optional [ Tuple [ int , int ]] = None ,
154- denominator = 0 ,
155- ) -> Tuple [ List [int ], int ]:
151+ remainder : tuple [ int , int ] | None = None ,
152+ denominator : int = 0 ,
153+ ) -> tuple [ list [int ], int ]:
156154 if len (a ) != len (b ):
157- raise ValueError ("a and b should have same length" )
155+ msg = "a and b should have same length"
156+ raise ValueError (msg )
158157
159158 a = a .copy () # pre-emptively copy
160159 if remainder :
@@ -169,7 +168,8 @@ def long_sub_same_len( # noqa: C901
169168 ret [i ] = a [i ] - b [i ]
170169 continue
171170 if i == 0 :
172- raise ValueError ("Cannot go negative" )
171+ msg = "Cannot go negative"
172+ raise ValueError (msg )
173173 do_break = False
174174 # look for a digit to the left to borrow from
175175 for j in reversed (range (i )):
@@ -189,18 +189,20 @@ def long_sub_same_len( # noqa: C901
189189 break
190190 if do_break :
191191 continue
192- raise ValueError ("Failed to find digit to borrow from" )
192+ msg = "Failed to find digit to borrow from"
193+ raise ValueError (msg )
193194 if remainder :
194195 # result, remainder
195196 return ret [:- 1 ], ret [- 1 ]
196197 return ret , 0
197198
198199
199200def long_add_same_len (
200- a : List [int ], b : List [int ], base : int , remainder : int , denominator : int
201- ) -> Tuple [ List [int ], bool , int , int ]:
201+ a : list [int ], b : list [int ], base : int , remainder : int , denominator : int
202+ ) -> tuple [ list [int ], bool , int , int ]:
202203 if len (a ) != len (b ):
203- raise ValueError ("a and b should have same length" )
204+ msg = "a and b should have same length"
205+ raise ValueError (msg )
204206
205207 carry = remainder >= denominator
206208 res = b .copy ()
@@ -215,22 +217,23 @@ def long_add_same_len(
215217 return res , carry , remainder , denominator
216218
217219
218- def right_pad (arr : List [int ], to_length : int , val : int = 0 ) -> List [int ]:
220+ def right_pad (arr : list [int ], to_length : int , val : int = 0 ) -> list [int ]:
219221 pad_len = to_length - len (arr )
220222 if pad_len > 0 :
221223 return arr + [val ] * pad_len
222224 return arr
223225
224226
225227def long_linspace (
226- a : List [int ], b : List [int ], base : int , n : int , m : int
227- ) -> List [ Tuple [ List [int ], int , int ]]:
228+ a : list [int ], b : list [int ], base : int , n : int , m : int
229+ ) -> list [ tuple [ list [int ], int , int ]]:
228230 if len (a ) < len (b ):
229231 a = right_pad (a , len (b ))
230232 elif len (b ) < len (a ):
231233 b = right_pad (b , len (a ))
232234 if a == b :
233- raise ValueError ("Start and end strings are lexicographically inseperable" )
235+ msg = "Start and end strings are lexicographically inseparable"
236+ raise ValueError (msg )
234237 a_div , a_div_rem = long_div (a , m , base )
235238 b_div , b_div_rem = long_div (b , m , base )
236239
@@ -252,21 +255,21 @@ def long_linspace(
252255 return ret
253256
254257
255- def left_pad (arr : List [int ], to_length : int , val : int = 0 ) -> List [int ]:
258+ def left_pad (arr : list [int ], to_length : int , val : int = 0 ) -> list [int ]:
256259 pad_len = to_length - len (arr )
257260 if pad_len > 0 :
258261 return [val ] * pad_len + arr
259262 return arr
260263
261264
262- def chop_digits (rock : List [int ], water : List [int ]) -> List [int ]:
265+ def chop_digits (rock : list [int ], water : list [int ]) -> list [int ]:
263266 for i in range (len (water )):
264267 if water [i ] and (i >= len (rock ) or rock [i ] != water [i ]):
265268 return water [: i + 1 ]
266269 return water
267270
268271
269- def lexicographic_less_than_array (a : List [int ], b : List [int ]) -> bool :
272+ def lexicographic_less_than_array (a : list [int ], b : list [int ]) -> bool :
270273 n = min (len (a ), len (b ))
271274 for i in range (n ):
272275 if a [i ] == b [i ]:
@@ -275,7 +278,7 @@ def lexicographic_less_than_array(a: List[int], b: List[int]) -> bool:
275278 return len (a ) < len (b )
276279
277280
278- def chop_successive_digits (strings : List [ List [int ]]) -> List [ List [int ]]:
281+ def chop_successive_digits (strings : list [ list [int ]]) -> list [ list [int ]]:
279282 reversed_ = not lexicographic_less_than_array (strings [0 ], strings [1 ])
280283 if reversed_ :
281284 strings .reverse ()
@@ -300,6 +303,7 @@ def chop_successive_digits(strings: List[List[int]]) -> List[List[int]]:
300303 digits + alpha_lower + alpha_upper ,
301304 # 0-9, then 10-35 repeating (for upper and lower case)
302305 chain (range (10 ), cycle (map (partial (add , 10 ), range (26 )))),
306+ strict = False ,
303307 )
304308 ),
305309)
0 commit comments