sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
use sciforge::benchmark::decode::decode;
use sciforge::benchmark::engine::bench;
use sciforge::benchmark::export::{Entry, export};
use sciforge::benchmark::report::generate;
use sciforge::hub::prelude::*;
use std::fs;
use std::path::Path;

struct Conversion {
    name: &'static str,
    category: &'static str,
    value: f64,
    from: &'static str,
    to: &'static str,
}

fn main() {
    let dir = Path::new("output/examples/unit_converter");
    fs::create_dir_all(dir).unwrap();

    let conversions: Vec<Conversion> = vec![
        Conversion {
            name: "Marathon distance",
            category: "Length",
            value: 42.195,
            from: "km",
            to: "mi",
        },
        Conversion {
            name: "Everest height",
            category: "Length",
            value: 8848.86,
            from: "m",
            to: "ft",
        },
        Conversion {
            name: "Light-year",
            category: "Length",
            value: 1.0,
            from: "ly",
            to: "km",
        },
        Conversion {
            name: "Human body temp",
            category: "Temperature",
            value: 37.0,
            from: "\u{00b0}C",
            to: "K",
        },
        Conversion {
            name: "Liquid nitrogen",
            category: "Temperature",
            value: 77.36,
            from: "K",
            to: "\u{00b0}C",
        },
        Conversion {
            name: "Sun surface",
            category: "Temperature",
            value: 5778.0,
            from: "K",
            to: "\u{00b0}F",
        },
        Conversion {
            name: "Atmospheric",
            category: "Pressure",
            value: 101325.0,
            from: "Pa",
            to: "atm",
        },
        Conversion {
            name: "Deep ocean",
            category: "Pressure",
            value: 1100.0,
            from: "atm",
            to: "Pa",
        },
        Conversion {
            name: "Earth mass",
            category: "Mass",
            value: 5.972e24,
            from: "kg",
            to: "lb",
        },
        Conversion {
            name: "Proton mass",
            category: "Mass",
            value: 1.672e-27,
            from: "kg",
            to: "amu",
        },
        Conversion {
            name: "1 eV",
            category: "Energy",
            value: 1.0,
            from: "eV",
            to: "J",
        },
        Conversion {
            name: "TNT equivalent",
            category: "Energy",
            value: 4.184e9,
            from: "J",
            to: "cal",
        },
        Conversion {
            name: "1 day",
            category: "Time",
            value: 1.0,
            from: "day",
            to: "s",
        },
        Conversion {
            name: "1 year",
            category: "Time",
            value: 1.0,
            from: "year",
            to: "h",
        },
        Conversion {
            name: "Right angle",
            category: "Angle",
            value: 90.0,
            from: "deg",
            to: "rad",
        },
    ];

    let result_strs: Vec<String> = conversions
        .iter()
        .map(|c| {
            let converted = match c.category {
                "Length" => {
                    let (from_u, to_u) = match (c.from, c.to) {
                        ("km", "mi") => (LengthUnit::Kilometer, LengthUnit::Mile),
                        ("m", "ft") => (LengthUnit::Meter, LengthUnit::Foot),
                        ("ly", "km") => (LengthUnit::LightYear, LengthUnit::Kilometer),
                        _ => (LengthUnit::Meter, LengthUnit::Meter),
                    };
                    let si = length_to_si(c.value, from_u);
                    length_from_si(si, to_u)
                }
                "Temperature" => {
                    let (from_u, to_u) = match (c.from, c.to) {
                        ("\u{00b0}C", "K") => (TemperatureUnit::Celsius, TemperatureUnit::Kelvin),
                        ("K", "\u{00b0}C") => (TemperatureUnit::Kelvin, TemperatureUnit::Celsius),
                        ("K", "\u{00b0}F") => {
                            (TemperatureUnit::Kelvin, TemperatureUnit::Fahrenheit)
                        }
                        _ => (TemperatureUnit::Kelvin, TemperatureUnit::Kelvin),
                    };
                    let k = temperature_to_kelvin(c.value, from_u);
                    kelvin_to_temperature(k, to_u)
                }
                "Pressure" => {
                    let (from_u, to_u) = match (c.from, c.to) {
                        ("Pa", "atm") => (PressureUnit::Pascal, PressureUnit::Atmosphere),
                        ("atm", "Pa") => (PressureUnit::Atmosphere, PressureUnit::Pascal),
                        _ => (PressureUnit::Pascal, PressureUnit::Pascal),
                    };
                    let si = pressure_to_si(c.value, from_u);
                    pressure_from_si(si, to_u)
                }
                "Mass" => {
                    let (from_u, to_u) = match (c.from, c.to) {
                        ("kg", "lb") => (MassUnit::Kilogram, MassUnit::Pound),
                        ("kg", "amu") => (MassUnit::Kilogram, MassUnit::Dalton),
                        _ => (MassUnit::Kilogram, MassUnit::Kilogram),
                    };
                    let si = mass_to_si(c.value, from_u);
                    mass_from_si(si, to_u)
                }
                "Energy" => {
                    let (from_u, to_u) = match (c.from, c.to) {
                        ("eV", "J") => (EnergyUnit::ElectronVolt, EnergyUnit::Joule),
                        ("J", "cal") => (EnergyUnit::Joule, EnergyUnit::Calorie),
                        _ => (EnergyUnit::Joule, EnergyUnit::Joule),
                    };
                    let si = energy_to_si(c.value, from_u);
                    energy_from_si(si, to_u)
                }
                "Time" => {
                    let (from_u, to_u) = match (c.from, c.to) {
                        ("day", "s") => (TimeUnit::Day, TimeUnit::Second),
                        ("year", "h") => (TimeUnit::Year, TimeUnit::Hour),
                        _ => (TimeUnit::Second, TimeUnit::Second),
                    };
                    let si = time_to_si(c.value, from_u);
                    time_from_si(si, to_u)
                }
                "Angle" => {
                    let from_u = match c.from {
                        "deg" => AngleUnit::Degree,
                        _ => AngleUnit::Radian,
                    };
                    let to_u = match c.to {
                        "rad" => AngleUnit::Radian,
                        _ => AngleUnit::Degree,
                    };
                    let rad = angle_to_radian(c.value, from_u);
                    radian_to_angle(rad, to_u)
                }
                _ => c.value,
            };
            format!("{:.6e}", converted)
        })
        .collect();

    let display_strs: Vec<String> = conversions
        .iter()
        .zip(result_strs.iter())
        .map(|(c, r)| format!("{} {} \u{2192} {} {}", c.value, c.from, r, c.to))
        .collect();

    let cat_strs: Vec<String> = conversions.iter().map(|c| c.category.to_string()).collect();
    let from_strs: Vec<String> = conversions
        .iter()
        .map(|c| format!("{} {}", c.value, c.from))
        .collect();
    let to_strs: Vec<String> = conversions
        .iter()
        .zip(result_strs.iter())
        .map(|(c, r)| format!("{} {}", r, c.to))
        .collect();

    let mut metrics_store: Vec<(String, String, _)> = Vec::new();

    let exp_names: Vec<String> = conversions
        .iter()
        .map(|c| format!("{}_convert", c.name))
        .collect();

    for (idx, c) in conversions.iter().enumerate() {
        let val = c.value;
        let cat = c.category;
        let m = bench(&exp_names[idx], "f64", 10000, move || {
            let mut v = 0u64;
            for i in 0..100 {
                let x = val + (i as f64) * 0.001;
                let r = match cat {
                    "Length" => {
                        length_from_si(length_to_si(x, LengthUnit::Meter), LengthUnit::Foot)
                    }
                    "Temperature" => kelvin_to_temperature(
                        temperature_to_kelvin(x, TemperatureUnit::Celsius),
                        TemperatureUnit::Fahrenheit,
                    ),
                    "Pressure" => pressure_from_si(
                        pressure_to_si(x, PressureUnit::Pascal),
                        PressureUnit::Atmosphere,
                    ),
                    "Mass" => mass_from_si(mass_to_si(x, MassUnit::Kilogram), MassUnit::Pound),
                    "Energy" => {
                        energy_from_si(energy_to_si(x, EnergyUnit::Joule), EnergyUnit::Calorie)
                    }
                    "Time" => time_from_si(time_to_si(x, TimeUnit::Day), TimeUnit::Hour),
                    "Angle" => {
                        radian_to_angle(angle_to_radian(x, AngleUnit::Degree), AngleUnit::Radian)
                    }
                    _ => x,
                };
                v = v.wrapping_add(r.to_bits());
            }
            v
        });
        let result = format!("{} | {}", c.name, display_strs[idx]);
        let rpt = generate(&m, &result);
        assert!(rpt.csv.starts_with("experiment_name"));
        metrics_store.push((c.name.to_string(), result, m));
    }

    let entries: Vec<Entry<'_>> = metrics_store
        .iter()
        .enumerate()
        .map(|(idx, (label, result, m))| Entry {
            metrics: m,
            result: result.as_str(),
            label: label.as_str(),
            tags: vec![
                ("category", cat_strs[idx].as_str()),
                ("symbol", conversions[idx].from),
                ("name", conversions[idx].name),
                ("from", from_strs[idx].as_str()),
                ("to", to_strs[idx].as_str()),
                ("conversion", display_strs[idx].as_str()),
            ],
            grid_row: Some((idx as u8 / 5) + 1),
            grid_col: Some((idx as u8 % 5) + 1),
        })
        .collect();

    let summary = export(
        "Unit Converter \u{2014} SI, CGS & Common Unit Conversions",
        &entries,
        dir,
    )
    .unwrap();
    assert!(summary.files_written > 0);

    let bmk_path = dir.join("bmk/unit_converter.bmk");
    let bytes = fs::read(&bmk_path).unwrap();
    let view = decode(&bytes).unwrap();
    assert!(!view.experiment_name.is_empty());
}