sim-nest 0.1.5

The SIM constellation umbrella facade: one crate that re-exports the runtime kernel, codecs, number domains, and libraries behind features.
Documentation
#[cfg(feature = "numbers-prelude")]
use sim_kernel::{Args, Expr, NumberLiteral, QuoteMode, Symbol};

#[cfg(feature = "numbers-prelude")]
use crate::numbers_prelude::NumbersPreludeLib;

#[cfg(feature = "numbers-prelude")]
use super::support::eval_cx;

#[cfg(feature = "numbers-prelude")]
#[test]
fn numbers_prelude_loads_stable_stack_idempotently() {
    let mut cx = eval_cx();
    NumbersPreludeLib::new().install_all(&mut cx).unwrap();
    NumbersPreludeLib::new().install_all(&mut cx).unwrap();

    for symbol in [
        Symbol::new("+"),
        Symbol::new("fn"),
        Symbol::new("diff"),
        Symbol::new("integrate-sym"),
        Symbol::new("eval-cas"),
        Symbol::new("numeric-diff"),
        Symbol::new("integrate"),
        Symbol::new("ode-solve"),
        Symbol::new("vec"),
        Symbol::new("matmul"),
    ] {
        assert!(cx.resolve_function(&symbol).is_ok(), "missing {symbol}");
    }
}

#[cfg(feature = "numbers-prelude")]
#[test]
fn numeric_worked_example_from_numeric_4_passes() {
    let mut cx = eval_cx();
    NumbersPreludeLib::new().install_all(&mut cx).unwrap();

    let xs = cx
        .eval_expr(Expr::Call {
            operator: Box::new(Expr::Symbol(Symbol::new("vec"))),
            args: vec![num("0"), num("1"), num("2"), num("3")],
        })
        .unwrap();
    cx.env_mut().define(Symbol::new("x"), xs);

    let polynomial = cx
        .eval_expr(Expr::Call {
            operator: Box::new(Expr::Symbol(Symbol::new("+"))),
            args: vec![
                Expr::Call {
                    operator: Box::new(Expr::Symbol(Symbol::new("*"))),
                    args: vec![
                        rat("1/2"),
                        Expr::Call {
                            operator: Box::new(Expr::Symbol(Symbol::new("^"))),
                            args: vec![quoted("x"), num("2")],
                        },
                    ],
                },
                quoted("a"),
            ],
        })
        .unwrap();
    let derivative = cx
        .call_function(
            &Symbol::new("diff"),
            Args::new(vec![
                polynomial,
                cx.factory().symbol(Symbol::new("x")).unwrap(),
            ]),
        )
        .unwrap();
    assert_eq!(
        cx.call_function(&Symbol::new("eval-cas"), Args::new(vec![derivative]))
            .unwrap()
            .object()
            .as_expr(&mut cx)
            .unwrap(),
        Expr::Vector(vec![num("0"), num("1"), num("2"), num("3")])
    );

    let func = cx
        .eval_expr(Expr::Call {
            operator: Box::new(Expr::Symbol(Symbol::new("fn"))),
            args: vec![
                Expr::List(vec![Expr::Symbol(Symbol::new("x"))]),
                Expr::Call {
                    operator: Box::new(Expr::Symbol(Symbol::new("+"))),
                    args: vec![
                        Expr::Call {
                            operator: Box::new(Expr::Symbol(Symbol::new("*"))),
                            args: vec![
                                rat("1/2"),
                                Expr::Call {
                                    operator: Box::new(Expr::Symbol(Symbol::new("^"))),
                                    args: vec![quoted("x"), num("2")],
                                },
                            ],
                        },
                        num("1"),
                    ],
                },
            ],
        })
        .unwrap();

    let func_expr = func.object().as_expr(&mut cx).unwrap();
    let approx = cx
        .eval_expr(Expr::Call {
            operator: Box::new(Expr::Symbol(Symbol::new("integrate"))),
            args: vec![
                func_expr,
                quoted("x"),
                num("0"),
                num("1"),
                Expr::Symbol(Symbol::new(":method")),
                quoted("simpson"),
                Expr::Symbol(Symbol::new(":n")),
                num("100"),
            ],
        })
        .unwrap();
    let exact = cx
        .call_function(
            &Symbol::new("integrate-sym"),
            Args::new(vec![func, cx.factory().symbol(Symbol::new("x")).unwrap()]),
        )
        .unwrap();
    let one = num_value(&mut cx, "1");
    let exact_at_one = cx.call_value(exact, Args::new(vec![one])).unwrap();

    assert_eq!(exact_at_one.object().display(&mut cx).unwrap(), "7/6");
    let approx_value = parse_numeric_display(&approx.object().display(&mut cx).unwrap());
    assert!((approx_value - (7.0 / 6.0)).abs() < 1.0e-6);
}

#[cfg(feature = "numbers-prelude")]
fn quoted(name: &str) -> Expr {
    Expr::Quote {
        mode: QuoteMode::Quote,
        expr: Box::new(Expr::Symbol(Symbol::new(name))),
    }
}

#[cfg(feature = "numbers-prelude")]
fn num(text: &str) -> Expr {
    Expr::Number(NumberLiteral {
        domain: Symbol::qualified("numbers", "i64"),
        canonical: text.to_owned(),
    })
}

#[cfg(feature = "numbers-prelude")]
fn rat(text: &str) -> Expr {
    Expr::Number(NumberLiteral {
        domain: Symbol::qualified("numbers", "rational"),
        canonical: text.to_owned(),
    })
}

#[cfg(feature = "numbers-prelude")]
fn num_value(cx: &mut sim_kernel::Cx, text: &str) -> sim_kernel::Value {
    cx.factory()
        .number_literal(Symbol::qualified("numbers", "i64"), text.to_owned())
        .unwrap()
}

#[cfg(feature = "numbers-prelude")]
fn parse_numeric_display(text: &str) -> f64 {
    if let Some((num, den)) = text.split_once('/') {
        let num = num.parse::<f64>().unwrap();
        let den = den.parse::<f64>().unwrap();
        return num / den;
    }
    text.parse::<f64>().unwrap()
}