Functional Weave
Code in Rust

health.dose-weight-based@1.0.0

impl/typescript.ts

3,533 bytes · the TypeScript implementation · view raw

import { type WeightBasedDose } from "./health_dose_weight_based_types.ts";

function checkRange(name: string, value: number, low: number, high: number): void {
  if (!Number.isInteger(value) || value < low || value > high) {
    throw new RangeError(`${name} must be a whole number from ${low} to ${high}, received ${value}`);
  }
}

/** num / den rounded half-up, for num >= 0 and den > 0. */
function halfUp(num: bigint, den: bigint): bigint {
  return (2n * num + den) / (2n * den);
}

/** num / den rounded half away from zero, for den > 0. */
function halfAwayFromZero(num: bigint, den: bigint): bigint {
  const magnitude = halfUp(num < 0n ? -num : num, den);
  return num < 0n ? -magnitude : magnitude;
}

/**
 * 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 to 0.30000000000000004 mL. 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.
 */
export function weightBasedDose(
  weightGrams: number,
  dosePerKgMicrograms: number,
  maxSingleDoseMicrograms: number | null,
  strengthMicrograms: number,
  strengthVolumeMicrolitres: number,
  measuringIncrementMicrolitres: number,
): WeightBasedDose {
  checkRange("weightGrams", weightGrams, 200, 500000);
  checkRange("dosePerKgMicrograms", dosePerKgMicrograms, 1, 1000000);
  if (maxSingleDoseMicrograms !== null) checkRange("maxSingleDoseMicrograms", maxSingleDoseMicrograms, 1, 100000000);
  checkRange("strengthMicrograms", strengthMicrograms, 1, 100000000);
  checkRange("strengthVolumeMicrolitres", strengthVolumeMicrolitres, 1, 1000000);
  checkRange("measuringIncrementMicrolitres", measuringIncrementMicrolitres, 1, 100000);

  const perKg = BigInt(weightGrams) * BigInt(dosePerKgMicrograms); // micrograms × 1000
  const max = maxSingleDoseMicrograms === null ? null : BigInt(maxSingleDoseMicrograms);
  const capped = max !== null && perKg > max * 1000n;
  // The target dose as the exact fraction targetNum / targetDen micrograms.
  const targetNum = capped ? (max as bigint) : perKg;
  const targetDen = capped ? 1n : 1000n;

  const strength = BigInt(strengthMicrograms);
  const volume = BigInt(strengthVolumeMicrolitres);
  const increment = BigInt(measuringIncrementMicrolitres);
  // Increments needed: target × volume / (strength × increment), nearest, a tie down.
  const stepsNum = targetNum * volume;
  const stepsDen = targetDen * strength * increment;
  let steps = stepsNum / stepsDen;
  if (2n * (stepsNum % stepsDen) > stepsDen) steps += 1n;
  // Rounding up to the nearest step must not take the dose over the maximum.
  if (max !== null && steps * increment * strength > max * volume) steps -= 1n;
  if (steps === 0n) {
    throw new RangeError("dose is less than one measuring increment; use a more dilute product or a finer measure");
  }

  const deliveredNum = steps * increment * strength; // micrograms × volume
  return {
    calculatedMicrograms: Number(halfUp(perKg, 1000n)),
    cappedAtMaximum: capped,
    targetMicrograms: Number(halfUp(targetNum, targetDen)),
    volumeMicrolitres: Number(steps * increment),
    deliveredMicrograms: Number(halfUp(deliveredNum, volume)),
    differenceBasisPoints: Number(
      halfAwayFromZero((deliveredNum * targetDen - targetNum * volume) * 10000n, targetNum * volume),
    ),
  };
}