diff --git a/src/decimojo/bigint/arithmetics.mojo b/src/decimojo/bigint/arithmetics.mojo index d772ae5d..12579197 100644 --- a/src/decimojo/bigint/arithmetics.mojo +++ b/src/decimojo/bigint/arithmetics.mojo @@ -38,9 +38,9 @@ fn add(x1: BigInt, x2: BigInt) raises -> BigInt: """ # If one of the numbers is zero, return the other number - if len(x1.words) == 1 and x1.words[0] == 0: + if x1.is_zero(): return x2 - if len(x2.words) == 1 and x2.words[0] == 0: + if x2.is_zero(): return x1 # If signs are different, we use `subtract` instead @@ -81,7 +81,7 @@ fn add(x1: BigInt, x2: BigInt) raises -> BigInt: if carry > 0: result.words.append(carry) - return result + return result^ fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt: @@ -157,7 +157,7 @@ fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt: while len(result.words) > 1 and result.words[len(result.words) - 1] == 0: result.words.resize(len(result.words) - 1) - return result + return result^ fn negative(x: BigInt) -> BigInt: @@ -171,7 +171,7 @@ fn negative(x: BigInt) -> BigInt: """ var result = x result.sign = not result.sign - return result + return result^ fn absolute(x: BigInt) -> BigInt: @@ -184,8 +184,6 @@ fn absolute(x: BigInt) -> BigInt: A new BigInt containing the absolute value of x. """ if x.sign: - var result = x - result.sign = False - return result + return -x else: return x diff --git a/src/decimojo/bigint/bigint.mojo b/src/decimojo/bigint/bigint.mojo index 9c407139..07f33ef1 100644 --- a/src/decimojo/bigint/bigint.mojo +++ b/src/decimojo/bigint/bigint.mojo @@ -25,6 +25,7 @@ mathematical methods that do not implement a trait. from memory import UnsafePointer import testing +import time import decimojo.bigint.arithmetics import decimojo.bigint.comparison @@ -244,7 +245,7 @@ struct BigInt(Absable, IntableRaising, Writable): @staticmethod fn from_string(value: String) raises -> BigInt: """Initializes a BigInt from a string representation. - The string is normalized with `deciomojo.str.parse_string_of_number()`. + The string is normalized with `deciomojo.str.parse_numeric_string()`. Args: value: The string representation of the BigInt. @@ -252,33 +253,32 @@ struct BigInt(Absable, IntableRaising, Writable): Returns: The BigInt representation of the string. """ - - var coef_string: String + var coef: List[UInt8] var scale: Int var sign: Bool - coef_string, scale, sign = decimojo.str.parse_string_of_number(value) + coef, scale, sign = decimojo.str.parse_numeric_string(value) # Check if the number is zero - if coef_string == "0": - return Self() + if len(coef) == 1 and coef[0] == UInt8(0): + return Self.from_raw_words(UInt32(0), sign=sign) # Check whether the number is an integer # If the fractional part is not zero, raise an error # If the fractional part is zero, remove the fractional part if scale > 0: - if scale >= len(coef_string): + if scale >= len(coef): raise Error( "Error in `from_string`: The number is not an integer." ) for i in range(1, scale + 1): - if coef_string[-i] != "0": + if coef[-i] != 0: raise Error( "Error in `from_string`: The number is not an integer." ) - coef_string = coef_string[:-scale] + coef.resize(-scale) scale = 0 - var number_of_digits = len(coef_string) - scale + var number_of_digits = len(coef) - scale var number_of_words = number_of_digits // 9 if number_of_digits % 9 != 0: number_of_words += 1 @@ -288,23 +288,24 @@ struct BigInt(Absable, IntableRaising, Writable): if scale == 0: # This is a true integer - var number_of_digits = len(coef_string) + var number_of_digits = len(coef) var number_of_words = number_of_digits // 9 if number_of_digits % 9 != 0: number_of_words += 1 - var result = Self(empty=True, capacity=number_of_words) - result.sign = sign - var end: Int = number_of_digits var start: Int while end >= 9: start = end - 9 - var word = UInt32(Int(coef_string[start:end])) + var word: UInt32 = 0 + for digit in coef[start:end]: + word = word * 10 + UInt32(digit[]) result.words.append(word) end = start if end > 0: - var word = UInt32(Int(coef_string[0:end])) + var word: UInt32 = 0 + for digit in coef[0:end]: + word = word * 10 + UInt32(digit[]) result.words.append(word) return result @@ -317,17 +318,22 @@ struct BigInt(Absable, IntableRaising, Writable): for _ in range(number_of_trailing_zero_words): result.words.append(UInt32(0)) - coef_string += "0" * remaining_trailing_zero_digits + for _ in range(remaining_trailing_zero_digits): + coef.append(UInt8(0)) var end: Int = number_of_digits + scale + remaining_trailing_zero_digits var start: Int while end >= 9: start = end - 9 - var word = UInt32(Int(coef_string[start:end])) + var word: UInt32 = 0 + for digit in coef[start:end]: + word = word * 10 + UInt32(digit[]) result.words.append(word) end = start if end > 0: - var word = UInt32(Int(coef_string[0:end])) + var word: UInt32 = 0 + for digit in coef[0:end]: + word = word * 10 + UInt32(digit[]) result.words.append(word) return result @@ -464,6 +470,21 @@ struct BigInt(Absable, IntableRaising, Writable): fn __sub__(self, other: Self) raises -> Self: return decimojo.bigint.arithmetics.subtract(self, other) + # ===------------------------------------------------------------------=== # + # Basic binary augmented arithmetic assignments dunders + # These methods are called to implement the binary augmented arithmetic + # assignments + # (+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=) + # ===------------------------------------------------------------------=== # + + @always_inline + fn __iadd__(mut self, other: Self) raises: + self = decimojo.bigint.arithmetics.add(self, other) + + @always_inline + fn __isub__(mut self, other: Self) raises: + self = decimojo.bigint.arithmetics.subtract(self, other) + # ===------------------------------------------------------------------=== # # Other methods # ===------------------------------------------------------------------=== # diff --git a/src/decimojo/str.mojo b/src/decimojo/str.mojo index 066690ef..c42c1393 100644 --- a/src/decimojo/str.mojo +++ b/src/decimojo/str.mojo @@ -16,8 +16,12 @@ """String manipulation functions.""" +import time -fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: + +fn parse_numeric_string( + value: String, +) raises -> Tuple[List[UInt8], Int, Bool]: """Parse the string of a number into normalized parts. Args: @@ -25,7 +29,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: Returns: A tuple of: - - Normalized string which represents an integer. + - Normalized coefficient as List[UInt8] which represents an integer. - Scale of the number. - Sign of the number. @@ -45,12 +49,13 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: Examples: ```console - parse_string("123") -> ("123", 0) - parse_string("123.456") -> ("123456", 3) - parse_string("123.456e3") -> ("123456", 0) - parse_string("123.456e-3") -> ("123456", 6) - parse_string("123.456e+10") -> ("123456", -7) - parse_string("0.00123456") -> ("123456", 8) + parse_string("123") -> (123, 0, False) + parse_string("123.456") -> (123456, 3, False) + parse_string("123.456e3") -> (123456, 0, False) + parse_string("123.456e-3") -> (123456, 6, False) + parse_string("123.456e+10") -> (123456, -7, False) + parse_string("0.00123456") -> (123456, 8, False) + parse_string("-123") -> (123, 0, True) ``` End of examples. """ @@ -60,7 +65,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: var value_bytes_len = len(value_bytes) if value_bytes_len == 0: - return Tuple(String(""), 0, False) + raise Error("Error in `parse_numeric_string`: Empty string.") if value_bytes_len != value_string_slice.char_length(): raise Error( @@ -81,19 +86,20 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: var mantissa_sign: Bool = False # True if negative var exponent_sign: Bool = False # True if negative - var coef_string: String = "" + var coef: List[UInt8] = List[UInt8](capacity=value_bytes_len) var scale: Int = 0 var raw_exponent: Int = 0 - for code in value_bytes: + for code_ptr in value_bytes: + var code = code_ptr[] # If the char is " ", skip it - if code[] == 32: + if code == 32: pass # If the char is "," or "_", skip it - elif code[] == 44 or code[] == 95: + elif code == 44 or code == 95: unexpected_end_char = True # If the char is "-" - elif code[] == 45: + elif code == 45: unexpected_end_char = True if exponent_sign_read: raise Error("Minus sign cannot appear twice in exponent.") @@ -106,7 +112,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: mantissa_sign = True mantissa_sign_read = True # If the char is "+" - elif code[] == 43: + elif code == 43: unexpected_end_char = True if exponent_sign_read: raise Error("Plus sign cannot appear twice in exponent.") @@ -117,7 +123,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: else: mantissa_sign_read = True # If the char is "." - elif code[] == 46: + elif code == 46: unexpected_end_char = False if decimal_point_read: raise Error("Decimal point can only appear once.") @@ -125,7 +131,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: decimal_point_read = True mantissa_sign_read = True # If the char is "e" or "E" - elif code[] == 101 or code[] == 69: + elif code == 101 or code == 69: unexpected_end_char = True if exponent_notation_read: raise Error("Exponential notation can only appear once.") @@ -135,7 +141,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: exponent_notation_read = True # If the char is a digit 0 - elif code[] == 48: + elif code == 48: unexpected_end_char = False # Exponent part @@ -150,41 +156,42 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: mantissa_start = True if mantissa_significant_start: - coef_string += "0" + coef.append(0) if decimal_point_read: scale += 1 # If the char is a digit 1 - 9 - elif code[] >= 49 and code[] <= 57: + elif code >= 49 and code <= 57: unexpected_end_char = False # Exponent part if exponent_notation_read: exponent_start = True - raw_exponent = raw_exponent * 10 + Int(code[] - 48) + raw_exponent = raw_exponent * 10 + Int(code - 48) # Mantissa part else: mantissa_significant_start = True mantissa_start = True - coef_string += String(code[] - 48) + coef.append(code - 48) if decimal_point_read: scale += 1 else: raise Error( "Invalid character in the string of the number: {}".format( - chr(Int(code[])) + chr(Int(code)) ) ) if unexpected_end_char: raise Error("Unexpected end character in the string of the number.") - if len(coef_string) == 0: + if len(coef) == 0: + # For example, "0000." if mantissa_start: - coef_string = "0" + coef.append(0) else: raise Error("No digits found in the string of the number.") @@ -200,4 +207,4 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]: # 1.234e8 -> 1234e5 -> 1234 and scale = -5 scale -= raw_exponent - return Tuple(coef_string, scale, mantissa_sign) + return Tuple(coef, scale, mantissa_sign)