uninum 0.1.1

A robust, ergonomic unified number type for Rust with automatic overflow handling, type promotion, and cross-type consistency.
Documentation
//! Financial calculation showcase for `uninum`.
//!
//! Run with `cargo run --example financial` to see the scenarios and to have
//! basic assertions verify the results.

use uninum::{Number, num};

fn calculate_compound_interest(principal: Number, rate: Number, years: Number) -> Number {
    let one = num!(1);
    let compound_factor = &one + &rate;
    &principal * compound_factor.pow(&years)
}

fn run_compound_interest_demo() {
    let principal = num!(1000);
    let rate = num!(0.05); // 5% interest - uses Decimal for precision
    let years = num!(2);

    let final_amount = calculate_compound_interest(principal, rate, years);
    let expected = num!(1102.5);

    assert!(
        final_amount.approx_eq(&expected, 1e-10, 0.0),
        "Expected {expected}, got {final_amount}"
    );
    println!("Compound interest result: {final_amount}");
}

#[cfg(feature = "decimal")]
fn run_precise_financial_arithmetic_demo() {
    let price = num!(19.99); // Automatically uses Decimal for precision
    let tax_rate = num!(0.0825); // 8.25% - exact decimal representation
    let quantity = num!(3);

    let subtotal = &price * &quantity;
    let tax = &subtotal * &tax_rate;
    let total = subtotal + tax;

    let expected = num!(64.917525);
    assert!(
        total.approx_eq(&expected, 1e-10, 0.0),
        "Expected exact decimal result {expected}, got {total}"
    );
    assert!(
        total.try_get_decimal().is_some(),
        "Should use Decimal for financial calculations"
    );
    println!("Invoice total with tax: {total}");
}

fn run_portfolio_value_demo() {
    struct Asset {
        shares: Number,
        price_per_share: Number,
    }

    let portfolio = [
        Asset {
            shares: num!(100),
            price_per_share: num!(150.50),
        },
        Asset {
            shares: num!(50),
            price_per_share: num!(75.25),
        },
        Asset {
            shares: num!(200),
            price_per_share: num!(25.00),
        },
    ];

    let total_value = portfolio
        .iter()
        .map(|asset| &asset.shares * &asset.price_per_share)
        .reduce(|acc, val| acc + val)
        .unwrap_or_else(|| num!(0));

    let expected = num!(23812.5);
    assert!(
        total_value.approx_eq(&expected, 1e-10, 0.0),
        "Portfolio calculation precision error: expected {expected}, got {total_value}"
    );
    println!("Total portfolio value: {total_value}");
}

fn calculate_tax(income: Number) -> Number {
    let bracket1_limit = num!(10000);
    let bracket2_limit = num!(40000);

    let rate1 = num!(0.10);
    let rate2 = num!(0.20);
    let rate3 = num!(0.30);

    if income <= bracket1_limit {
        &income * &rate1
    } else if income <= bracket2_limit {
        let tax1 = &bracket1_limit * &rate1;
        let tax2 = (&income - &bracket1_limit) * &rate2;
        tax1 + tax2
    } else {
        let tax1 = &bracket1_limit * &rate1;
        let tax2 = (&bracket2_limit - &bracket1_limit) * &rate2;
        let tax3 = (&income - &bracket2_limit) * &rate3;
        tax1 + tax2 + tax3
    }
}

fn run_tax_bracket_demo() {
    let low_income = num!(8000);
    let mid_income = num!(35000);
    let high_income = num!(60000);

    let tax1 = calculate_tax(low_income);
    let tax2 = calculate_tax(mid_income);
    let tax3 = calculate_tax(high_income);

    assert!(tax1.approx_eq(&num!(800.0), 1e-10, 0.0));
    assert!(tax2.approx_eq(&num!(6000.0), 1e-10, 0.0));
    assert!(tax3.approx_eq(&num!(13000.0), 1e-10, 0.0));
    assert!(tax1.is_finite() && tax2.is_finite() && tax3.is_finite());
    println!("Tax brackets => low: {tax1}, mid: {tax2}, high: {tax3}");
}

fn main() {
    run_compound_interest_demo();
    #[cfg(feature = "decimal")]
    run_precise_financial_arithmetic_demo();
    run_portfolio_value_demo();
    run_tax_bracket_demo();
    println!("Financial examples completed successfully.");
}