#[path = "support.rs"]
mod support;
fn main() {
support::header("noyalib -- scientific (lossless numeric profile)");
#[cfg(not(feature = "lossless-float"))]
{
println!(" This example requires the 'lossless-float' feature.");
println!(" Run: cargo run --example scientific --features lossless-float");
println!();
}
#[cfg(feature = "lossless-float")]
run_examples();
}
#[cfg(feature = "lossless-float")]
#[derive(Debug, Clone, Copy, PartialEq)]
struct Radians(pub f64);
#[cfg(feature = "lossless-float")]
impl Radians {
fn to_degrees(self) -> f64 {
self.0.to_degrees()
}
}
#[cfg(feature = "lossless-float")]
impl<'de> serde::Deserialize<'de> for Radians {
fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
let degrees = f64::deserialize(de)?;
Ok(Self(degrees.to_radians()))
}
}
#[cfg(feature = "lossless-float")]
impl serde::Serialize for Radians {
fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_f64(self.0)
}
}
#[cfg(feature = "lossless-float")]
fn run_examples() {
use noyalib::lossless_float::LosslessFloat;
support::task_with_output("LosslessFloat: valid values", || {
let cases: &[(&str, f64)] = &[
("0.0", 0.0),
("1.0", 1.0),
("-1.0", -1.0),
("9.81", 9.81),
("1.0e10", 1.0e10),
];
let mut lines = Vec::new();
for &(yaml, expected) in cases {
let sf: LosslessFloat = noyalib::from_str(yaml).unwrap();
assert!((sf.get() - expected).abs() < 1e-10);
lines.push(format!("{yaml:>12} -> {}", sf.get()));
}
lines
});
support::task_with_output("LosslessFloat: rejects NaN and Infinity", || {
let mut lines = Vec::new();
for yaml in &[".nan", ".inf", "-.inf"] {
let result: Result<LosslessFloat, _> = noyalib::from_str(yaml);
assert!(result.is_err());
lines.push(format!("{yaml:>8} -> rejected ({})", result.unwrap_err()));
}
lines
});
support::task_with_output("Radians: degrees in YAML -> radians in Rust", || {
let cases: &[(&str, f64)] = &[
("0.0", 0.0),
("90.0", core::f64::consts::FRAC_PI_2),
("180.0", core::f64::consts::PI),
("360.0", core::f64::consts::TAU),
("-90.0", -core::f64::consts::FRAC_PI_2),
];
let mut lines = Vec::new();
for &(yaml, expected_rad) in cases {
let r: Radians = noyalib::from_str(yaml).unwrap();
assert!((r.0 - expected_rad).abs() < 1e-10);
lines.push(format!("{yaml:>8} deg -> {:.6} rad", r.0));
}
lines
});
support::task_with_output("Sensor calibration: joint angles from YAML", || {
let yaml = r"
joint1: 90.0
joint2: -45.0
joint3: 180.0
joint4: 0.0
joint5: 270.0
joint6: 135.0
";
#[derive(Debug, serde::Deserialize)]
struct RobotArm {
joint1: Radians,
joint2: Radians,
joint3: Radians,
joint4: Radians,
joint5: Radians,
joint6: Radians,
}
let arm: RobotArm = noyalib::from_str(yaml).unwrap();
let joints = [
arm.joint1, arm.joint2, arm.joint3, arm.joint4, arm.joint5, arm.joint6,
];
let mut lines = Vec::new();
for (i, j) in joints.iter().enumerate() {
lines.push(format!(
"joint{}: {:.4} rad ({:.1} deg)",
i + 1,
j.0,
j.to_degrees()
));
}
lines
});
support::task_with_output("Round-trip: serialize Radians back to YAML", || {
let r = Radians(core::f64::consts::PI);
let yaml = noyalib::to_string(&r).unwrap();
let parsed: f64 = noyalib::from_str(yaml.trim()).unwrap();
assert!((parsed - core::f64::consts::PI).abs() < 1e-10);
vec![
format!("Radians(PI) serialized as: {}", yaml.trim()),
format!("Deserialized back as f64: {parsed:.10}"),
]
});
support::task_with_output("LosslessFloat in a calibration struct", || {
let yaml = r"
offset_x: 0.001
offset_y: -0.002
scale: 1.00015
";
#[derive(Debug, serde::Deserialize)]
struct Calibration {
offset_x: LosslessFloat,
offset_y: LosslessFloat,
scale: LosslessFloat,
}
let cal: Calibration = noyalib::from_str(yaml).unwrap();
vec![
format!("offset_x = {}", cal.offset_x.get()),
format!("offset_y = {}", cal.offset_y.get()),
format!("scale = {}", cal.scale.get()),
]
});
support::summary(6);
}