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