use super::funejson::Value; use super::math_pow::pow; use super::math_round_float::round_float; fn check_range(name: &str, value: f64, low: f64, high: f64, unit: &str) { if !value.is_finite() || value < low || value > high { panic!("{} must be a number from {} to {} {}, received {}", name, low, high, unit, value); } } /// Body surface area by both published formulas. They differ by up to a few /// per cent, and a dose protocol names one, so both are returned rather than a /// silent default. Powers go through math.pow, never `powf` or `sqrt`, so the /// three languages agree to the bit before rounding. /// /// # Panics /// Panics when the weight or height is outside its range. pub fn body_surface_area(weight_kg: f64, height_cm: f64) -> BodySurfaceArea { check_range("weightKg", weight_kg, 1.0, 650.0, "kg"); // Height in metres (1.75) is the commonest unit slip. check_range("heightCm", height_cm, 30.0, 272.0, "cm"); // Mosteller (1987): BSA (m²) = ([height (cm) × weight (kg)] / 3600)^½. let mosteller = pow((height_cm * weight_kg) / 3600.0, 0.5); // DuBois & DuBois (1916): A = W^0.425 × H^0.725 × 71.84, A in cm². let du_bois = (pow(weight_kg, 0.425) * pow(height_cm, 0.725) * 71.84) / 10000.0; BodySurfaceArea { mosteller: round_float(mosteller, 2), du_bois: round_float(du_bois, 2) } } pub fn body_surface_area_to_value(result: &BodySurfaceArea) -> Value { Value::obj(vec![("mosteller", Value::Float(result.mosteller)), ("duBois", Value::Float(result.du_bois))]) } fn number_arg(value: &Value, name: &str, low: f64, high: f64, unit: &str) -> f64 { match value { Value::Int(_) | Value::Float(_) => value.as_f64(), _ => panic!("{} must be a number from {} to {} {}, received {:?}", name, low, high, unit, value), } } pub fn fune_vector(args: &[Value]) -> Value { body_surface_area_to_value(&body_surface_area( number_arg(&args[0], "weightKg", 1.0, 650.0, "kg"), number_arg(&args[1], "heightCm", 30.0, 272.0, "cm"), )) }