Functional Weave
Code in Rust

math.round-div-big@1.0.0

impl/rust.rs

2,020 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_big_integer::{parse_big_integer, BigInt};  ← from math.big-integer ^1.0.0 · built alongside by fune

/// math.round-div, for integers too big for an i64.
///
/// An exact product such as distance x gallon size x price per litre easily
/// passes 2^63, where `round_div` would overflow. The rounding rules are the
/// same as math.round-div's: every mode works on the magnitude and puts the
/// sign back afterwards, so -7/2 half-up is -4.
///
/// # Panics
/// Panics on a malformed number, a zero denominator or an unknown mode.
pub fn round_div_big(numerator: &str, denominator: &str, mode: &str) -> String {
    let n0 = parse_big_integer(numerator);
    let d0 = parse_big_integer(denominator);
    if d0.is_zero() {
        panic!("denominator must not be zero");
    }
    let negative = n0.is_negative() != d0.is_negative();
    let n = n0.abs();
    let d = d0.abs();
    let (mut quotient, remainder) = n.div_rem(&d);
    let twice = remainder.add(&remainder);
    let one = BigInt::from_i64(1);
    match mode {
        "down" => {}
        "up" => {
            if !remainder.is_zero() {
                quotient = quotient.add(&one);
            }
        }
        "half-even" => {
            let odd = !quotient.rem(&BigInt::from_i64(2)).is_zero();
            if twice > d || (twice == d && odd) {
                quotient = quotient.add(&one);
            }
        }
        "half-up" => {
            if twice >= d {
                quotient = quotient.add(&one);
            }
        }
        other => panic!("unknown rounding mode \"{}\"", other),
    }
    if negative && !quotient.is_zero() {
        quotient = quotient.neg();
    }
    quotient.to_string()
}

pub fn fune_vector(args: &[Value]) -> Value {
    for (i, name) in [(0usize, "numerator"), (1usize, "denominator")] {
        if !matches!(args[i], Value::Str(_)) {
            panic!("not a whole number in decimal: {} must be a string", name);
        }
    }
    Value::str(&round_div_big(args[0].as_str(), args[1].as_str(), args[2].as_str()))
}