diff --git a/benches/bigdecimal/bench.mojo b/benches/bigdecimal/bench.mojo index 1cdb264..a6dffd9 100644 --- a/benches/bigdecimal/bench.mojo +++ b/benches/bigdecimal/bench.mojo @@ -2,6 +2,7 @@ from bench_bigdecimal_add import main as bench_add from bench_bigdecimal_subtract import main as bench_sub from bench_bigdecimal_multiply import main as bench_multiply from bench_bigdecimal_divide import main as bench_divide +from bench_bigdecimal_scale_up_by_power_of_10 import main as bench_scale_up fn main() raises: @@ -17,6 +18,8 @@ div: Divide (true divide) all: Run all benchmarks q: Exit ========================================= +scaleup: Scale up by power of 10 +========================================= """ ) var command = input("Type name of bench you want to run: ") @@ -35,6 +38,8 @@ q: Exit bench_divide() elif command == "q": return + elif command == "scaleup": + bench_scale_up() else: print("Invalid input") main() diff --git a/benches/bigdecimal/bench_bigdecimal_scale_up_by_power_of_10.mojo b/benches/bigdecimal/bench_bigdecimal_scale_up_by_power_of_10.mojo new file mode 100644 index 0000000..2637738 --- /dev/null +++ b/benches/bigdecimal/bench_bigdecimal_scale_up_by_power_of_10.mojo @@ -0,0 +1,322 @@ +"""Benchmarks for BigUInt scale_up_by_power_of_10 function.""" + +from decimojo.biguint.biguint import BigUInt +import decimojo.biguint.arithmetics +from time import perf_counter_ns +import time +import os +from collections import List +from python import Python, PythonObject + + +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_scale_up_" + 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.""" + print(msg) + log_file.write(msg + "\n") + log_file.flush() # Ensure the message is written immediately + + +fn run_benchmark_scale_up( + name: String, + value: String, + power: Int, + iterations: Int, + log_file: PythonObject, + mut speedup_factors: List[Float64], +) raises: + """ + Run a benchmark for Mojo BigUInt scale_up_by_power_of_10 function. + + Args: + name: Name of the benchmark case. + value: String representation of the BigUInt. + power: The power of 10 to scale up by. + iterations: Number of iterations to run. + log_file: File object for logging results. + speedup_factors: Mojo List to store benchmark times. + """ + log_print("\nBenchmark: " + name, log_file) + log_print("BigUInt value: " + value, log_file) + log_print("Power of 10: " + String(power), log_file) + + # Set up Mojo value + var mojo_value = BigUInt(value) + + # Benchmark Mojo implementation + var t0 = perf_counter_ns() + for _ in range(iterations): + _ = decimojo.biguint.arithmetics.scale_up_by_power_of_10( + mojo_value, power + ) + var mojo_time = (perf_counter_ns() - t0) / iterations + + # Print results + log_print( + "Mojo scale_up: " + String(mojo_time) + " ns per iteration", log_file + ) + speedup_factors.append(Float64(mojo_time)) + + +fn main() raises: + # Open log file + var log_file = open_log_file() + var datetime = Python.import_module("datetime") + + # Create a Mojo List to store benchmark times + var speedup_factors = List[Float64]() + + # Display benchmark header with system information + log_print( + "=== DeciMojo BigUInt scale_up_by_power_of_10 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 scale_up_by_power_of_10 benchmarks with " + + String(iterations) + + " iterations each", + log_file, + ) + + # Case 1: Small power + run_benchmark_scale_up( + "Small power", "12345", 5, iterations, log_file, speedup_factors + ) + + # Case 2: Medium power + run_benchmark_scale_up( + "Medium power", "12345", 20, iterations, log_file, speedup_factors + ) + + # Case 3: Large power + run_benchmark_scale_up( + "Large power", "12345", 50, iterations, log_file, speedup_factors + ) + + # Case 4: Very large power + run_benchmark_scale_up( + "Very large power", "12345", 100, iterations, log_file, speedup_factors + ) + + # Case 5: Small number, small power + run_benchmark_scale_up( + "Small number, small power", + "1", + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 6: Large number, small power + run_benchmark_scale_up( + "Large number, small power", + "9" * 50, + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 7: Small number, large power + run_benchmark_scale_up( + "Small number, large power", + "1", + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 8: Large number, large power + run_benchmark_scale_up( + "Large number, large power", + "9" * 50, + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 9: Power of 10 + run_benchmark_scale_up( + "Power of 10", "1", 10, iterations, log_file, speedup_factors + ) + + # Case 10: Power of 10, large number + run_benchmark_scale_up( + "Power of 10, large number", + "1234567890", + 10, + iterations, + log_file, + speedup_factors, + ) + + # Case 11: Zero value + run_benchmark_scale_up( + "Zero value", "0", 10, iterations, log_file, speedup_factors + ) + + # Case 12: Zero power + run_benchmark_scale_up( + "Zero power", "12345", 0, iterations, log_file, speedup_factors + ) + + # Case 13: Large number, zero power + run_benchmark_scale_up( + "Large number, zero power", + "9" * 50, + 0, + iterations, + log_file, + speedup_factors, + ) + + # Case 14: Number with many digits, small power + run_benchmark_scale_up( + "Number with many digits, small power", + "1" * 100, + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 15: Number with many digits, large power + run_benchmark_scale_up( + "Number with many digits, large power", + "1" * 100, + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 16: Number with repeating digits, small power + run_benchmark_scale_up( + "Number with repeating digits, small power", + "12345" * 20, + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 17: Number with repeating digits, large power + run_benchmark_scale_up( + "Number with repeating digits, large power", + "12345" * 20, + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 18: Number close to max UInt32, small power + run_benchmark_scale_up( + "Number close to max UInt32, small power", + "4294967295", + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 19: Number close to max UInt32, large power + run_benchmark_scale_up( + "Number close to max UInt32, large power", + "4294967295", + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 20: Large power, large number + run_benchmark_scale_up( + "Large power, large number", + "12345678901234567890", + 100, + iterations, + log_file, + speedup_factors, + ) + + # Calculate average benchmark time + var sum_time: Float64 = 0.0 + for i in range(len(speedup_factors)): + sum_time += speedup_factors[i] + var average_time = sum_time / Float64(len(speedup_factors)) + + # Display summary + log_print( + "\n=== BigUInt scale_up_by_power_of_10 Benchmark Summary ===", log_file + ) + log_print( + "Benchmarked: " + + String(len(speedup_factors)) + + " different scaling cases", + log_file, + ) + log_print( + "Each case ran: " + String(iterations) + " iterations", log_file + ) + log_print("Average time: " + String(average_time) + " ns", log_file) + + # List all benchmark times + log_print("\nIndividual benchmark times:", log_file) + for i in range(len(speedup_factors)): + log_print( + String("Case {}: {} ns").format( + i + 1, round(speedup_factors[i], 2) + ), + log_file, + ) + + # Close the log file + log_file.close() + print("Benchmark completed. Log file closed.") diff --git a/benches/bigdecimal/bench_scale_up_by_power_of_10.mojo b/benches/bigdecimal/bench_scale_up_by_power_of_10.mojo new file mode 100644 index 0000000..2637738 --- /dev/null +++ b/benches/bigdecimal/bench_scale_up_by_power_of_10.mojo @@ -0,0 +1,322 @@ +"""Benchmarks for BigUInt scale_up_by_power_of_10 function.""" + +from decimojo.biguint.biguint import BigUInt +import decimojo.biguint.arithmetics +from time import perf_counter_ns +import time +import os +from collections import List +from python import Python, PythonObject + + +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_scale_up_" + 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.""" + print(msg) + log_file.write(msg + "\n") + log_file.flush() # Ensure the message is written immediately + + +fn run_benchmark_scale_up( + name: String, + value: String, + power: Int, + iterations: Int, + log_file: PythonObject, + mut speedup_factors: List[Float64], +) raises: + """ + Run a benchmark for Mojo BigUInt scale_up_by_power_of_10 function. + + Args: + name: Name of the benchmark case. + value: String representation of the BigUInt. + power: The power of 10 to scale up by. + iterations: Number of iterations to run. + log_file: File object for logging results. + speedup_factors: Mojo List to store benchmark times. + """ + log_print("\nBenchmark: " + name, log_file) + log_print("BigUInt value: " + value, log_file) + log_print("Power of 10: " + String(power), log_file) + + # Set up Mojo value + var mojo_value = BigUInt(value) + + # Benchmark Mojo implementation + var t0 = perf_counter_ns() + for _ in range(iterations): + _ = decimojo.biguint.arithmetics.scale_up_by_power_of_10( + mojo_value, power + ) + var mojo_time = (perf_counter_ns() - t0) / iterations + + # Print results + log_print( + "Mojo scale_up: " + String(mojo_time) + " ns per iteration", log_file + ) + speedup_factors.append(Float64(mojo_time)) + + +fn main() raises: + # Open log file + var log_file = open_log_file() + var datetime = Python.import_module("datetime") + + # Create a Mojo List to store benchmark times + var speedup_factors = List[Float64]() + + # Display benchmark header with system information + log_print( + "=== DeciMojo BigUInt scale_up_by_power_of_10 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 scale_up_by_power_of_10 benchmarks with " + + String(iterations) + + " iterations each", + log_file, + ) + + # Case 1: Small power + run_benchmark_scale_up( + "Small power", "12345", 5, iterations, log_file, speedup_factors + ) + + # Case 2: Medium power + run_benchmark_scale_up( + "Medium power", "12345", 20, iterations, log_file, speedup_factors + ) + + # Case 3: Large power + run_benchmark_scale_up( + "Large power", "12345", 50, iterations, log_file, speedup_factors + ) + + # Case 4: Very large power + run_benchmark_scale_up( + "Very large power", "12345", 100, iterations, log_file, speedup_factors + ) + + # Case 5: Small number, small power + run_benchmark_scale_up( + "Small number, small power", + "1", + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 6: Large number, small power + run_benchmark_scale_up( + "Large number, small power", + "9" * 50, + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 7: Small number, large power + run_benchmark_scale_up( + "Small number, large power", + "1", + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 8: Large number, large power + run_benchmark_scale_up( + "Large number, large power", + "9" * 50, + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 9: Power of 10 + run_benchmark_scale_up( + "Power of 10", "1", 10, iterations, log_file, speedup_factors + ) + + # Case 10: Power of 10, large number + run_benchmark_scale_up( + "Power of 10, large number", + "1234567890", + 10, + iterations, + log_file, + speedup_factors, + ) + + # Case 11: Zero value + run_benchmark_scale_up( + "Zero value", "0", 10, iterations, log_file, speedup_factors + ) + + # Case 12: Zero power + run_benchmark_scale_up( + "Zero power", "12345", 0, iterations, log_file, speedup_factors + ) + + # Case 13: Large number, zero power + run_benchmark_scale_up( + "Large number, zero power", + "9" * 50, + 0, + iterations, + log_file, + speedup_factors, + ) + + # Case 14: Number with many digits, small power + run_benchmark_scale_up( + "Number with many digits, small power", + "1" * 100, + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 15: Number with many digits, large power + run_benchmark_scale_up( + "Number with many digits, large power", + "1" * 100, + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 16: Number with repeating digits, small power + run_benchmark_scale_up( + "Number with repeating digits, small power", + "12345" * 20, + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 17: Number with repeating digits, large power + run_benchmark_scale_up( + "Number with repeating digits, large power", + "12345" * 20, + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 18: Number close to max UInt32, small power + run_benchmark_scale_up( + "Number close to max UInt32, small power", + "4294967295", + 5, + iterations, + log_file, + speedup_factors, + ) + + # Case 19: Number close to max UInt32, large power + run_benchmark_scale_up( + "Number close to max UInt32, large power", + "4294967295", + 50, + iterations, + log_file, + speedup_factors, + ) + + # Case 20: Large power, large number + run_benchmark_scale_up( + "Large power, large number", + "12345678901234567890", + 100, + iterations, + log_file, + speedup_factors, + ) + + # Calculate average benchmark time + var sum_time: Float64 = 0.0 + for i in range(len(speedup_factors)): + sum_time += speedup_factors[i] + var average_time = sum_time / Float64(len(speedup_factors)) + + # Display summary + log_print( + "\n=== BigUInt scale_up_by_power_of_10 Benchmark Summary ===", log_file + ) + log_print( + "Benchmarked: " + + String(len(speedup_factors)) + + " different scaling cases", + log_file, + ) + log_print( + "Each case ran: " + String(iterations) + " iterations", log_file + ) + log_print("Average time: " + String(average_time) + " ns", log_file) + + # List all benchmark times + log_print("\nIndividual benchmark times:", log_file) + for i in range(len(speedup_factors)): + log_print( + String("Case {}: {} ns").format( + i + 1, round(speedup_factors[i], 2) + ), + log_file, + ) + + # Close the log file + log_file.close() + print("Benchmark completed. Log file closed.") diff --git a/src/decimojo/__init__.mojo b/src/decimojo/__init__.mojo index 76580f4..5b917c3 100644 --- a/src/decimojo/__init__.mojo +++ b/src/decimojo/__init__.mojo @@ -28,8 +28,8 @@ from decimojo import Decimal, BigInt, RoundingMode from .decimal.decimal import Decimal, Dec from .bigint.bigint import BigInt, BInt from .biguint.biguint import BigUInt, BUInt -from .bigdecimal.bigdecimal import BigDecimal, BDec -from .rounding_mode import RoundingMode +from .bigdecimal.bigdecimal import BigDecimal, BigDec, BDec +from .rounding_mode import RoundingMode, RM # Core functions from .decimal.arithmetics import ( diff --git a/src/decimojo/bigdecimal/bigdecimal.mojo b/src/decimojo/bigdecimal/bigdecimal.mojo index 15cdf11..f2694c8 100644 --- a/src/decimojo/bigdecimal/bigdecimal.mojo +++ b/src/decimojo/bigdecimal/bigdecimal.mojo @@ -28,6 +28,7 @@ import testing from decimojo.rounding_mode import RoundingMode +alias BigDec = BigDecimal alias BDec = BigDecimal @@ -459,10 +460,91 @@ struct BigDecimal: fn __truediv__(self, other: Self) raises -> Self: return decimojo.bigdecimal.arithmetics.true_divide(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.bigdecimal.arithmetics.add(self, other) + + @always_inline + fn __isub__(mut self, other: Self) raises: + self = decimojo.bigdecimal.arithmetics.subtract(self, other) + + @always_inline + fn __imul__(mut self, other: Self) raises: + self = decimojo.bigdecimal.arithmetics.multiply(self, other) + + @always_inline + fn __itruediv__(mut self, other: Self) raises: + self = decimojo.bigdecimal.arithmetics.true_divide(self, other) + + # ===------------------------------------------------------------------=== # + # Basic binary comparison operation dunders + # __gt__, __ge__, __lt__, __le__, __eq__, __ne__ + # ===------------------------------------------------------------------=== # + + @always_inline + fn __gt__(self, other: BigDecimal) raises -> Bool: + """Returns whether self is greater than other.""" + return decimojo.bigdecimal.comparison.compare(self, other) > 0 + + @always_inline + fn __ge__(self, other: BigDecimal) raises -> Bool: + """Returns whether self is greater than or equal to other.""" + return decimojo.bigdecimal.comparison.compare(self, other) >= 0 + + @always_inline + fn __lt__(self, other: BigDecimal) raises -> Bool: + """Returns whether self is less than other.""" + return decimojo.bigdecimal.comparison.compare(self, other) < 0 + + @always_inline + fn __le__(self, other: BigDecimal) raises -> Bool: + """Returns whether self is less than or equal to other.""" + return decimojo.bigdecimal.comparison.compare(self, other) <= 0 + + @always_inline + fn __eq__(self, other: BigDecimal) raises -> Bool: + """Returns whether self equals other.""" + return decimojo.bigdecimal.comparison.compare(self, other) == 0 + + @always_inline + fn __ne__(self, other: BigDecimal) raises -> Bool: + """Returns whether self does not equal other.""" + return decimojo.bigdecimal.comparison.compare(self, other) != 0 + # ===------------------------------------------------------------------=== # # Mathematical methods that do not implement a trait (not a dunder) # ===------------------------------------------------------------------=== # + @always_inline + fn compare(self, other: Self) raises -> Int8: + """Compares two BigDecimal numbers. + See `comparison.compare()` for more information. + """ + return decimojo.bigdecimal.comparison.compare(self, other) + + @always_inline + fn compare_absolute(self, other: Self) raises -> Int8: + """Compares two BigDecimal numbers by absolute value. + See `comparison.compare_absolute()` for more information. + """ + return decimojo.bigdecimal.comparison.compare_absolute(self, other) + + @always_inline + fn max(self, other: Self) raises -> Self: + """Returns the maximum of two BigDecimal numbers.""" + return decimojo.bigdecimal.comparison.max(self, other) + + fn min(self, other: Self) raises -> Self: + """Returns the minimum of two BigDecimal numbers.""" + return decimojo.bigdecimal.comparison.min(self, other) + @always_inline fn true_divide(self, other: Self, precision: Int) raises -> Self: """Returns the result of true division of two BigDecimal numbers. diff --git a/src/decimojo/bigdecimal/comparison.mojo b/src/decimojo/bigdecimal/comparison.mojo new file mode 100644 index 0000000..0bf4115 --- /dev/null +++ b/src/decimojo/bigdecimal/comparison.mojo @@ -0,0 +1,154 @@ +# ===----------------------------------------------------------------------=== # +# Copyright 2025 Yuhao Zhu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +""" +Implements functions for comparison operations on BigDecimal objects. +""" + +from decimojo.bigdecimal.bigdecimal import BigDecimal + + +fn compare_absolute(x1: BigDecimal, x2: BigDecimal) raises -> Int8: + """Compares the absolute values of two numbers. + + Args: + x1: First number. + x2: Second number. + + Returns: + Terinary value indicating the comparison result: + (1) 1 if |x1| > |x2|. + (2) 0 if |x1| = |x2|. + (3) -1 if |x1| < |x2|. + """ + # Handle zero cases + if x1.coefficient.is_zero() and x2.coefficient.is_zero(): + return 0 + if x1.coefficient.is_zero(): + return -1 + if x2.coefficient.is_zero(): + return 1 + + # If scales are equal, compare coefficients directly + if x1.scale == x2.scale: + # Use existing BigUInt comparison + return x1.coefficient.compare(x2.coefficient) + + # Compare number of digits before the decimal point (integer part) + var x1_int_digits = x1.coefficient.number_of_digits() - x1.scale + var x2_int_digits = x2.coefficient.number_of_digits() - x2.scale + + # If integer parts have different lengths, larger integer part wins + if x1_int_digits > x2_int_digits: + return 1 + if x1_int_digits < x2_int_digits: + return -1 + + # Integer parts have same length, need to compare digit by digit + # Scale up the number with smaller scale to match the other's scale + var scale_diff = x1.scale - x2.scale + + if scale_diff > 0: + # x1 has larger scale (more decimal places) + var scaled_x2 = x2.coefficient.scale_up_by_power_of_10(scale_diff) + return x1.coefficient.compare(scaled_x2^) + else: + # x2 has larger scale (more decimal places) + var scaled_x1 = x1.coefficient.scale_up_by_power_of_10(-scale_diff) + return scaled_x1.compare(x2.coefficient) + + +fn compare(x1: BigDecimal, x2: BigDecimal) raises -> Int8: + """Compares two BigDecimal numbers. + + Args: + x1: First number. + x2: Second number. + + Returns: + Terinary value indicating the comparison result: + (1) 1 if x1 > x2. + (2) 0 if x1 = x2. + (3) -1 if x1 < x2. + """ + # Handle zero cases first + if x1.coefficient.is_zero() and x2.coefficient.is_zero(): + return 0 + + # If one is zero, handle specially + if x1.coefficient.is_zero(): + return 1 if x2.sign else -1 # 0 > negative, 0 < positive + if x2.coefficient.is_zero(): + return -1 if x1.sign else 1 # negative < 0, positive > 0 + + # If signs differ, the positive one is greater + if not x1.sign and x2.sign: # x1 is positive, x2 is negative + return 1 + if x1.sign and not x2.sign: # x1 is negative, x2 is positive + return -1 + + # Same sign - compare absolute values + var abs_comparison = compare_absolute(x1, x2) + + # For negative numbers, reverse the comparison result + if x1.sign: # Both are negative + return -abs_comparison # Negate the result for negative numbers + else: # Both are positive + return abs_comparison + + +fn equals(x1: BigDecimal, x2: BigDecimal) raises -> Bool: + """Returns whether x1 equals x2.""" + return compare(x1, x2) == 0 + + +fn not_equals(x1: BigDecimal, x2: BigDecimal) raises -> Bool: + """Returns whether x1 does not equal x2.""" + return compare(x1, x2) != 0 + + +fn less_than(x1: BigDecimal, x2: BigDecimal) raises -> Bool: + """Returns whether x1 is less than x2.""" + return compare(x1, x2) < 0 + + +fn less_than_or_equal(x1: BigDecimal, x2: BigDecimal) raises -> Bool: + """Returns whether x1 is less than or equal to x2.""" + return compare(x1, x2) <= 0 + + +fn greater_than(x1: BigDecimal, x2: BigDecimal) raises -> Bool: + """Returns whether x1 is greater than x2.""" + return compare(x1, x2) > 0 + + +fn greater_than_or_equal(x1: BigDecimal, x2: BigDecimal) raises -> Bool: + """Returns whether x1 is greater than or equal to x2.""" + return compare(x1, x2) >= 0 + + +fn max(x1: BigDecimal, x2: BigDecimal) raises -> BigDecimal: + """Returns the maximum of x1 and x2.""" + if compare(x1, x2) >= 0: + return x1 + return x2 + + +fn min(x1: BigDecimal, x2: BigDecimal) raises -> BigDecimal: + """Returns the minimum of x1 and x2.""" + if compare(x1, x2) <= 0: + return x1 + return x2 diff --git a/src/decimojo/biguint/arithmetics.mojo b/src/decimojo/biguint/arithmetics.mojo index 635fb4d..c738c16 100644 --- a/src/decimojo/biguint/arithmetics.mojo +++ b/src/decimojo/biguint/arithmetics.mojo @@ -397,6 +397,7 @@ fn floor_divide(x1: BigUInt, x2: BigUInt) raises -> BigUInt: return floor_divide_general(normalized_x1, normalized_x2) +@always_inline fn truncate_divide(x1: BigUInt, x2: BigUInt) raises -> BigUInt: """Returns the quotient of two BigUInt numbers, truncating toward zero. It is equal to floored division for unsigned numbers. @@ -473,6 +474,7 @@ fn floor_modulo(x1: BigUInt, x2: BigUInt) raises -> BigUInt: return remainder^ +@always_inline fn truncate_modulo(x1: BigUInt, x2: BigUInt) raises -> BigUInt: """Returns the remainder of two BigUInt numbers, truncating toward zero. It is equal to floored modulo for unsigned numbers. @@ -719,37 +721,48 @@ fn scale_up_by_power_of_10(x: BigUInt, n: Int) raises -> BigUInt: var number_of_zero_words = n // 9 var number_of_remaining_digits = n % 9 - var result: BigUInt = x + var words = List[UInt32](capacity=number_of_zero_words + len(x.words) + 1) + # Add zero words + for _ in range(number_of_zero_words): + words.append(UInt32(0)) + # Add the original words times 10^number_of_remaining_digits if number_of_remaining_digits == 0: - pass - elif number_of_remaining_digits == 1: - result = multiply(result, BigUInt(UInt32(10))) - elif number_of_remaining_digits == 2: - result = multiply(result, BigUInt(UInt32(100))) - elif number_of_remaining_digits == 3: - result = multiply(result, BigUInt(UInt32(1000))) - elif number_of_remaining_digits == 4: - result = multiply(result, BigUInt(UInt32(10000))) - elif number_of_remaining_digits == 5: - result = multiply(result, BigUInt(UInt32(100000))) - elif number_of_remaining_digits == 6: - result = multiply(result, BigUInt(UInt32(1000000))) - elif number_of_remaining_digits == 7: - result = multiply(result, BigUInt(UInt32(10000000))) - else: # number_of_remaining_digits == 8 - result = multiply(result, BigUInt(UInt32(100000000))) - - if number_of_zero_words > 0: - var words = List[UInt32]( - capacity=number_of_zero_words + len(result.words) + for i in range(len(x.words)): + words.append(x.words[i]) + else: # number_of_remaining_digits > 0 + var carry = UInt64(0) + var multiplier: UInt64 + + if number_of_remaining_digits == 1: + multiplier = UInt64(10) + elif number_of_remaining_digits == 2: + multiplier = UInt64(100) + elif number_of_remaining_digits == 3: + multiplier = UInt64(1000) + elif number_of_remaining_digits == 4: + multiplier = UInt64(10_000) + elif number_of_remaining_digits == 5: + multiplier = UInt64(100_000) + elif number_of_remaining_digits == 6: + multiplier = UInt64(1_000_000) + elif number_of_remaining_digits == 7: + multiplier = UInt64(10_000_000) + else: # number_of_remaining_digits == 8 + multiplier = UInt64(100_000_000) + debug_assert( + number_of_remaining_digits >= 9, + "number_of_remaining_digits must be less than 9", ) - for _ in range(number_of_zero_words): - words.append(UInt32(0)) - for i in range(len(result.words)): - words.append(result.words[i]) - result.words = words^ - return result^ + for i in range(len(x.words)): + var product = UInt64(x.words[i]) * multiplier + carry + words.append(UInt32(product % UInt64(1_000_000_000))) + carry = product // UInt64(1_000_000_000) + # Add the last carry if it exists + if carry > 0: + words.append(UInt32(carry)) + + return BigUInt(words=words^) # ===----------------------------------------------------------------------=== # diff --git a/src/decimojo/rounding_mode.mojo b/src/decimojo/rounding_mode.mojo index 972c5cd..a7013c3 100644 --- a/src/decimojo/rounding_mode.mojo +++ b/src/decimojo/rounding_mode.mojo @@ -17,6 +17,8 @@ """Implements the RoundingMode for different rounding modes. """ +alias RM = RoundingMode + struct RoundingMode: """ diff --git a/tests/bigdecimal/test_bigdecimal_compare.mojo b/tests/bigdecimal/test_bigdecimal_compare.mojo new file mode 100644 index 0000000..0e72fce --- /dev/null +++ b/tests/bigdecimal/test_bigdecimal_compare.mojo @@ -0,0 +1,334 @@ +""" +Test BigDecimal comparison operations. +""" + +import testing +from python import PythonObject +from tomlmojo import parse_file +from decimojo import BigDecimal +from decimojo.bigdecimal.comparison import compare_absolute, compare +from decimojo.tests import TestCase +from collections import List + +alias file_path = "tests/bigdecimal/test_data/bigdecimal_compare.toml" + + +fn load_test_cases( + file_path: String, table_name: String +) raises -> List[TestCase]: + """Load test cases from a TOML file for a specific table.""" + var toml = parse_file(file_path) + var test_cases = List[TestCase]() + + # Get array of test cases + var cases_array = toml.get_array_of_tables(table_name) + + for i in range(len(cases_array)): + var case_table = cases_array[i] + test_cases.append( + TestCase( + case_table["a"].as_string(), + case_table["b"].as_string(), + case_table["expected"].as_string(), + case_table["description"].as_string(), + ) + ) + + return test_cases + + +fn test_compare_absolute() raises: + """Test the compare_absolute function for BigDecimal.""" + print("------------------------------------------------------") + print("Testing BigDecimal compare_absolute...") + + # Load test cases from TOML file + var test_cases = load_test_cases(file_path, "compare_absolute_tests") + print("Loaded", len(test_cases), "test cases for compare_absolute") + + # Track test results + var passed = 0 + var failed = 0 + + # Run all test cases in a loop + for i in range(len(test_cases)): + var test_case = test_cases[i] + var a = BigDecimal(test_case.a) + var b = BigDecimal(test_case.b) + var expected = Int8(Int(test_case.expected)) + var result = compare_absolute(a, b) + + try: + testing.assert_equal(result, expected, test_case.description) + passed += 1 + except e: + print( + "✗ Case", + i + 1, + "failed:", + test_case.description, + "\n Input: |", + test_case.a, + "| compare |", + test_case.b, + "|", + "\n Expected:", + expected, + "\n Got:", + result, + ) + failed += 1 + + print( + "BigDecimal compare_absolute tests:", + passed, + "passed,", + failed, + "failed", + ) + testing.assert_equal(failed, 0, "All compare_absolute tests should pass") + + +fn test_greater_than() raises: + """Test the > operator for BigDecimal.""" + print("------------------------------------------------------") + print("Testing BigDecimal > operator...") + var test_cases = load_test_cases(file_path, "greater_than_tests") + var passed = 0 + var failed = 0 + + for i in range(len(test_cases)): + var test_case = test_cases[i] + var a = BigDecimal(test_case.a) + var b = BigDecimal(test_case.b) + var expected = test_case.expected == "true" + var result = a > b + try: + testing.assert_equal(result, expected, test_case.description) + passed += 1 + except e: + print( + "✗ Case", + i + 1, + "failed:", + test_case.description, + "\n Input:", + test_case.a, + ">", + test_case.b, + "\n Expected:", + expected, + "\n Got:", + result, + ) + failed += 1 + + print("BigDecimal > tests:", passed, "passed,", failed, "failed") + testing.assert_equal(failed, 0, "All > tests should pass") + + +fn test_less_than() raises: + """Test the < operator for BigDecimal.""" + print("------------------------------------------------------") + print("Testing BigDecimal < operator...") + var test_cases = load_test_cases(file_path, "less_than_tests") + var passed = 0 + var failed = 0 + + for i in range(len(test_cases)): + var test_case = test_cases[i] + var a = BigDecimal(test_case.a) + var b = BigDecimal(test_case.b) + var expected = test_case.expected == "true" + var result = a < b + try: + testing.assert_equal(result, expected, test_case.description) + passed += 1 + except e: + print( + "✗ Case", + i + 1, + "failed:", + test_case.description, + "\n Input:", + test_case.a, + "<", + test_case.b, + "\n Expected:", + expected, + "\n Got:", + result, + ) + failed += 1 + + print("BigDecimal < tests:", passed, "passed,", failed, "failed") + testing.assert_equal(failed, 0, "All < tests should pass") + + +fn test_greater_than_or_equal() raises: + """Test the >= operator for BigDecimal.""" + print("------------------------------------------------------") + print("Testing BigDecimal >= operator...") + var test_cases = load_test_cases(file_path, "greater_than_or_equal_tests") + var passed = 0 + var failed = 0 + + for i in range(len(test_cases)): + var test_case = test_cases[i] + var a = BigDecimal(test_case.a) + var b = BigDecimal(test_case.b) + var expected = test_case.expected == "true" + var result = a >= b + try: + testing.assert_equal(result, expected, test_case.description) + passed += 1 + except e: + print( + "✗ Case", + i + 1, + "failed:", + test_case.description, + "\n Input:", + test_case.a, + ">=", + test_case.b, + "\n Expected:", + expected, + "\n Got:", + result, + ) + failed += 1 + + print("BigDecimal >= tests:", passed, "passed,", failed, "failed") + testing.assert_equal(failed, 0, "All >= tests should pass") + + +fn test_less_than_or_equal() raises: + """Test the <= operator for BigDecimal.""" + print("------------------------------------------------------") + print("Testing BigDecimal <= operator...") + var test_cases = load_test_cases(file_path, "less_than_or_equal_tests") + var passed = 0 + var failed = 0 + + for i in range(len(test_cases)): + var test_case = test_cases[i] + var a = BigDecimal(test_case.a) + var b = BigDecimal(test_case.b) + var expected = test_case.expected == "true" + var result = a <= b + try: + testing.assert_equal(result, expected, test_case.description) + passed += 1 + except e: + print( + "✗ Case", + i + 1, + "failed:", + test_case.description, + "\n Input:", + test_case.a, + "<=", + test_case.b, + "\n Expected:", + expected, + "\n Got:", + result, + ) + failed += 1 + + print("BigDecimal <= tests:", passed, "passed,", failed, "failed") + testing.assert_equal(failed, 0, "All <= tests should pass") + + +fn test_equal() raises: + """Test the == operator for BigDecimal.""" + print("------------------------------------------------------") + print("Testing BigDecimal == operator...") + var test_cases = load_test_cases(file_path, "equal_tests") + var passed = 0 + var failed = 0 + + for i in range(len(test_cases)): + var test_case = test_cases[i] + var a = BigDecimal(test_case.a) + var b = BigDecimal(test_case.b) + var expected = test_case.expected == "true" + var result = a == b + try: + testing.assert_equal(result, expected, test_case.description) + passed += 1 + except e: + print( + "✗ Case", + i + 1, + "failed:", + test_case.description, + "\n Input:", + test_case.a, + "==", + test_case.b, + "\n Expected:", + expected, + "\n Got:", + result, + ) + failed += 1 + + print("BigDecimal == tests:", passed, "passed,", failed, "failed") + testing.assert_equal(failed, 0, "All == tests should pass") + + +fn test_not_equal() raises: + """Test the != operator for BigDecimal.""" + print("------------------------------------------------------") + print("Testing BigDecimal != operator...") + var test_cases = load_test_cases(file_path, "not_equal_tests") + var passed = 0 + var failed = 0 + + for i in range(len(test_cases)): + var test_case = test_cases[i] + var a = BigDecimal(test_case.a) + var b = BigDecimal(test_case.b) + var expected = test_case.expected == "true" + var result = a != b + try: + testing.assert_equal(result, expected, test_case.description) + passed += 1 + except e: + print( + "✗ Case", + i + 1, + "failed:", + test_case.description, + "\n Input:", + test_case.a, + "!=", + test_case.b, + "\n Expected:", + expected, + "\n Got:", + result, + ) + failed += 1 + + print("BigDecimal != tests:", passed, "passed,", failed, "failed") + testing.assert_equal(failed, 0, "All != tests should pass") + + +fn main() raises: + print("Running BigDecimal comparison tests") + + # Run compare_absolute tests + test_compare_absolute() + + # Run comparison operator tests + test_greater_than() + test_less_than() + test_greater_than_or_equal() + test_less_than_or_equal() + test_equal() + test_not_equal() + + print("All BigDecimal comparison tests passed!") diff --git a/tests/bigdecimal/test_data/bigdecimal_compare.toml b/tests/bigdecimal/test_data/bigdecimal_compare.toml new file mode 100644 index 0000000..5e26d83 --- /dev/null +++ b/tests/bigdecimal/test_data/bigdecimal_compare.toml @@ -0,0 +1,679 @@ +# Test cases for BigDecimal comparison (compare_absolute) + +[[compare_absolute_tests]] +a = "10" +b = "5" +expected = "1" +description = "Simple case: a > b" + +[[compare_absolute_tests]] +a = "5" +b = "10" +expected = "-1" +description = "Simple case: a < b" + +[[compare_absolute_tests]] +a = "5" +b = "5" +expected = "0" +description = "Simple case: a == b" + +[[compare_absolute_tests]] +a = "-10" +b = "5" +expected = "1" +description = "Negative a, positive b, |a| > |b|" + +[[compare_absolute_tests]] +a = "10" +b = "-5" +expected = "1" +description = "Positive a, negative b, |a| > |b|" + +[[compare_absolute_tests]] +a = "-5" +b = "10" +expected = "-1" +description = "Negative a, positive b, |a| < |b|" + +[[compare_absolute_tests]] +a = "5" +b = "-10" +expected = "-1" +description = "Positive a, negative b, |a| < |b|" + +[[compare_absolute_tests]] +a = "-10" +b = "-5" +expected = "1" +description = "Negative a, negative b, |a| > |b|" + +[[compare_absolute_tests]] +a = "-5" +b = "-10" +expected = "-1" +description = "Negative a, negative b, |a| < |b|" + +[[compare_absolute_tests]] +a = "-5" +b = "5" +expected = "0" +description = "Negative a, positive b, |a| == |b|" + +[[compare_absolute_tests]] +a = "5" +b = "-5" +expected = "0" +description = "Positive a, negative b, |a| == |b|" + +[[compare_absolute_tests]] +a = "-0" +b = "0" +expected = "0" +description = "Negative zero vs positive zero" + +[[compare_absolute_tests]] +a = "0" +b = "-0" +expected = "0" +description = "Positive zero vs negative zero" + +[[compare_absolute_tests]] +a = "12345678901234567890" +b = "1234567890123456789" +expected = "1" +description = "Large numbers: a > b" + +[[compare_absolute_tests]] +a = "1234567890123456789" +b = "12345678901234567890" +expected = "-1" +description = "Large numbers: a < b" + +[[compare_absolute_tests]] +a = "99999999999999999999" +b = "99999999999999999999" +expected = "0" +description = "Large numbers: a == b" + +[[compare_absolute_tests]] +a = "0.000000001" +b = "0.0000000001" +expected = "1" +description = "Small decimals: a > b" + +[[compare_absolute_tests]] +a = "0.0000000001" +b = "0.000000001" +expected = "-1" +description = "Small decimals: a < b" + +[[compare_absolute_tests]] +a = "0.000000001" +b = "0.000000001" +expected = "0" +description = "Small decimals: a == b" + +[[compare_absolute_tests]] +a = "1.23456789" +b = "1.23456788" +expected = "1" +description = "Close decimals: a > b" + +[[compare_absolute_tests]] +a = "1.23456788" +b = "1.23456789" +expected = "-1" +description = "Close decimals: a < b" + +[[compare_absolute_tests]] +a = "1.23456789" +b = "1.23456789" +expected = "0" +description = "Close decimals: a == b" + +[[compare_absolute_tests]] +a = "1.23e5" +b = "4.56e4" +expected = "1" +description = "Scientific notation: a > b" + +[[compare_absolute_tests]] +a = "1.23e-5" +b = "4.56e-2" +expected = "-1" +description = "Scientific notation: a < b" + +[[compare_absolute_tests]] +a = "1.23e5" +b = "1.23e5" +expected = "0" +description = "Scientific notation: a == b" + +[[compare_absolute_tests]] +a = "3.14159265358979323846" +b = "2.71828182845904523536" +expected = "1" +description = "Math constants: PI > E" + +[[compare_absolute_tests]] +a = "1.61803398874989484820" +b = "1.41421356237309504880" +expected = "1" +description = "Math constants: PHI > sqrt(2)" + +[[compare_absolute_tests]] +a = "0.66666666666666666666" +b = "0.33333333333333333333" +expected = "1" +description = "Repeating decimals: 2/3 > 1/3" + +[[compare_absolute_tests]] +a = "10000000000000000000000000000" +b = "1" +expected = "1" +description = "Large vs small: a > b" + +[[compare_absolute_tests]] +a = "0.000000000000000000000000001" +b = "1" +expected = "-1" +description = "Small vs large: a < b" + +[[compare_absolute_tests]] +a = "1000.00" +b = "999.99" +expected = "1" +description = "Financial numbers: a > b" + +[[compare_absolute_tests]] +a = "999.99" +b = "1000.00" +expected = "-1" +description = "Financial numbers: a < b" + +[[compare_absolute_tests]] +a = "1000.00" +b = "1000.00" +expected = "0" +description = "Financial numbers: a == b" + +[[compare_absolute_tests]] +a = "1.79769313486231570E+308" +b = "10" +expected = "1" +description = "Near max double precision: a > b" + +[[compare_absolute_tests]] +a = "9.9999999999999999999999" +b = "9.9999999999999999999998" +expected = "1" +description = "Near equal large numbers: a > b" + +[[compare_absolute_tests]] +a = "9.9999999999999999999998" +b = "9.9999999999999999999999" +expected = "-1" +description = "Near equal large numbers: a < b" + +[[compare_absolute_tests]] +a = "1111111110111111111.111111110111111110" +b = "987654321098765432.123456789098765432" +expected = "1" +description = "Complex numbers: a > b" + +[[compare_absolute_tests]] +a = "37.7749" +b = "37.7748" +expected = "1" +description = "GPS coordinates: a > b" + +[[compare_absolute_tests]] +a = "98.6" +b = "37.0" +expected = "1" +description = "Temperature conversion: a > b" + +[[compare_absolute_tests]] +a = "1000.50" +b = "243.22" +expected = "1" +description = "Bank balance: a > b" + +[[compare_absolute_tests]] +a = "987654321987654321.987654321" +b = "0.000000000000000000000000001" +expected = "1" +description = "Extreme scale difference: a > b" + +[[compare_absolute_tests]] +a = "0.0440" +b = "0.0015" +expected = "1" +description = "Interest rate: a > b" + +[[compare_absolute_tests]] +a = "1.23456789012345678901234567" +b = "1.23456789012345678901234566" +expected = "1" +description = "Very close numbers: a > b" + +[[compare_absolute_tests]] +a = "1.23456789012345678901234566" +b = "1.23456789012345678901234567" +expected = "-1" +description = "Very close numbers: a < b" + +[[compare_absolute_tests]] +a = "1.23456789012345678901234567" +b = "1.23456789012345678901234567" +expected = "0" +description = "Very close numbers: a == b" + +[[compare_absolute_tests]] +a = "1.000000000000000000000000000" +b = "0.999999999999999999999999999" +expected = "1" +description = "Near one: a > b" + +[[compare_absolute_tests]] +a = "0.999999999999999999999999999" +b = "1.000000000000000000000000000" +expected = "-1" +description = "Near one: a < b" + +[[compare_absolute_tests]] +a = "1.000000000000000000000000000" +b = "1.000000000000000000000000000" +expected = "0" +description = "Near one: a == b" + +# Test cases for BigDecimal comparison (>) + +[[greater_than_tests]] +a = "10" +b = "5" +expected = "true" +description = "Simple case: a > b" + +[[greater_than_tests]] +a = "5" +b = "10" +expected = "false" +description = "Simple case: a < b" + +[[greater_than_tests]] +a = "5" +b = "5" +expected = "false" +description = "Simple case: a == b" + +[[greater_than_tests]] +a = "-10" +b = "5" +expected = "false" +description = "Negative a, positive b, |a| > |b|" + +[[greater_than_tests]] +a = "10" +b = "-5" +expected = "true" +description = "Positive a, negative b, |a| > |b|" + +[[greater_than_tests]] +a = "12345678901234567890" +b = "1234567890123456789" +expected = "true" +description = "Large numbers: a > b" + +[[greater_than_tests]] +a = "0.000000001" +b = "0.0000000001" +expected = "true" +description = "Small decimals: a > b" + +[[greater_than_tests]] +a = "1.23456789" +b = "1.23456788" +expected = "true" +description = "Close decimals: a > b" + +[[greater_than_tests]] +a = "1.23e5" +b = "4.56e4" +expected = "true" +description = "Scientific notation: a > b" + +[[greater_than_tests]] +a = "3.14159265358979323846" +b = "2.71828182845904523536" +expected = "true" +description = "Math constants: PI > E" + +[[greater_than_tests]] +a = "1.61803398874989484820" +b = "1.41421356237309504880" +expected = "true" +description = "Math constants: PHI > sqrt(2)" + +[[greater_than_tests]] +a = "0.66666666666666666666" +b = "0.33333333333333333333" +expected = "true" +description = "Repeating decimals: 2/3 > 1/3" + +[[greater_than_tests]] +a = "10000000000000000000000000000" +b = "1" +expected = "true" +description = "Large vs small: a > b" + +[[greater_than_tests]] +a = "1000.00" +b = "999.99" +expected = "true" +description = "Financial numbers: a > b" + +[[greater_than_tests]] +a = "1.79769313486231570E+308" +b = "10" +expected = "true" +description = "Near max double precision: a > b" + +[[greater_than_tests]] +a = "9.9999999999999999999999" +b = "9.9999999999999999999998" +expected = "true" +description = "Near equal large numbers: a > b" + +[[greater_than_tests]] +a = "1111111110111111111.111111110111111110" +b = "987654321098765432.123456789098765432" +expected = "true" +description = "Complex numbers: a > b" + +[[greater_than_tests]] +a = "37.7749" +b = "37.7748" +expected = "true" +description = "GPS coordinates: a > b" + +[[greater_than_tests]] +a = "98.6" +b = "37.0" +expected = "true" +description = "Temperature conversion: a > b" + +[[greater_than_tests]] +a = "1000.50" +b = "243.22" +expected = "true" +description = "Bank balance: a > b" + +[[greater_than_tests]] +a = "987654321987654321.987654321" +b = "0.000000000000000000000000001" +expected = "true" +description = "Extreme scale difference: a > b" + +[[greater_than_tests]] +a = "0.0440" +b = "0.0015" +expected = "true" +description = "Interest rate: a > b" + +[[greater_than_tests]] +a = "1.23456789012345678901234567" +b = "1.23456789012345678901234566" +expected = "true" +description = "Very close numbers: a > b" + +[[greater_than_tests]] +a = "1.000000000000000000000000000" +b = "0.999999999999999999999999999" +expected = "true" +description = "Near one: a > b" + +# Test cases for BigDecimal comparison (<) + +[[less_than_tests]] +a = "10" +b = "5" +expected = "false" +description = "Simple case: a > b" + +[[less_than_tests]] +a = "5" +b = "10" +expected = "true" +description = "Simple case: a < b" + +[[less_than_tests]] +a = "5" +b = "5" +expected = "false" +description = "Simple case: a == b" + +[[less_than_tests]] +a = "-5" +b = "10" +expected = "true" +description = "Negative a, positive b, |a| < |b|" + +[[less_than_tests]] +a = "5" +b = "-10" +expected = "false" +description = "Positive a, negative b, |a| < |b|" + +[[less_than_tests]] +a = "-10" +b = "-5" +expected = "true" +description = "Negative a, negative b, |a| > |b|" + +[[less_than_tests]] +a = "-5" +b = "-10" +expected = "false" +description = "Negative a, negative b, |a| < |b|" + +[[less_than_tests]] +a = "1234567890123456789" +b = "12345678901234567890" +expected = "true" +description = "Large numbers: a < b" + +[[less_than_tests]] +a = "0.0000000001" +b = "0.000000001" +expected = "true" +description = "Small decimals: a < b" + +[[less_than_tests]] +a = "1.23456788" +b = "1.23456789" +expected = "true" +description = "Close decimals: a < b" + +[[less_than_tests]] +a = "1.23e-5" +b = "4.56e-2" +expected = "true" +description = "Scientific notation: a < b" + +[[less_than_tests]] +a = "0.000000000000000000000000001" +b = "1" +expected = "true" +description = "Small vs large: a < b" + +[[less_than_tests]] +a = "999.99" +b = "1000.00" +expected = "true" +description = "Financial numbers: a < b" + +[[less_than_tests]] +a = "9.9999999999999999999998" +b = "9.9999999999999999999999" +expected = "true" +description = "Near equal large numbers: a < b" + +[[less_than_tests]] +a = "1.23456789012345678901234566" +b = "1.23456789012345678901234567" +expected = "true" +description = "Very close numbers: a < b" + +[[less_than_tests]] +a = "0.999999999999999999999999999" +b = "1.000000000000000000000000000" +expected = "true" +description = "Near one: a < b" + +# Test cases for BigDecimal comparison (>=) + +[[greater_than_or_equal_tests]] +a = "10" +b = "5" +expected = "true" +description = "Simple case: a > b" + +[[greater_than_or_equal_tests]] +a = "5" +b = "10" +expected = "false" +description = "Simple case: a < b" + +[[greater_than_or_equal_tests]] +a = "5" +b = "5" +expected = "true" +description = "Simple case: a == b" + +# Test cases for BigDecimal comparison (<=) + +[[less_than_or_equal_tests]] +a = "10" +b = "5" +expected = "false" +description = "Simple case: a > b" + +[[less_than_or_equal_tests]] +a = "5" +b = "10" +expected = "true" +description = "Simple case: a < b" + +[[less_than_or_equal_tests]] +a = "5" +b = "5" +expected = "true" +description = "Simple case: a == b" + +# Test cases for BigDecimal comparison (==) + +[[equal_tests]] +a = "10" +b = "5" +expected = "false" +description = "Simple case: a > b" + +[[equal_tests]] +a = "5" +b = "10" +expected = "false" +description = "Simple case: a < b" + +[[equal_tests]] +a = "5" +b = "5" +expected = "true" +description = "Simple case: a == b" + +[[equal_tests]] +a = "-5" +b = "5" +expected = "false" +description = "Negative a, positive b, |a| == |b|" + +[[equal_tests]] +a = "5" +b = "-5" +expected = "false" +description = "Positive a, negative b, |a| == |b|" + +[[equal_tests]] +a = "-0" +b = "0" +expected = "true" +description = "Negative zero vs positive zero" + +[[equal_tests]] +a = "0" +b = "-0" +expected = "true" +description = "Positive zero vs negative zero" + +[[equal_tests]] +a = "99999999999999999999" +b = "99999999999999999999" +expected = "true" +description = "Large numbers: a == b" + +[[equal_tests]] +a = "0.000000001" +b = "0.000000001" +expected = "true" +description = "Small decimals: a == b" + +[[equal_tests]] +a = "1.23456789" +b = "1.23456789" +expected = "true" +description = "Close decimals: a == b" + +[[equal_tests]] +a = "1.23e5" +b = "1.23e5" +expected = "true" +description = "Scientific notation: a == b" + +[[equal_tests]] +a = "1000.00" +b = "1000.00" +expected = "true" +description = "Financial numbers: a == b" + +[[equal_tests]] +a = "1.23456789012345678901234567" +b = "1.23456789012345678901234567" +expected = "true" +description = "Very close numbers: a == b" + +[[equal_tests]] +a = "1.000000000000000000000000000" +b = "1.000000000000000000000000000" +expected = "true" +description = "Near one: a == b" + +# Test cases for BigDecimal comparison (!=) + +[[not_equal_tests]] +a = "10" +b = "5" +expected = "true" +description = "Simple case: a > b" + +[[not_equal_tests]] +a = "5" +b = "10" +expected = "true" +description = "Simple case: a < b" + +[[not_equal_tests]] +a = "5" +b = "5" +expected = "false" +description = "Simple case: a == b" \ No newline at end of file