diff --git a/benches/bigint/bench.mojo b/benches/bigint/bench.mojo index 6191c1f5..0786b549 100644 --- a/benches/bigint/bench.mojo +++ b/benches/bigint/bench.mojo @@ -19,7 +19,7 @@ q: Exit ========================================= """ ) - var command = input("Type the number of the bench you want to run: ") + var command = input("Type name of bench you want to run: ") if command == "add": bench_add() elif command == "mul": diff --git a/benches/biguint/bench.mojo b/benches/biguint/bench.mojo index 51a92146..654d1471 100644 --- a/benches/biguint/bench.mojo +++ b/benches/biguint/bench.mojo @@ -1,6 +1,7 @@ from bench_biguint_add import main as bench_add from bench_biguint_multiply import main as bench_multiply from bench_biguint_truncate_divide import main as bench_truncate_divide +from bench_biguint_from_string import main as bench_from_string fn main() raises: @@ -12,22 +13,26 @@ This is the BigUInt Benchmarks add: Add mul: Multiply trunc: Truncate divide (//) +fromstr: From string all: Run all benchmarks q: Exit ========================================= """ ) - var command = input("Type the number of bench you want to run: ") + var command = input("Type name of bench you want to run: ") if command == "add": bench_add() elif command == "mul": bench_multiply() elif command == "trunc": bench_truncate_divide() + elif command == "fromstr": + bench_from_string() elif command == "all": bench_add() bench_multiply() bench_truncate_divide() + bench_from_string() elif command == "q": return else: diff --git a/benches/biguint/bench_biguint_from_string.mojo b/benches/biguint/bench_biguint_from_string.mojo new file mode 100644 index 00000000..514df647 --- /dev/null +++ b/benches/biguint/bench_biguint_from_string.mojo @@ -0,0 +1,372 @@ +""" +Comprehensive benchmarks for BigUInt from_string constructor. +Compares performance against Python's built-in int() constructor with 20 diverse test cases. +""" + +from decimojo.biguint.biguint import BigUInt +import decimojo.biguint.arithmetics +from python import Python, PythonObject +from time import perf_counter_ns +import time +import os +from collections import List + + +fn open_log_file() raises -> PythonObject: + """ + Creates and opens a log file with a timestamp in the filename. + + Returns: + A file object opened for writing. + """ + var python = Python.import_module("builtins") + var datetime = Python.import_module("datetime") + + # Create logs directory if it doesn't exist + var log_dir = "./logs" + if not os.path.exists(log_dir): + os.makedirs(log_dir) + + # Generate a timestamp for the filename + var timestamp = String(datetime.datetime.now().isoformat()) + var log_filename = log_dir + "/benchmark_biguint_from_string_" + timestamp + ".log" + + print("Saving benchmark results to:", log_filename) + return python.open(log_filename, "w") + + +fn log_print(msg: String, log_file: PythonObject) raises: + """ + Prints a message to both the console and the log file. + + Args: + msg: The message to print. + log_file: The file object to write to. + """ + print(msg) + log_file.write(msg + "\n") + log_file.flush() # Ensure the message is written immediately + + +fn run_benchmark_from_string( + name: String, + value: String, + iterations: Int, + log_file: PythonObject, + mut speedup_factors: List[Float64], +) raises: + """ + Run a benchmark comparing Mojo BigUInt from_string constructor with Python int constructor. + + Args: + name: Name of the benchmark case. + value: String representation of the integer to parse. + iterations: Number of iterations to run. + log_file: File object for logging results. + speedup_factors: Mojo List to store speedup factors for averaging. + """ + log_print("\nBenchmark: " + name, log_file) + log_print("String value: " + value, log_file) + + # Get Python's built-in module + var py = Python.import_module("builtins") + + # Execute the operations once to verify correctness + try: + var mojo_result = BigUInt(value) + var py_result = py.int(value) + + # Display results for verification + log_print("Mojo parsed value: " + String(mojo_result), log_file) + log_print("Python parsed value: " + String(py_result), log_file) + + # Benchmark Mojo implementation + var t0 = perf_counter_ns() + for _ in range(iterations): + _ = BigUInt(value) + var mojo_time = (perf_counter_ns() - t0) / iterations + if mojo_time == 0: + mojo_time = 1 # Prevent division by zero + + # Benchmark Python implementation + t0 = perf_counter_ns() + for _ in range(iterations): + _ = py.int(value) + var python_time = (perf_counter_ns() - t0) / iterations + + # Calculate speedup factor + var speedup = python_time / mojo_time + speedup_factors.append(Float64(speedup)) + + # Print results with speedup comparison + log_print( + "Mojo from_string: " + String(mojo_time) + " ns per iteration", + log_file, + ) + log_print( + "Python int(): " + String(python_time) + " ns per iteration", + log_file, + ) + log_print("Speedup factor: " + String(speedup), log_file) + except e: + log_print("Error occurred during benchmark: " + String(e), log_file) + log_print("Skipping this benchmark case", log_file) + + +fn main() raises: + # Open log file + var log_file = open_log_file() + var datetime = Python.import_module("datetime") + + # Create a Mojo List to store speedup factors for averaging later + var speedup_factors = List[Float64]() + + # Display benchmark header with system information + log_print("=== DeciMojo BigUInt from_string Benchmark ===", log_file) + log_print("Time: " + String(datetime.datetime.now().isoformat()), log_file) + + # Try to get system info + try: + var platform = Python.import_module("platform") + log_print( + "System: " + + String(platform.system()) + + " " + + String(platform.release()), + log_file, + ) + log_print("Processor: " + String(platform.processor()), log_file) + log_print( + "Python version: " + String(platform.python_version()), log_file + ) + except: + log_print("Could not retrieve system information", log_file) + + var iterations = 1000 + + # Define benchmark cases + log_print( + "\nRunning from_string benchmarks with " + + String(iterations) + + " iterations each", + log_file, + ) + + # Case 1: Small integer (1-3 digits) + run_benchmark_from_string( + "Small integer (1-3 digits)", + "42", + iterations, + log_file, + speedup_factors, + ) + + # Case 2: Medium integer (10 digits) + run_benchmark_from_string( + "Medium integer (10 digits)", + "1234567890", + iterations, + log_file, + speedup_factors, + ) + + # Case 3: Large integer (50 digits) + run_benchmark_from_string( + "Large integer (50 digits)", + "12345678901234567890123456789012345678901234567890", + iterations, + log_file, + speedup_factors, + ) + + # Case 4: Very large integer (100 digits) + run_benchmark_from_string( + "Very large integer (100 digits)", + "1" + "0" * 99, # 10^99 + iterations, + log_file, + speedup_factors, + ) + + # Case 5: Power of 2 (100 digits) + run_benchmark_from_string( + "Power of 2 (2^256)", + "115792089237316195423570985008687907853269984665640564039457584007913129639936", + iterations, + log_file, + speedup_factors, + ) + + # Case 6: Large number with repeating pattern + run_benchmark_from_string( + "Large number with repeating pattern (50 digits)", + "12345" * 10, # Pattern repeats 10 times + iterations, + log_file, + speedup_factors, + ) + + # Case 7: Number with all 9s (stress test) + run_benchmark_from_string( + "Number with all 9s (100 digits)", + "9" * 100, # 100 nines + iterations, + log_file, + speedup_factors, + ) + + # Case 8: Number with all 1s + run_benchmark_from_string( + "Number with all 1s (100 digits)", + "1" * 100, # 100 ones + iterations, + log_file, + speedup_factors, + ) + + # Case 9: Number with alternating digits + run_benchmark_from_string( + "Number with alternating digits (100 digits)", + "10" * 50, # Alternating 1s and 0s + iterations, + log_file, + speedup_factors, + ) + + # Case 10: Prime number + run_benchmark_from_string( + "Large prime number", + "170141183460469231731687303715884105727", # Mersenne prime 2^127 - 1 + iterations, + log_file, + speedup_factors, + ) + + # Case 11: Factorial value + run_benchmark_from_string( + "Factorial value (25! = ~25 digits)", + "15511210043330985984000000", # 25! + iterations, + log_file, + speedup_factors, + ) + + # Case 12: String with leading zeros + run_benchmark_from_string( + "String with leading zeros", + "000123456789", + iterations, + log_file, + speedup_factors, + ) + + # Case 13: Very large fibonacci number + run_benchmark_from_string( + "Large Fibonacci number (Fib(1000), ~209 digits)", + "43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875", + iterations, + log_file, + speedup_factors, + ) + + # Case 14: String representation of a power of ten + run_benchmark_from_string( + "Power of ten (10^100)", + "1" + "0" * 100, + iterations, + log_file, + speedup_factors, + ) + + # Case 15: String representation of a number just below a power of ten + run_benchmark_from_string( + "Just below power of ten (10^50 - 1)", + "1" + "0" * 49 + "0" * 49 + "9", # 10^50 - 1 + iterations, + log_file, + speedup_factors, + ) + + # Case 16: Pi digits + run_benchmark_from_string( + "Pi digits (100 digits)", + "31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", + iterations, + log_file, + speedup_factors, + ) + + # Case 17: Very large number (500 digits) + run_benchmark_from_string( + "Very large number (500 digits)", + "1" + "0" * 499, # 10^499 + iterations, + log_file, + speedup_factors, + ) + + # Case 18: Very large number with complex pattern + run_benchmark_from_string( + "Complex pattern (200 digits)", + "123456789" * 22 + + "1234", # 9 digits repeated 22 times + 4 extra digits + iterations, + log_file, + speedup_factors, + ) + + # Case 19: Extremely large number (1000 digits) + run_benchmark_from_string( + "Extremely large number (1000 digits)", + "1" + "0" * 999, # 10^999 + iterations, + log_file, + speedup_factors, + ) + + # Case 20: Maximum UInt64 value + run_benchmark_from_string( + "Maximum UInt64 value", + "18446744073709551615", # 2^64 - 1 + iterations, + log_file, + speedup_factors, + ) + + # Calculate average speedup factor (ignoring any cases that might have failed) + if len(speedup_factors) > 0: + var sum_speedup: Float64 = 0.0 + for i in range(len(speedup_factors)): + sum_speedup += speedup_factors[i] + var average_speedup = sum_speedup / Float64(len(speedup_factors)) + + # Display summary + log_print("\n=== BigUInt from_string Benchmark Summary ===", log_file) + log_print( + "Benchmarked: " + + String(len(speedup_factors)) + + " different string parsing cases", + log_file, + ) + log_print( + "Each case ran: " + String(iterations) + " iterations", log_file + ) + log_print( + "Average speedup: " + String(average_speedup) + "×", log_file + ) + + # List all speedup factors + log_print("\nIndividual speedup factors:", log_file) + for i in range(len(speedup_factors)): + log_print( + String("Case {}: {}×").format( + i + 1, round(speedup_factors[i], 2) + ), + log_file, + ) + else: + log_print("\nNo valid benchmark cases were completed", log_file) + + # Close the log file + log_file.close() + print("Benchmark completed. Log file closed.") diff --git a/src/decimojo/bigint/bigint.mojo b/src/decimojo/bigint/bigint.mojo index 0bbb3565..b6618ecc 100644 --- a/src/decimojo/bigint/bigint.mojo +++ b/src/decimojo/bigint/bigint.mojo @@ -154,10 +154,8 @@ struct BigInt(Absable, IntableRaising, Writable): End of examples. """ + self.magnitude = BigUInt(List[UInt32](elements=words^)) self.sign = sign - self.magnitude = BigUInt(empty=True, capacity=len(words)) - for word in words: - self.magnitude.words.append(word[]) fn __init__(out self, value: Int) raises: """Initializes a BigInt from an Int. @@ -263,7 +261,7 @@ struct BigInt(Absable, IntableRaising, Writable): return Self(BigUInt(words^), sign) @staticmethod - fn from_uint128(value: UInt128, sign: Bool = False) -> Self: + fn from_uint128(value: UInt128, sign: Bool = False) raises -> Self: """Initializes a BigInt from a UInt128 value. Args: @@ -276,7 +274,7 @@ struct BigInt(Absable, IntableRaising, Writable): if value == 0: return Self() - var magnitude = BigUInt.from_uint128(value) + var magnitude = BigUInt.from_scalar(value) return Self(magnitude^, sign) @staticmethod diff --git a/src/decimojo/biguint/arithmetics.mojo b/src/decimojo/biguint/arithmetics.mojo index b9062d88..39c83dcb 100644 --- a/src/decimojo/biguint/arithmetics.mojo +++ b/src/decimojo/biguint/arithmetics.mojo @@ -49,9 +49,7 @@ fn add(x1: BigUInt, x2: BigUInt) raises -> BigUInt: return x1 # The result will have at most one more word than the longer operand - var result = BigUInt( - empty=True, capacity=max(len(x1.words), len(x2.words)) + 1 - ) + var words = List[UInt32](capacity=max(len(x1.words), len(x2.words)) + 1) var carry: UInt32 = 0 var ith: Int = 0 @@ -71,15 +69,15 @@ fn add(x1: BigUInt, x2: BigUInt) raises -> BigUInt: # Compute new word and carry carry = UInt32(sum_of_words // 1_000_000_000) - result.words.append(UInt32(sum_of_words % 1_000_000_000)) + words.append(UInt32(sum_of_words % 1_000_000_000)) ith += 1 # Handle final carry if it exists if carry > 0: - result.words.append(carry) + words.append(carry) - return result^ + return BigUInt(words=words^) fn add_inplace(mut x1: BigUInt, x2: BigUInt) raises: @@ -158,7 +156,7 @@ fn subtract(x1: BigUInt, x2: BigUInt) raises -> BigUInt: # Now it is safe to subtract the smaller number from the larger one # The result will have no more words than the larger operand - var result = BigUInt(empty=True, capacity=max(len(x1.words), len(x2.words))) + var words = List[UInt32](capacity=max(len(x1.words), len(x2.words))) var borrow: Int32 = 0 var ith: Int = 0 var difference: Int32 = 0 # Int32 is sufficient for the difference @@ -175,9 +173,10 @@ fn subtract(x1: BigUInt, x2: BigUInt) raises -> BigUInt: borrow = Int32(1) else: borrow = Int32(0) - result.words.append(UInt32(difference)) + words.append(UInt32(difference)) ith += 1 + var result = BigUInt(words=words^) result.remove_trailing_zeros() return result^ @@ -228,17 +227,16 @@ fn multiply(x1: BigUInt, x2: BigUInt) raises -> BigUInt: # CASE: One of the operands is one or negative one if x1.is_one(): return x2 - if x2.is_one(): return x1 # The maximum number of words in the result is the sum of the words in the operands var max_result_len = len(x1.words) + len(x2.words) - var result = BigUInt(empty=True, capacity=max_result_len) + var words = List[UInt32](capacity=max_result_len) # Initialize result words with zeros for _ in range(max_result_len): - result.words.append(0) + words.append(0) # Perform the multiplication word by word (from least significant to most significant) # x1 = x1[0] + x1[1] * 10^9 @@ -262,17 +260,18 @@ fn multiply(x1: BigUInt, x2: BigUInt) raises -> BigUInt: # plus the value already at this position in the result var product = UInt64(x1.words[i]) * UInt64( x2.words[j] - ) + carry + UInt64(result.words[i + j]) + ) + carry + UInt64(words[i + j]) # The lower 9 digits (base 10^9) go into the current word # The upper digits become the carry for the next position - result.words[i + j] = UInt32(product % 1_000_000_000) + words[i + j] = UInt32(product % 1_000_000_000) carry = product // 1_000_000_000 # If there is a carry left, add it to the next position if carry > 0: - result.words[i + len(x2.words)] += UInt32(carry) + words[i + len(x2.words)] += UInt32(carry) + var result = BigUInt(words=words^) result.remove_trailing_zeros() return result^ @@ -366,9 +365,10 @@ fn floor_divide(x1: BigUInt, x2: BigUInt) raises -> BigUInt: if word_shift >= len(x1.words): return BigUInt() # Create result with the remaining words - result = BigUInt(empty=True) + words = List[UInt32]() for i in range(word_shift, len(x1.words)): - result.words.append(x1.words[i]) + words.append(x1.words[i]) + result = BigUInt(words=words^) # Get the last word of the divisor var x2_word = x2.words[len(x2.words) - 1] @@ -609,15 +609,15 @@ fn divmod(x1: BigUInt, x2: BigUInt) raises -> Tuple[BigUInt, BigUInt]: # CASE: Duo words division by means of UInt64 if len(x1.words) <= 2 and len(x2.words) <= 2: - var result = BigUInt.from_uint64(x1.to_uint64() // x2.to_uint64()) - var remainder = BigUInt.from_uint64(x1.to_uint64() % x2.to_uint64()) + var result = BigUInt.from_scalar(x1.to_uint64() // x2.to_uint64()) + var remainder = BigUInt.from_scalar(x1.to_uint64() % x2.to_uint64()) return Tuple(result^, remainder^) # CASE: Divisor is 10^n # First remove the last words (10^9) and then shift the rest if BigUInt.is_power_of_10(x2): - var result: BigUInt - var remainder = BigUInt(empty=True) + var result = BigUInt(List[UInt32]()) + var remainder = BigUInt(List[UInt32]()) if len(x2.words) == 1: result = x1 @@ -627,7 +627,6 @@ fn divmod(x1: BigUInt, x2: BigUInt) raises -> Tuple[BigUInt, BigUInt]: if word_shift >= len(x1.words): return Tuple(BigUInt(), x1) # Create result with the remaining words - result = BigUInt(empty=True) for i in range(word_shift, len(x1.words)): result.words.append(x1.words[i]) for i in range(min(word_shift, len(x1.words))): @@ -659,7 +658,6 @@ fn divmod(x1: BigUInt, x2: BigUInt) raises -> Tuple[BigUInt, BigUInt]: else: remainder.words.append(carry) - # Remove leading zeros result.remove_trailing_zeros() remainder.remove_trailing_zeros() @@ -865,7 +863,7 @@ fn floor_divide_general(x1: BigUInt, x2: BigUInt) raises -> BigUInt: raise Error("Error in `floor_divide_general`: Division by zero") # Initialize result and remainder - var result = BigUInt(empty=True, capacity=len(x1.words)) + var result = BigUInt(List[UInt32](capacity=len(x1.words))) var remainder = x1 # Calculate significant digits @@ -1160,7 +1158,7 @@ fn divmod_general(x1: BigUInt, x2: BigUInt) raises -> Tuple[BigUInt, BigUInt]: raise Error("Error in `divmod_general`: Division by zero") # Initialize result and remainder - var result = BigUInt(empty=True, capacity=len(x1.words)) + var result = BigUInt(List[UInt32](capacity=len(x1.words))) var remainder = x1 # Calculate significant digits @@ -1263,7 +1261,7 @@ fn shift_words_left(num: BigUInt, positions: Int) -> BigUInt: if num.is_zero(): return BigUInt() - var result = BigUInt(empty=True, capacity=len(num.words) + positions) + var result = BigUInt(List[UInt32](capacity=len(num.words) + positions)) # Add zeros for the shift for _ in range(positions): @@ -1295,7 +1293,7 @@ fn power_of_10(n: Int) raises -> BigUInt: var words = n // 9 var remainder = n % 9 - var result = BigUInt(empty=True) + var result = BigUInt(List[UInt32]()) # Add trailing zeros for full power-of-billion words for _ in range(words): diff --git a/src/decimojo/biguint/biguint.mojo b/src/decimojo/biguint/biguint.mojo index e0b304da..46e4b333 100644 --- a/src/decimojo/biguint/biguint.mojo +++ b/src/decimojo/biguint/biguint.mojo @@ -76,33 +76,6 @@ struct BigUInt(Absable, IntableRaising, Writable): """Initializes a BigUInt with value 0.""" self.words = List[UInt32](UInt32(0)) - # FIXME: This is a temporary solution - # A unitialized BigUInt is not a good idea - fn __init__(out self, empty: Bool): - """Initializes an empty BigUInt. - - Args: - empty: A Bool value indicating whether the BigUInt is empty. - If True, the BigUInt is empty. - If False, the BigUInt is intialized with value 0. - """ - self.words = List[UInt32]() - if not empty: - self.words.append(UInt32(0)) - - fn __init__(out self, empty: Bool, capacity: Int): - """Initializes an empty BigUInt with a given capacity. - - Args: - empty: A Bool value indicating whether the BigUInt is empty. - If True, the BigUInt is empty. - If False, the BigUInt is intialized with value 0. - capacity: The capacity of the BigUInt. - """ - self.words = List[UInt32](capacity=capacity) - if not empty: - self.words.append(UInt32(0)) - fn __init__(out self, owned words: List[UInt32]): """Initializes a BigUInt from a list of UInt32 words. It does not check whether the list is empty or the words are invalid. @@ -149,18 +122,25 @@ struct BigUInt(Absable, IntableRaising, Writable): """ self = Self.from_int(value) - fn __init__(out self, value: String) raises: + fn __init__[dtype: DType](out self, value: Scalar[dtype]) raises: + """Initializes a BigUInt from a Mojo Scalar. + See `from_scalar()` for more information. + """ + self = Self.from_scalar(value) + + fn __init__(out self, value: String, ignore_sign: Bool = False) raises: """Initializes a BigUInt from a string representation. See `from_string()` for more information. """ - self = Self.from_string(value) + self = Self.from_string(value, ignore_sign=ignore_sign) # ===------------------------------------------------------------------=== # # Constructing methods that are not dunders # + # from_list(owned words: List[UInt32]) -> Self # from_words(*words: UInt32) -> Self # from_int(value: Int) -> Self - # from_uint128(value: UInt128) -> Self + # from_scalar[dtype: DType](value: Scalar[dtype]) -> Self # from_string(value: String) -> Self # ===------------------------------------------------------------------=== # @@ -247,76 +227,52 @@ struct BigUInt(Absable, IntableRaising, Writable): return Self(list_of_words^) @staticmethod - fn from_uint64(value: UInt64) raises -> Self: - """Initializes a BigUInt from an UInt64. + fn from_scalar[dtype: DType, //](value: Scalar[dtype]) raises -> Self: + """Initializes a BigUInt from a Mojo Scalar. Args: - value: The UInt64 value to be converted to BigUInt. + value: The Scalar value to be converted to BigUInt. Returns: - The BigUInt representation of the UInt64 value. - """ - if value == 0: - return Self() - - var list_of_words = List[UInt32]() - var remainder: UInt64 = value - var quotient: UInt64 - while remainder != 0: - quotient = remainder // 1_000_000_000 - remainder = remainder % 1_000_000_000 - list_of_words.append(UInt32(remainder)) - remainder = quotient - - return Self(list_of_words^) - - @staticmethod - fn from_uint128(value: UInt128) -> Self: - """Initializes a BigUInt from a UInt128 value. - - Args: - value: The UInt128 value to be converted to BigUInt. + The BigUInt representation of the Scalar value. - Returns: - The BigUInt representation of the UInt128 value. + Notes: + If the value is a floating-point number, it is converted to a string + with full precision before converting to BigUInt. + If the fractional part is not zero, an error is raised. """ - if value == 0: - return Self() - - var list_of_words = List[UInt32]() - var remainder: UInt128 = value - var quotient: UInt128 - while remainder != 0: - quotient = remainder // 1_000_000_000 - remainder = remainder % 1_000_000_000 - list_of_words.append(UInt32(remainder)) - remainder = quotient - - return Self(list_of_words^) - - @staticmethod - fn from_uint256(value: UInt256) -> Self: - """Initializes a BigUInt from a UInt256 value. - - Args: - value: The UInt256 value to be converted to BigUInt. + if value < 0: + raise Error("Error in `from_scalar()`: The value is negative") - Returns: - The BigUInt representation of the UInt256 value. - """ if value == 0: return Self() - var list_of_words = List[UInt32]() - var remainder: UInt256 = value - var quotient: UInt256 - while remainder != 0: - quotient = remainder // 1_000_000_000 - remainder = remainder % 1_000_000_000 - list_of_words.append(UInt32(remainder)) - remainder = quotient + @parameter + if dtype.is_integral(): + var list_of_words = List[UInt32]() + var remainder: Scalar[dtype] = value + var quotient: Scalar[dtype] + while remainder != 0: + quotient = remainder // 1_000_000_000 + remainder = remainder % 1_000_000_000 + list_of_words.append(UInt32(remainder)) + remainder = quotient + + return Self(list_of_words^) + + else: + if value != value: # Check for NaN + raise Error( + "Error in `from_scalar()`: Cannot convert NaN to BigUInt" + ) - return Self(list_of_words^) + # Convert to string with full precision + try: + return Self.from_string(String(value)) + except e: + raise Error("Error in `from_scalar()`: ", e) + + return Self() @staticmethod fn from_string(value: String, ignore_sign: Bool = False) raises -> BigUInt: @@ -357,7 +313,7 @@ struct BigUInt(Absable, IntableRaising, Writable): raise Error( "Error in `from_string`: The number is not an integer." ) - coef.resize(-scale) + coef.resize(len(coef) - scale) scale = 0 var number_of_digits = len(coef) - scale @@ -365,15 +321,11 @@ struct BigUInt(Absable, IntableRaising, Writable): if number_of_digits % 9 != 0: number_of_words += 1 - var result = Self(empty=True, capacity=number_of_words) + var result_words = List[UInt32](capacity=number_of_words) if scale == 0: # This is a true integer 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 end: Int = number_of_digits var start: Int while end >= 9: @@ -381,15 +333,15 @@ struct BigUInt(Absable, IntableRaising, Writable): var word: UInt32 = 0 for digit in coef[start:end]: word = word * 10 + UInt32(digit[]) - result.words.append(word) + result_words.append(word) end = start if end > 0: var word: UInt32 = 0 for digit in coef[0:end]: word = word * 10 + UInt32(digit[]) - result.words.append(word) + result_words.append(word) - return result + return Self(result_words^) else: # scale < 0 # This is a true integer with postive exponent @@ -397,7 +349,7 @@ struct BigUInt(Absable, IntableRaising, Writable): var remaining_trailing_zero_digits = -scale % 9 for _ in range(number_of_trailing_zero_words): - result.words.append(UInt32(0)) + result_words.append(UInt32(0)) for _ in range(remaining_trailing_zero_digits): coef.append(UInt8(0)) @@ -409,15 +361,15 @@ struct BigUInt(Absable, IntableRaising, Writable): var word: UInt32 = 0 for digit in coef[start:end]: word = word * 10 + UInt32(digit[]) - result.words.append(word) + result_words.append(word) end = start if end > 0: var word: UInt32 = 0 for digit in coef[0:end]: word = word * 10 + UInt32(digit[]) - result.words.append(word) + result_words.append(word) - return result + return Self(result_words^) # ===------------------------------------------------------------------=== # # Output dunders, type-transfer dunders @@ -786,11 +738,6 @@ struct BigUInt(Absable, IntableRaising, Writable): """Returns True if this BigUInt represents two.""" return len(self.words) == 1 and self.words[0] == 2 - @always_inline - fn number_of_words(self) -> Int: - """Returns the number of words in the BigInt.""" - return len(self.words) - fn is_power_of_10(x: BigUInt) -> Bool: """Check if x is a power of 10.""" for i in range(len(x.words) - 1): @@ -811,6 +758,11 @@ struct BigUInt(Absable, IntableRaising, Writable): return True return False + @always_inline + fn number_of_words(self) -> Int: + """Returns the number of words in the BigInt.""" + return len(self.words) + fn internal_representation(self): """Prints the internal representation details of a BigUInt.""" print("\nInternal Representation Details of BigUInt") @@ -826,6 +778,23 @@ struct BigUInt(Absable, IntableRaising, Writable): ) print("--------------------------------") + fn ith_digit(self, i: Int) raises -> UInt8: + """Returns the ith digit of the BigUInt.""" + if i < 0: + raise Error("Error in `ith_digit()`: The index is negative") + if i >= len(self.words) * 9: + return 0 + var word_index = i // 9 + var digit_index = i % 9 + if word_index >= len(self.words): + return 0 + var word = self.words[word_index] + var digit: UInt32 = 0 + for _ in range(digit_index): + word = word // 10 + digit = word % 10 + return UInt8(digit) + fn is_unitialized(self) -> Bool: """Returns True if the BigUInt is uninitialized.""" return len(self.words) == 0 diff --git a/src/decimojo/prelude.mojo b/src/decimojo/prelude.mojo index 1e3ff105..b093b4dd 100644 --- a/src/decimojo/prelude.mojo +++ b/src/decimojo/prelude.mojo @@ -1,7 +1,4 @@ # ===----------------------------------------------------------------------=== # -# DeciMojo: A fixed-point decimal arithmetic library in Mojo -# https://github.com/forfudan/decimojo -# # Copyright 2025 Yuhao Zhu # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/decimojo/rounding_mode.mojo b/src/decimojo/rounding_mode.mojo index f4c13334..972c5cd6 100644 --- a/src/decimojo/rounding_mode.mojo +++ b/src/decimojo/rounding_mode.mojo @@ -1,7 +1,4 @@ # ===----------------------------------------------------------------------=== # -# DeciMojo: A fixed-point decimal arithmetic library in Mojo -# https://github.com/forfudan/decimojo -# # Copyright 2025 Yuhao Zhu # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/decimojo/utility.mojo b/src/decimojo/utility.mojo index 6e3aaa51..69d44624 100644 --- a/src/decimojo/utility.mojo +++ b/src/decimojo/utility.mojo @@ -1,7 +1,4 @@ # ===----------------------------------------------------------------------=== # -# DeciMojo: A fixed-point decimal arithmetic library in Mojo -# https://github.com/forfudan/decimojo -# # Copyright 2025 Yuhao Zhu # # Licensed under the Apache License, Version 2.0 (the "License");