defmodule Growth.Calc.Centile do
  @moduledoc """
  Calculate the value of measurement at a given z-score and fitted values of Box-Cox by age/height:

  * **power** `t:Growth.Calc.ZScore.l/0`
  * **median** `t:Growth.Calc.ZScore.m/0`
  * **coefficientof variation** `t:Growth.Calc.ZScore.s/0`

  This calculation is described in the [instructions provided by the World Health Organization](https://cdn.who.int/media/docs/default-source/child-growth/growth-reference-5-19-years/computation.pdf).

  ## Examples:

      iex> measures =
      ...>   [
      ...>     [-3.0, -1.7862, 16.9392, 0.1107],
      ...>     [2, -1.3592, 20.4951, 0.12579],
      ...>     [-1.2, -1.6318, 16.049, 0.10038]
      ...>   ]
      iex> Enum.map(measures, &apply(Growth.Calc.Centile, :compute, &1))
      [13.05127032828574, 27.884359024082663, 14.37765739914362]

  """

  alias Growth.Calc.ZScore

  @spec compute(number(), ZScore.l(), ZScore.m(), ZScore.s()) :: number() | :na
  @doc """
  Compute the measurement value for a given z-score and the Box-Cox values: power (`l`), median (`m`), and coefficient of variation (`s`).

  Returns the measurement based on the z-score when -3 <= z-score <= 3; otherwise, `:na.`

  ## Examples:

      iex> Growth.Calc.Centile.compute(0.0, -1.6318, 16.049, 0.10038)
      16.049

  """
  def compute(z_score, l, m, s) when -3 <= z_score and z_score <= 3 do
    m * :math.pow(1 + l * s * z_score, 1 / l)
  end

  def compute(_z_score, _l, _m, _s) do
    :na
  end
end
