diff --git a/recipes/decimojo/README.md b/recipes/decimojo/README.md index ffcfced7..0bdf2fe8 100644 --- a/recipes/decimojo/README.md +++ b/recipes/decimojo/README.md @@ -2,276 +2,231 @@ ![icon](icon_256x256.png) -A fixed-point decimal arithmetic library implemented in [the Mojo programming language πŸ”₯](https://www.modular.com/mojo). +A comprehensive decimal and integer mathematics library for [Mojo](https://www.modular.com/mojo). -## Overview - -DeciMojo provides a Decimal type implementation for Mojo with fixed-precision arithmetic, designed to handle financial calculations and other scenarios where floating-point rounding errors are problematic. - -Repo: [https://github.com/forFudan/DeciMojo](https://github.com/forFudan/DeciMojo) - -## Objective - -Financial calculations and data analysis require precise decimal arithmetic that floating-point numbers cannot reliably provide. As someone working in finance and credit risk model validation, I needed a dependable correctly-rounded, fixed-precision numeric type when migrating my personal projects from Python to Mojo. +**[δΈ­ζ–‡Β·ζΌ’ε­—Β»](https://zhuyuhao.com/decimojo/docs/readme_zht.html)**γ€€|γ€€**[Repository on GitHubΒ»](https://github.com/forfudan/decimojo)** -Since Mojo currently lacks a native Decimal type in its standard library, I decided to create my own implementation to fill that gap. - -This project draws inspiration from several established decimal implementations and documentation, e.g., [Python built-in `Decimal` type](https://docs.python.org/3/library/decimal.html), [Rust `rust_decimal` crate](https://docs.rs/rust_decimal/latest/rust_decimal/index.html), [Microsoft's `Decimal` implementation](https://learn.microsoft.com/en-us/dotnet/api/system.decimal.getbits?view=net-9.0&redirectedfrom=MSDN#System_Decimal_GetBits_System_Decimal_), [General Decimal Arithmetic Specification](https://speleotrove.com/decimal/decarith.html), etc. Many thanks to these predecessors for their contributions and their commitment to open knowledge sharing. - -## Nomenclature +## Overview -DeciMojo combines "Decimal" and "Mojo" - reflecting both its purpose (decimal arithmetic) and the programming language it's implemented in. The name highlights the project's focus on bringing precise decimal calculations to the Mojo ecosystem. +DeciMojo provides a comprehensive decimal and integer mathematics library for Mojo, delivering exact calculations for financial modeling, scientific computing, and applications where floating-point approximation errors are unacceptable. Beyond basic arithmetic, the library includes advanced mathematical functions with guaranteed precision. -For brevity, you can also refer to it "decimo" (derived from the Latin root "decimus" meaning "tenth"). +The core types are: -## Status +- A 128-bit fixed-point decimal implementation (`Decimal`) supporting up to 29 significant digits with a maximum of 28 decimal places[^fixed]. It features a complete set of mathematical functions including logarithms, exponentiation, roots, and trigonometric operations. +- A base-10 arbitrary-precision signed integer type (`BigInt`) and a base-10 arbitrary-precision unsigned integer type (`BigUInt`) supporting unlimited digits[^integer]. It features comprehensive arithmetic operations, comparison functions, and supports extremely large integer calculations efficiently. -Rome is not built in one day. DeciMojo is currently under active development and appears to be between the **"make it work"** and **"make it right"** phases, leaning more toward the latter. Bug reports and feature requests are welcome! If you encounter issues, please [file them here](https://github.com/forFudan/decimojo/issues). +The library is expanding to include `BigDecimal` types that support arbitrary precision[^arbitrary], allowing for calculations with unlimited digits and decimal places. These extensions are currently under active development. -### Make it Work βœ… (MOSTLY COMPLETED) +## Installation -- Core decimal implementation exists and functions -- Basic arithmetic operations (+, -, *, /) are implemented -- Type conversions to/from various formats work -- String representation and parsing are functional -- Construction from different sources (strings, numbers) is supported +DeciMojo is available in the [modular-community](https://repo.prefix.dev/modular-community) package repository. You can install it using any of these methods: -### Make it Right πŸ”„ (IN PROGRESS) +From the `magic` CLI, simply run ```magic add decimojo```. This fetches the latest version and makes it immediately available for import. -- Edge case handling is being addressed (division by zero, zero to negative power) -- Scale and precision management shows sophistication -- Financial calculations demonstrate proper rounding -- High precision support is implemented (up to 28 decimal places) -- The examples show robust handling of various scenarios +For projects with a `mojoproject.toml`file, add the dependency ```decimojo = ">=0.2.0"```. Then run `magic install` to download and install the package. -### Make it Fast ⏳ (FUTURE WORK) +For the latest development version, clone the [GitHub repository](https://github.com/forfudan/decimojo) and build the package locally. -- Performance optimization is acknowledged but not currently prioritized +| `decimojo` | `mojo` | +| ---------- | ------ | +| v0.1.0 | >=25.1 | +| v0.2.0 | >=25.2 | -## Examples +## Quick start -Here are 10 key examples highlighting the most important features of the `Decimal` type in its current state: - -### 1. Fixed-Point Precision for Financial Calculations +Here is a comprehensive quick-start guide showcasing each major function of the `Decimal` type. ```mojo -from decimojo.prelude import * - -# The classic floating-point problem -print(0.1 + 0.2) # 0.30000000000000004 (not exactly 0.3) - -# Decimal solves this with exact representation -var d1 = Decimal("0.1") -var d2 = Decimal("0.2") -var sum = d1 + d2 -print(sum) # Exactly 0.3 - -# Financial calculation example - computing tax -var price = Decimal("19.99") -var tax_rate = Decimal("0.0725") -var tax = price * tax_rate # Exactly 1.449275 -var total = price + tax # Exactly 21.439275 +from decimojo import Decimal, RoundingMode + +fn main() raises: + # === Construction === + var a = Decimal("123.45") # From string + var b = Decimal(123) # From integer + var c = Decimal(123, 2) # Integer with scale (1.23) + var d = Decimal.from_float(3.14159) # From floating-point + + # === Basic Arithmetic === + print(a + b) # Addition: 246.45 + print(a - b) # Subtraction: 0.45 + print(a * b) # Multiplication: 15184.35 + print(a / b) # Division: 1.0036585365853658536585365854 + + # === Rounding & Precision === + print(a.round(1)) # Round to 1 decimal place: 123.5 + print(a.quantize(Decimal("0.01"))) # Format to 2 decimal places: 123.45 + print(a.round(0, RoundingMode.ROUND_DOWN)) # Round down to integer: 123 + + # === Comparison === + print(a > b) # Greater than: True + print(a == Decimal("123.45")) # Equality: True + print(a.is_zero()) # Check for zero: False + print(Decimal("0").is_zero()) # Check for zero: True + + # === Type Conversions === + print(Float64(a)) # To float: 123.45 + print(a.to_int()) # To integer: 123 + print(a.to_str()) # To string: "123.45" + print(a.coefficient()) # Get coefficient: 12345 + print(a.scale()) # Get scale: 2 + + # === Mathematical Functions === + print(Decimal("2").sqrt()) # Square root: 1.4142135623730950488016887242 + print(Decimal("100").root(3)) # Cube root: 4.641588833612778892410076351 + print(Decimal("2.71828").ln()) # Natural log: 0.9999993273472820031578910056 + print(Decimal("10").log10()) # Base-10 log: 1 + print(Decimal("16").log(Decimal("2"))) # Log base 2: 3.9999999999999999999999999999 + print(Decimal("10").exp()) # e^10: 22026.465794806716516957900645 + print(Decimal("2").power(10)) # Power: 1024 + + # === Sign Handling === + print(-a) # Negation: -123.45 + print(abs(Decimal("-123.45"))) # Absolute value: 123.45 + print(Decimal("123.45").is_negative()) # Check if negative: False + + # === Special Values === + print(Decimal.PI()) # Ο€ constant: 3.1415926535897932384626433833 + print(Decimal.E()) # e constant: 2.7182818284590452353602874714 + print(Decimal.ONE()) # Value 1: 1 + print(Decimal.ZERO()) # Value 0: 0 + print(Decimal.MAX()) # Maximum value: 79228162514264337593543950335 + + # === Convenience Methods === + print(Decimal("123.400").is_integer()) # Check if integer: False + print(a.number_of_significant_digits()) # Count significant digits: 5 + print(Decimal("12.34").to_str_scientific()) # Scientific notation: 1.234E+1 ``` -### 2. Basic Arithmetic with Proper Rounding +[Click here for 8 key examples](https://zhuyuhao.com/decimojo/docs/examples) highlighting the most important features of the `Decimal` type. + +Here is a comprehensive quick-start guide showcasing each major function of the `BigInt` type. ```mojo -# Addition with different scales -var a = Decimal("123.45") -var b = Decimal("67.8") -print(a + b) # 191.25 (preserves highest precision) - -# Subtraction with negative result -var c = Decimal("50") -var d = Decimal("75.25") -print(c - d) # -25.25 - -# Multiplication preserving full precision -var e = Decimal("12.34") -var f = Decimal("5.67") -print(e * f) # 69.9678 (all digits preserved) - -# Division with repeating decimals handled precisely -var g = Decimal("1") -var h = Decimal("3") -print(g / h) # 0.3333333333333333333333333333 (to precision limit) +from decimojo import BigInt, BInt +# BInt is an alias for BigInt + +fn main() raises: + # === Construction === + var a = BigInt("12345678901234567890") # From string + var b = BInt(12345) # From integer + + # === Basic Arithmetic === + print(a + b) # Addition: 12345678901234580235 + print(a - b) # Subtraction: 12345678901234555545 + print(a * b) # Multiplication: 152415787814108380241050 + + # === Division Operations === + print(a // b) # Floor division: 999650944609516 + print(a.truncate_divide(b)) # Truncate division: 999650944609516 + print(a % b) # Modulo: 9615 + + # === Power Operation === + print(BInt(2).power(10)) # Power: 1024 + print(BInt(2) ** 10) # Power (using ** operator): 1024 + + # === Comparison === + print(a > b) # Greater than: True + print(a == BInt("12345678901234567890")) # Equality: True + print(a.is_zero()) # Check for zero: False + + # === Type Conversions === + print(a.to_str()) # To string: "12345678901234567890" + + # === Sign Handling === + print(-a) # Negation: -12345678901234567890 + print(abs(BInt("-12345678901234567890"))) # Absolute value: 12345678901234567890 + print(a.is_negative()) # Check if negative: False + + # === Extremely large numbers === + # 3600 digits // 1800 digits + print(BInt("123456789" * 400) // BInt("987654321" * 200)) ``` -### 3. Scale and Precision Management +## Objective -```mojo -# Scale refers to number of decimal places -var d1 = Decimal("123.45") -print(d1.scale()) # 2 +Financial calculations and data analysis require precise decimal arithmetic that floating-point numbers cannot reliably provide. As someone working in finance and credit risk model validation, I needed a dependable correctly-rounded, fixed-precision numeric type when migrating my personal projects from Python to Mojo. -var d2 = Decimal("123.4500") -print(d2.scale()) # 4 +Since Mojo currently lacks a native Decimal type in its standard library, I decided to create my own implementation to fill that gap. -# Arithmetic operations combine scales appropriately -var sum = Decimal("0.123") + Decimal("0.45") # Takes larger scale -print(sum) # 0.573 +This project draws inspiration from several established decimal implementations and documentation, e.g., [Python built-in `Decimal` type](https://docs.python.org/3/library/decimal.html), [Rust `rust_decimal` crate](https://docs.rs/rust_decimal/latest/rust_decimal/index.html), [Microsoft's `Decimal` implementation](https://learn.microsoft.com/en-us/dotnet/api/system.decimal.getbits?view=net-9.0&redirectedfrom=MSDN#System_Decimal_GetBits_System_Decimal_), [General Decimal Arithmetic Specification](https://speleotrove.com/decimal/decarith.html), etc. Many thanks to these predecessors for their contributions and their commitment to open knowledge sharing. -var product = Decimal("0.12") * Decimal("0.34") # Sums the scales -print(product) # 0.0408 +## Nomenclature -# High precision is preserved (up to 28 decimal places) -var precise = Decimal("0.1234567890123456789012345678") -print(precise) # 0.1234567890123456789012345678 -``` +DeciMojo combines "Deci" and "Mojo" - reflecting its purpose and implementation language. "Deci" (from Latin "decimus" meaning "tenth") highlights our focus on the decimal numeral system that humans naturally use for counting and calculations. -### 4. Sign Handling and Absolute Value +Although the name emphasizes decimals with fractional parts, DeciMojo embraces the full spectrum of decimal mathematics. Our `BigInt` type, while handling only integers, is designed specifically for the decimal numeral system with its base-10 internal representation. This approach offers optimal performance while maintaining human-readable decimal semantics, contrasting with binary-focused libraries. Furthermore, `BigInt` serves as the foundation for our `BigDecimal` implementation, enabling arbitrary-precision calculations across both integer and fractional domains. -```mojo -# Negation operator -var pos = Decimal("123.45") -var neg = -pos -print(neg) # -123.45 - -# Multiple negations -var back_to_pos = -(-pos) -print(back_to_pos) # 123.45 - -# Absolute value -var abs_val = abs(Decimal("-987.65")) -print(abs_val) # 987.65 - -# Sign checking -print(Decimal("-123.45").is_negative()) # True -print(Decimal("0").is_negative()) # False -print(Decimal("123.45").is_negative()) # False -``` +The name ultimately emphasizes our mission: bringing precise, reliable decimal calculations to the Mojo ecosystem, addressing the fundamental need for exact arithmetic that floating-point representations cannot provide. -### 5. Advanced Mathematical Operations +## Status -```mojo -from decimojo.mathematics import sqrt +Rome wasn't built in a day. DeciMojo is currently under active development. For the 128-bit `Decimal` type, it has successfully progressed through the **"make it work"** phase and is now well into the **"make it right"** phase with many optimizations already in place. Bug reports and feature requests are welcome! If you encounter issues, please [file them here](https://github.com/forfudan/decimojo/issues). -# Integer powers -var squared = Decimal("3") ** 2 -print(squared) # 9 +### Make it Work βœ… (COMPLETED) -# Negative powers (reciprocals) -var recip = Decimal("2") ** (-1) -print(recip) # 0.5 +- Core decimal implementation with a robust 128-bit representation (96-bit coefficient + 32-bit flags) +- Comprehensive arithmetic operations (+, -, *, /, %, **) with proper overflow handling +- Type conversions to/from various formats (String, Int, Float64, etc.) +- Proper representation of special values (NaN, Infinity) +- Full suite of comparison operators with correct decimal semantics -# Square root with high precision -var root2 = sqrt(Decimal("2")) -print(root2) # 1.414213562373095048801688724... +### Make it Right πŸ”„ (MOSTLY COMPLETED) -# Special cases -print(Decimal("10") ** 0) # 1 (anything to power 0) -print(Decimal("1") ** 20) # 1 (1 to any power) -print(Decimal("0") ** 5) # 0 (0 to positive power) -``` +- Reorganized codebase with modular structure (decimal, arithmetics, comparison, exponential). +- Edge case handling for all operations (division by zero, zero to negative power). +- Scale and precision management with sophisticated rounding strategies. +- Financial calculations with banker's rounding (ROUND_HALF_EVEN). +- High-precision advanced mathematical functions (sqrt, root, ln, exp, log10, power). +- Proper implementation of traits (Absable, Comparable, Floatable, Roundable, etc). +- **BigInt and BigUInt** implementations with complete arithmetic operations, proper division semantics (floor and truncate), and support for arbitrary-precision calculations. -### 6. Type Conversions and Interoperability +### Make it Fast ⚑ (SIGNIFICANT PROGRESS) -```mojo -var d = Decimal("123.456") +DeciMojo delivers exceptional performance compared to Python's `decimal` module while maintaining precise calculations. This performance difference stems from fundamental design choices: -# Converting to string (for display or serialization) -var str_val = String(d) -print(str_val) # "123.456" +- **DeciMojo**: Uses a fixed 128-bit representation (96-bit coefficient + 32-bit flags) with a maximum of 28 decimal places, optimized for modern hardware and Mojo's performance capabilities. +- **Python decimal**: Implements arbitrary precision that can represent numbers with unlimited significant digits but requires dynamic memory allocation and more complex algorithms. -# Getting internal representation -print(repr(d)) # Shows internal state +This architectural difference explains our benchmarking results: -# Converting to int (truncates toward zero) -var i = Int(d) -print(i) # 123 +- Core arithmetic operations (+, -, *, /) achieve 100x-3500x speedup over Python's decimal module. +- Special case handling (powers of 0, 1, etc.) shows up to 3500x performance improvement. +- Advanced mathematical functions (sqrt, ln, exp) demonstrate 5x-600x better performance. +- Only specific edge cases (like computing 10^(1/100)) occasionally perform better in Python due to its arbitrary precision algorithms. -# Converting to float (may lose precision) -var f = Float64(d) -print(f) # 123.456 -``` +Regular benchmarks against Python's `decimal` module are available in the `bench/` folder, documenting both the performance advantages and the few specific operations where different approaches are needed. -### 7. Handling Edge Cases and Errors +### Future Extensions πŸš€ (PLANNED) -```mojo -# Division by zero is properly caught -try: - var result = Decimal("10") / Decimal("0") -except: - print("Division by zero properly detected") - -# Zero raised to negative power -try: - var invalid = Decimal("0") ** (-1) -except: - print("Zero to negative power properly detected") - -# Smallest representable positive value -var tiny = Decimal("0." + "0" * 27 + "1") # 28 decimal places -print(tiny) # 0.0000000000000000000000000001 -``` +- **BigDecimal**: πŸ”„ **IN PROGRESS** - Arbitrary-precision decimal type with configurable precision[^arbitrary]. +- **BigComplex**: πŸ“ **PLANNED** - Arbitrary-precision complex number type built on BigDecimal. -### 8. Equality and Zero Comparisons +## Tests and benches -```mojo -# Equal values with different representations -var a = Decimal("123.4500") -var b = Decimal("123.45") -print(a == b) # True (numeric value equality) - -# Zero values with different scales -var z1 = Decimal("0") -var z2 = Decimal("0.000") -print(z1 == z2) # True - -# Zero detection -print(z1.is_zero()) # True -print(z2.is_zero()) # True -``` +After cloning the repo onto your local disk, you can: -### 9. Real World Financial Examples +- Use `magic run test` to run tests. +- Use `magic run bench_decimal` to generate logs for benchmarking tests against `python.decimal` module. The log files are saved in `benches/decimal/logs/`. -```mojo -# Monthly loan payment calculation -var principal = Decimal("200000") # $200,000 loan -var annual_rate = Decimal("0.05") # 5% interest rate -var monthly_rate = annual_rate / Decimal("12") -var num_payments = Decimal("360") # 30 years - -# Monthly payment formula: P * r(1+r)^n/((1+r)^n-1) -var numerator = monthly_rate * (Decimal("1") + monthly_rate) ** 360 -var denominator = (Decimal("1") + monthly_rate) ** 360 - Decimal("1") -var payment = principal * (numerator / denominator) -print("Monthly payment: $" + String(round(payment, 2))) # $1,073.64 - -# Correct handling of multiple items and discounts -var item1 = Decimal("29.99") -var item2 = Decimal("59.99") -var subtotal = item1 + item2 # 89.98 -var discount = subtotal * Decimal("0.15") # 15% off -var after_discount = subtotal - discount -var tax = after_discount * Decimal("0.08") # 8% tax -var final = after_discount + tax -print("Final price: $" + String(round(final, 2))) -``` +## Citation -### 10. Maximum Precision and Limit Testing +If you find DeciMojo useful for your research, consider listing it in your citations. -```mojo -# Maximum value supported -var max_val = Decimal.MAX() -print(max_val) # 79228162514264337593543950335 - -# Minimum value supported -var min_val = Decimal.MIN() -print(min_val) # -79228162514264337593543950335 - -# Operations near limits -var near_max = Decimal("79228162514264337593543950334") # MAX() - 1 -var still_valid = near_max + Decimal("1") -print(still_valid == max_val) # True - -# Maximum precision for high-accuracy scientific calculations -var pi = Decimal("3.1415926535897932384626433832") -var radius = Decimal("2.5") -var area = pi * (radius ** 2) -print("Circle area: " + String(area)) # Precisely calculated area +```tex +@software{Zhu.2025, + author = {Zhu, Yuhao}, + year = {2025}, + title = {A comprehensive decimal and integer mathematics library for Mojo}, + url = {https://github.com/forfudan/decimojo}, + version = {0.2.0}, + note = {Computer Software} +} ``` ## License -Distributed under the Apache 2.0 License. See [LICENSE](https://github.com/forFudan/decimojo/blob/main/LICENSE) for details. +This repository and its contributions are licensed under the Apache License v2.0. + +[^fixed]: The `Decimal` type can represent values with up to 29 significant digits and a maximum of 28 digits after the decimal point. When a value exceeds the maximum representable value (`2^96 - 1`), DeciMojo either raises an error or rounds the value to fit within these constraints. For example, the significant digits of `8.8888888888888888888888888888` (29 eights total with 28 after the decimal point) exceeds the maximum representable value (`2^96 - 1`) and is automatically rounded to `8.888888888888888888888888889` (28 eights total with 27 after the decimal point). DeciMojo's `Decimal` type is similar to `System.Decimal` (C#/.NET), `rust_decimal` in Rust, `DECIMAL/NUMERIC` in SQL Server, etc. +[^integer]: The BigInt implementation uses a base-10 representation for users (maintaining decimal semantics), while internally using an optimized base-10^9 storage system for efficient calculations. This approach balances human-readable decimal operations with high-performance computing. It provides both floor division (round toward negative infinity) and truncate division (round toward zero) semantics, enabling precise handling of division operations with correct mathematical behavior regardless of operand signs. +[^arbitrary]: Built on top of our completed BigInt implementation, BigDecimal will support arbitrary precision for both the integer and fractional parts, similar to `decimal` and `mpmath` in Python, `java.math.BigDecimal` in Java, etc. diff --git a/recipes/decimojo/recipe.yaml b/recipes/decimojo/recipe.yaml index 711a4efb..5624c81b 100644 --- a/recipes/decimojo/recipe.yaml +++ b/recipes/decimojo/recipe.yaml @@ -1,21 +1,21 @@ context: - version: "0.1.0" + version: "0.2.0" package: name: "decimojo" version: ${{ version }} source: - - git: https://github.com/forFudan/DeciMojo.git - rev: 47f0a517f5a8e350b18531bd07798a7e22be23bd + - git: https://github.com/forFudan/decimojo.git + rev: fba2fa810382d34f36907b18312340ff730bcec8 build: number: 0 script: - - mojo package decimojo -o ${{ PREFIX }}/lib/mojo/decimojo.mojopkg + - mojo package src/decimojo -o ${{ PREFIX }}/lib/mojo/decimojo.mojopkg requirements: host: - - max=25.1 + - max=25.2 run: - ${{ pin_compatible('max') }} @@ -26,19 +26,19 @@ tests: - mojo run tests.mojo requirements: run: - - max=25.1 + - max=25.2 files: recipe: - tests.mojo about: - homepage: https://github.com/forFudan/DeciMojo.git + homepage: https://github.com/forFudan/decimojo.git license: Apache-2.0 license_file: LICENSE - summary: DeciMojo is a fixed-point decimal arithmetic library in Mojo - repository: https://github.com/forFudan/DeciMojo.git + summary: A comprehensive decimal and integer mathematics library for Mojo + repository: https://github.com/forFudan/decimojo.git extra: - project_name: DeciMojo + project_name: decimojo maintainers: - - forFudan + - forfudan diff --git a/recipes/decimojo/tests.mojo b/recipes/decimojo/tests.mojo index a68e0bc2..45386a44 100644 --- a/recipes/decimojo/tests.mojo +++ b/recipes/decimojo/tests.mojo @@ -589,14 +589,6 @@ fn test_multiplication() raises: "Multiplication by zero should yield zero (positive number case)", ) - # Test 15c: Multiplication of negative number by zero - var value15c = Decimal("-987.654") - testing.assert_equal( - String(value15c * zero), - "0.000", - "Multiplication by zero should yield zero (negative number case)", - ) - # Test 15d: Multiplication of small number by zero var value15d = Decimal("0.0001") testing.assert_equal( @@ -605,14 +597,6 @@ fn test_multiplication() raises: "Multiplication by zero should yield zero (small number case)", ) - # Test 15e: Multiplication of large negative number by zero - var value15e = Decimal("-99999.99999") - testing.assert_equal( - String(value15e * zero), - "0.00000", - "Multiplication by zero should yield zero (large negative number case)", - ) - print("Decimal multiplication tests passed!") @@ -933,7 +917,6 @@ fn test_division() raises: var result34 = a34 / b34 # Result should be about 0.66666... var expected34 = Decimal("0.66666666666666666666666666667") - print(result34) testing.assert_equal( result34, expected34,