import math from .health_bsa_types import BodySurfaceArea from .math_pow import pow from .math_round_float import round_float def _check_range(name: str, value: float, low: float, high: float, unit: str) -> None: ok = not isinstance(value, bool) and isinstance(value, (int, float)) and math.isfinite(value) if not ok or value < low or value > high: raise ValueError("%s must be a number from %s to %s %s, received %r" % (name, low, high, unit, value)) def body_surface_area(weight_kg: float, height_cm: float) -> BodySurfaceArea: """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 ** or math.sqrt, so the three languages agree to the bit before rounding.""" _check_range("weightKg", weight_kg, 1, 650, "kg") # Height in metres (1.75) is the commonest unit slip. _check_range("heightCm", height_cm, 30, 272, "cm") w = float(weight_kg) h = float(height_cm) # Mosteller (1987): BSA (m2) = ([height (cm) x weight (kg)] / 3600)^1/2. mosteller = pow((h * w) / 3600, 0.5) # DuBois & DuBois (1916): A = W^0.425 x H^0.725 x 71.84, A in cm2. du_bois = (pow(w, 0.425) * pow(h, 0.725) * 71.84) / 10000 return BodySurfaceArea(mosteller=round_float(mosteller, 2), du_bois=round_float(du_bois, 2))