use super::funejson::Value; 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 mass index for an adult: weight over height squared, to one decimal /// place, and the band it falls in. The bands are published to one decimal /// place (18.5-24.9, 25-29.9, ...), so the category is read from the rounded /// index: 24.97 is reported as 25.0 and is overweight, not healthy. /// /// # Panics /// Panics when the weight or height is outside its range. pub fn bmi(weight_kg: f64, height_cm: f64) -> Bmi { check_range("weightKg", weight_kg, 10.0, 650.0, "kg"); // Height in metres (1.75) is the commonest unit slip; 50 cm is the floor. check_range("heightCm", height_cm, 50.0, 272.0, "cm"); let metres = height_cm / 100.0; let value = round_float(weight_kg / (metres * metres), 1); let category = if value < 18.5 { "underweight" } else if value <= 24.9 { "healthy-weight" } else if value <= 29.9 { "overweight" } else if value <= 34.9 { "obesity-class-1" } else if value <= 39.9 { "obesity-class-2" } else { "obesity-class-3" }; Bmi { value, category: category.to_string() } } pub fn bmi_to_value(result: &Bmi) -> Value { Value::obj(vec![("value", Value::Float(result.value)), ("category", Value::str(&result.category))]) } 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 { bmi_to_value(&bmi( number_arg(&args[0], "weightKg", 10.0, 650.0, "kg"), number_arg(&args[1], "heightCm", 50.0, 272.0, "cm"), )) }