3,480 bytes · the Python implementation · view raw
from typing import Optional
from .health_dose_weight_based_types import WeightBasedDose
def _check_range(name: str, value: int, low: int, high: int) -> None:
if isinstance(value, bool) ornot isinstance(value, int) or value < low or value > high:
raise ValueError("%s must be a whole number from %d to %d, received %r" % (name, low, high, value))
def _half_up(num: int, den: int) -> int:
"""num / den rounded half-up, for num >= 0 and den > 0."""return (2 * num + den) // (2 * den)
def _half_away_from_zero(num: int, den: int) -> int:
magnitude = _half_up(abs(num), den)
return -magnitude if num < 0else magnitude
def weight_based_dose(
weight_grams: int,
dose_per_kg_micrograms: int,
max_single_dose_micrograms: Optional[int],
strength_micrograms: int,
strength_volume_microlitres: int,
measuring_increment_microlitres: int,
) -> WeightBasedDose:
"""A weight-based dose, capped at the maximum single dose and turned into a volume the measuring device can show. Every quantity is a whole number of micrograms, microlitres or grams and every step is an exact fraction, so a 0.1 mL step never drifts. The volume is the nearest whole number of increments, a tie going down, and never one that would deliver more than the maximum single dose. """
_check_range("weightGrams", weight_grams, 200, 500000)
_check_range("dosePerKgMicrograms", dose_per_kg_micrograms, 1, 1000000)
if max_single_dose_micrograms isnotNone:
_check_range("maxSingleDoseMicrograms", max_single_dose_micrograms, 1, 100000000)
_check_range("strengthMicrograms", strength_micrograms, 1, 100000000)
_check_range("strengthVolumeMicrolitres", strength_volume_microlitres, 1, 1000000)
_check_range("measuringIncrementMicrolitres", measuring_increment_microlitres, 1, 100000)
per_kg = weight_grams * dose_per_kg_micrograms # micrograms x 1000
max_dose = max_single_dose_micrograms
capped = max_dose isnotNoneand per_kg > max_dose * 1000# The target dose as the exact fraction target_num / target_den micrograms.
target_num = max_dose if capped else per_kg
target_den = 1if capped else1000assert target_num isnotNone
strength = strength_micrograms
volume = strength_volume_microlitres
increment = measuring_increment_microlitres
# Increments needed: target x volume / (strength x increment), nearest, a tie down.
steps_num = target_num * volume
steps_den = target_den * strength * increment
steps, remainder = divmod(steps_num, steps_den)
if2 * remainder > steps_den:
steps += 1# Rounding up to the nearest step must not take the dose over the maximum.if max_dose isnotNoneand steps * increment * strength > max_dose * volume:
steps -= 1if steps == 0:
raise ValueError("dose is less than one measuring increment; use a more dilute product or a finer measure")
delivered_num = steps * increment * strength # micrograms x volumereturn WeightBasedDose(
calculated_micrograms=_half_up(per_kg, 1000),
capped_at_maximum=capped,
target_micrograms=_half_up(target_num, target_den),
volume_microlitres=steps * increment,
delivered_micrograms=_half_up(delivered_num, volume),
difference_basis_points=_half_away_from_zero(
(delivered_num * target_den - target_num * volume) * 10000, target_num * volume
),
)