diff --git a/lib/growth/score/bmi.ex b/lib/growth/score/bmi.ex index c5fe2c6..6d0266a 100644 --- a/lib/growth/score/bmi.ex +++ b/lib/growth/score/bmi.ex @@ -2,4 +2,70 @@ defmodule Growth.Score.BMI do @moduledoc """ Calculate z-score for body mass index for age. """ + + alias Growth.Calc.Centile + alias Growth.Calc.ZScore + + @spec result(Growth.t()) :: Growth.t() + @doc """ + Calculate z-score and centile values and add them to the growth measurement results. + """ + + def result(%Growth{bmi: nil} = measure) do + measure + end + + def result(%Growth{results: results} = measure) do + result = + measure + |> lms() + |> Enum.map(fn {precision, {l, m, s}} -> + {precision, {z_score(measure.bmi, l, m, s), centile(measure.bmi, l, m, s)}} + end) + + %{measure | results: [%{bmi: result} | results]} + end + + @spec lms(Growth.t()) :: [{String.t(), {number(), number(), number()}}] + @doc """ + Get the fitted values of Box-Cox: + + * power (`l`) + * median (`m`) + * coefficient of variation (`s`) + + """ + def lms(%Growth{} = measure) do + [ + {measure.gender, "day", measure.age_in_days}, + {measure.gender, "week", measure.age_in_weeks}, + {measure.gender, "month", measure.age_in_months} + ] + |> Enum.map(fn {_, precision, _} = key -> + case :ets.lookup(__MODULE__, key) do + [{^key, %{l: l, m: m, s: s}} | _] -> + {precision, {l, m, s}} + + _ -> + nil + end + end) + |> Enum.reject(&is_nil/1) + end + + @spec z_score(number(), number(), number(), number()) :: number() + @doc """ + Check `Growth.Calc.ZScore.compute/4`. + """ + def z_score(value, l, m, s) do + ZScore.compute(value, l, m, s) + end + + @spec centile(number(), number(), number(), number()) :: number() | :na + @doc """ + Check `Growth.Calc.Centile.compute/4`. + """ + def centile(value, l, m, s) do + Centile.compute(value, l, m, s) + end end