UNPKG

diabetic-utils

Version:

Zero-bloat TypeScript utilities for diabetes data: glucose, A1C, conversions, time-in-range, and more.

1,226 lines (1,207 loc) 71.7 kB
/** * Formats a clinical A1C value as a percent string (e.g., "7.2%"). * Used for clinical reporting and display. * @param val - A1C value (percentage) * @returns A1C as string with percent sign */ declare function formatA1C(val: number): string; /** * Validates a clinical A1C value (percentage). * Ensures value is within physiologically plausible range for clinical analytics. * @param value - Candidate A1C value * @returns True if value is a valid A1C percentage */ declare function isValidA1C(value: unknown): boolean; /** * Returns the clinical category for an A1C value (normal, prediabetes, diabetes, or invalid). * Uses ADA thresholds by default, but allows custom cutoffs for research or population-specific use. * @param a1c - A1C value (percentage) * @returns 'normal' | 'prediabetes' | 'diabetes' | 'invalid' */ /** * Returns the clinical category for an A1C value (normal, prediabetes, diabetes, or invalid). * Uses ADA thresholds by default, but allows custom cutoffs for research or population-specific use. * @param a1c - A1C value (percentage) * @param thresholds - Optional custom thresholds: { normalMax?: number; prediabetesMax?: number } * @returns 'normal' | 'prediabetes' | 'diabetes' | 'invalid' */ declare function getA1CCategory(a1c: number, thresholds?: { normalMax?: number; prediabetesMax?: number; }): 'normal' | 'prediabetes' | 'diabetes' | 'invalid'; /** * Checks if an A1C value is within a target range. * @param a1c - A1C value * @param target - [min, max] range (default: [6.5, 7.0]) * @param thresholds - Optional custom thresholds: { min?: number; max?: number } * @returns True if in target range */ declare function isA1CInTarget(a1c: number, target?: [number, number], thresholds?: { min?: number; max?: number; }): boolean; /** * Calculates the change (delta) between two A1C values. * @param current - Current A1C * @param previous - Previous A1C * @returns Delta (current - previous) * @throws If either value is invalid */ declare function a1cDelta(current: number, previous: number): number; /** * Determines the trend of A1C values over time. * @param readings - Array of A1C values (chronological order) * @returns 'increasing' | 'decreasing' | 'stable' | 'insufficient data' */ declare function a1cTrend(readings: number[]): 'increasing' | 'decreasing' | 'stable' | 'insufficient data'; /** * Calculates HOMA-IR (Homeostatic Model Assessment for Insulin Resistance) from fasting glucose and insulin. * * Formula: HOMA-IR = (fasting glucose [mg/dL] × fasting insulin [µIU/mL]) / 405 * * Used for estimating insulin resistance. Not a diagnostic tool. * * @param glucose - Fasting glucose value in mg/dL. Must be a positive finite number. * @param insulin - Fasting insulin value in µIU/mL. Must be a positive finite number. * @returns Object with numeric HOMA-IR value and interpretation label. * @throws {Error} If glucose or insulin are invalid (non-finite, zero, or negative). * @see https://pubmed.ncbi.nlm.nih.gov/3899825/ (Original HOMA-IR publication) * @see https://diabetesjournals.org/care/article/26/1/118/22567/Prevalence-and-Concomitants-of-Glucose-Intolerance (ADA: Glucose Intolerance and HOMA-IR context) */ declare function calculateHOMAIR(glucose: number, insulin: number): { value: number; interpretation: string; }; /** * Checks consistency among A1C, fasting glucose, and fasting insulin markers. * * Returns: * - Estimated average glucose (mg/dL), calculated per CDC formula * - HOMA-IR result (value and interpretation) * - Flags for potential inconsistencies * - Informational note and disclaimer * * Used for high-level insight and trend alignment, not for diagnosis. * * @param a1c - A1C value (percentage). Must be a positive finite number. * @param glucose - Fasting glucose value in mg/dL. Must be a positive finite number. * @param insulin - Fasting insulin value in µIU/mL. Must be a positive finite number. * @returns Object with estimated average glucose (mg/dL), HOMA-IR result object, flags array, recommendation string, and disclaimer. * @throws {Error} If any input value is invalid (non-finite, zero, or negative). * @see https://www.cdc.gov/diabetes/diabetes-testing/prediabetes-a1c-test.html (CDC: eAG formula) */ declare function checkGlycemicAlignment(a1c: number, glucose: number, insulin: number): { estimatedAverageGlucose: number; homaIR: { value: number; interpretation: string; }; flags: string[]; recommendation: string; disclaimer: string; }; /** * Denominator constant for HOMA-IR calculation. * HOMA-IR = (glucose [mg/dL] × insulin [µIU/mL]) / HOMA_IR_DENOMINATOR * @see https://www.ncbi.nlm.nih.gov/books/NBK279396/ */ declare const HOMA_IR_DENOMINATOR = 405; /** * Interpretation cutoffs for HOMA-IR (insulin resistance assessment). * These are general categories, not diagnostic. */ declare const HOMA_IR_CUTOFFS: { readonly VERY_SENSITIVE: 1; readonly NORMAL: 2; readonly EARLY_RESISTANCE: 2.9; }; /** * Hypoglycemia threshold (mg/dL). * Used for detecting low glucose events. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare const HYPO_THRESHOLD_MGDL = 70; /** * Hyperglycemia threshold (mg/dL). * Used for detecting high glucose events. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare const HYPER_THRESHOLD_MGDL = 180; /** * Hypoglycemia threshold (mmol/L). * Used for low glucose detection in international/metric contexts. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare const HYPO_THRESHOLD_MMOLL = 3.9; /** * Hyperglycemia threshold (mmol/L). * Used for high glucose detection in international/metric contexts. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare const HYPER_THRESHOLD_MMOLL = 10; /** * Multiplier for converting A1C to estimated average glucose (eAG). * Used in eAG calculation per CDC/ADA guidelines. * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html */ declare const A1C_TO_EAG_MULTIPLIER = 28.7; /** * Constant for converting A1C to estimated average glucose (eAG). * Used in eAG calculation per CDC/ADA guidelines. * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html */ declare const A1C_TO_EAG_CONSTANT = 46.7; /** * GMI (Glucose Management Indicator) calculation coefficients. * Used for estimating GMI from average glucose values. * @see https://diatribe.org/glucose-management-indicator-gmi */ declare const GMI_COEFFICIENTS: { /** Slope for mmol/L to GMI conversion */ readonly MMOL_L_SLOPE: 1.57; /** Intercept for mmol/L to GMI conversion */ readonly MMOL_L_INTERCEPT: 3.5; /** Slope for mg/dL to GMI conversion */ readonly MG_DL_SLOPE: 0.03; /** Intercept for mg/dL to GMI conversion */ readonly MG_DL_INTERCEPT: 2.4; /** Slope for A1C to GMI conversion */ readonly A1C_SLOPE: 0.02392; /** Intercept for A1C to GMI conversion */ readonly A1C_INTERCEPT: 3.31; }; /** * Conversion factor between mg/dL and mmol/L. * Used for unit conversion. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ declare const MGDL_MMOLL_CONVERSION = 18.0182; /** * String literal for mg/dL glucose unit. * Used for data interoperability and formatting. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ declare const MG_DL: "mg/dL"; /** * String literal for mmol/L unit. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ declare const MMOL_L: "mmol/L"; /** * Color codes for glucose zones. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare const GLUCOSE_COLOR_LOW = "#D32F2F"; declare const GLUCOSE_COLOR_NORMAL = "#388E3C"; declare const GLUCOSE_COLOR_NORMAL_UP = "#4CAF50"; declare const GLUCOSE_COLOR_NORMAL_DOWN = "#2E7D32"; declare const GLUCOSE_COLOR_ELEVATED = "#FBC02D"; declare const GLUCOSE_COLOR_HIGH = "#F57C00"; /** * Glucose zone color mapping for different statuses and trends. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare const GLUCOSE_ZONE_COLORS: { readonly LOW: "#D32F2F"; readonly NORMAL: "#388E3C"; readonly ELEVATED: "#FBC02D"; readonly HIGH: "#F57C00"; readonly NORMAL_UP: "#4CAF50"; readonly NORMAL_DOWN: "#2E7D32"; }; /** * Unicode arrows for glucose trend indication. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare const TREND_ARROWS: { readonly STEADY: "→"; readonly RISING: "↗"; readonly FALLING: "↘"; readonly RAPIDRISE: "↑"; readonly RAPIDFALL: "↓"; }; /** * Level 2 hypoglycemia threshold (mg/dL). * Readings below this value indicate significant hypoglycemia. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_VERY_LOW_THRESHOLD_MGDL = 54; /** * Level 1 hypoglycemia threshold (mg/dL). * Target range begins at this value. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_LOW_THRESHOLD_MGDL = 70; /** * Level 1 hyperglycemia threshold (mg/dL). * Target range ends at this value. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_HIGH_THRESHOLD_MGDL = 180; /** * Level 2 hyperglycemia threshold (mg/dL). * Readings above this value indicate significant hyperglycemia. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_VERY_HIGH_THRESHOLD_MGDL = 250; /** * Level 2 hypoglycemia threshold (mmol/L). * Readings below this value indicate significant hypoglycemia. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_VERY_LOW_THRESHOLD_MMOLL = 3; /** * Level 1 hypoglycemia threshold (mmol/L). * Target range begins at this value. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_LOW_THRESHOLD_MMOLL = 3.9; /** * Level 1 hyperglycemia threshold (mmol/L). * Target range ends at this value. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_HIGH_THRESHOLD_MMOLL = 10; /** * Level 2 hyperglycemia threshold (mmol/L). * Readings above this value indicate significant hyperglycemia. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_VERY_HIGH_THRESHOLD_MMOLL = 13.9; /** * Lower bound of target glucose range during pregnancy (mg/dL). * Applies to Type 1, Type 2, and gestational diabetes. * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)} */ declare const PREGNANCY_TARGET_LOW_MGDL = 63; /** * Upper bound of target glucose range during pregnancy (mg/dL). * Applies to Type 1, Type 2, and gestational diabetes. * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)} */ declare const PREGNANCY_TARGET_HIGH_MGDL = 140; /** * Lower bound of target glucose range during pregnancy (mmol/L). * Applies to Type 1, Type 2, and gestational diabetes. * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)} */ declare const PREGNANCY_TARGET_LOW_MMOLL = 3.5; /** * Upper bound of target glucose range during pregnancy (mmol/L). * Applies to Type 1, Type 2, and gestational diabetes. * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)} */ declare const PREGNANCY_TARGET_HIGH_MMOLL = 7.8; /** * Target percentage for time-in-range (70-180 mg/dL). * Clinical goal: ≥70% of readings in target range. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_GOAL_STANDARD = 70; /** * Maximum acceptable percentage for Level 1 hypoglycemia (54-69 mg/dL). * Clinical goal: <4% of readings in this range. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TBR_LEVEL1_GOAL = 4; /** * Maximum acceptable percentage for Level 2 hypoglycemia (<54 mg/dL). * Clinical goal: <1% of readings in this range. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TBR_LEVEL2_GOAL = 1; /** * Maximum acceptable percentage for Level 1 hyperglycemia (181-250 mg/dL). * Clinical goal: <25% of readings in this range. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TAR_LEVEL1_GOAL = 25; /** * Maximum acceptable percentage for Level 2 hyperglycemia (>250 mg/dL). * Clinical goal: <5% of readings in this range. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TAR_LEVEL2_GOAL = 5; /** * Target percentage for time-in-range for older/high-risk adults. * More lenient goal: ≥50% of readings in target range. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TIR_GOAL_OLDER_ADULTS = 50; /** * Maximum acceptable percentage for Level 1 hypoglycemia for older/high-risk adults. * More stringent goal: <1% of readings in this range. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TBR_LEVEL1_GOAL_OLDER_ADULTS = 1; /** * Maximum acceptable percentage for Level 2 hypoglycemia for older/high-risk adults. * More stringent goal: <0.5% of readings in this range. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ declare const TBR_LEVEL2_GOAL_OLDER_ADULTS = 0.5; /** * Supported clinical glucose units. * Used for all clinical analytics and conversions. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ type GlucoseUnit = typeof MG_DL | typeof MMOL_L; /** * List of allowed clinical glucose units. * Used for input validation and unit conversion. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ declare const AllowedGlucoseUnits: GlucoseUnit[]; /** * Single clinical glucose reading. * Includes value, unit, and ISO 8601 timestamp for clinical analytics. * @see https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7445493/ */ interface GlucoseReading { readonly value: number; readonly unit: GlucoseUnit; readonly timestamp: string; } /** * Result object for clinical Time-in-Range (TIR) analytics. * Percentages for in-range, below-range, and above-range readings. * @see https://care.diabetesjournals.org/content/42/8/1593 */ interface TIRResult { inRange: number; belowRange: number; aboveRange: number; } /** * Options for clinical GMI (Glucose Management Indicator) estimation. * Used to standardize GMI calculation input. * @see https://diatribe.org/glucose-management-indicator-gmi */ interface EstimateGMIOptions { value: number; unit: GlucoseUnit; } /** * Result of glucose unit conversion. * Provides converted value and new unit for clinical interoperability. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ interface ConversionResult { /** Converted glucose value */ readonly value: number; /** New glucose unit after conversion */ readonly unit: GlucoseUnit; } /** * Options for clinical Time-in-Range (TIR) analytics. */ interface TIROptions { readings: GlucoseReading[]; unit: GlucoseUnit; range: [number, number]; } /** * Single clinical A1C reading (value and ISO date). */ interface A1CReading { value: number; date: string; } /** * Options for clinical glucose statistics analytics. * Controls which metrics are calculated and reported. */ interface GlucoseStatsOptions { readings: GlucoseReading[]; unit: GlucoseUnit; range: [number, number]; gmi?: boolean; a1c?: boolean; tir?: boolean; tirRange?: [number, number]; tirPercent?: boolean; tirPercentBelow?: boolean; tirPercentAbove?: boolean; tirPercentInRange?: boolean; tirPercentBelowRounded?: boolean; tirPercentAboveRounded?: boolean; tirPercentInRangeRounded?: boolean; } /** * Population type for TIR target assessment. * Different populations have different clinical goals. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ type TIRPopulation = 'standard' | 'older-adults' | 'high-risk'; /** * Overall glycemic control assessment based on TIR metrics. */ type TIRAssessment = 'excellent' | 'good' | 'needs improvement' | 'concerning'; /** * Detailed metrics for a single glucose range. * Provides percentage, duration, count, and average value for clinical analysis. */ interface RangeMetrics { /** Percentage of readings in this range (0-100) */ readonly percentage: number; /** Total duration in this range (minutes) */ readonly duration: number; /** Count of readings in this range */ readonly readingCount: number; /** Average glucose value in this range (mg/dL or mmol/L) */ readonly averageValue: number | null; } /** * Assessment of whether TIR metrics meet clinical targets. * Based on International Consensus on Time in Range (2019). * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ interface TargetAssessment { /** TIR ≥70% for standard population, ≥50% for older adults */ readonly tirMeetsGoal: boolean; /** TBR Level 1 <4% for standard, <1% for older adults */ readonly tbrLevel1Safe: boolean; /** TBR Level 2 <1% for standard, <0.5% for older adults */ readonly tbrLevel2Safe: boolean; /** TAR Level 1 <25% (all populations) */ readonly tarLevel1Acceptable: boolean; /** TAR Level 2 <5% (all populations) */ readonly tarLevel2Acceptable: boolean; /** Overall assessment of glycemic control */ readonly overallAssessment: TIRAssessment; /** Clinical recommendations based on metrics */ readonly recommendations: readonly string[]; } /** * Summary statistics for TIR calculation. */ interface TIRSummary { /** Total number of glucose readings analyzed */ readonly totalReadings: number; /** Total duration of data analyzed (minutes) */ readonly totalDuration: number; /** Data quality assessment */ readonly dataQuality: 'excellent' | 'good' | 'fair' | 'poor'; } /** * Complete Enhanced Time-in-Range result. * Provides detailed breakdown across five clinical glucose ranges per International Consensus 2019. * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} */ interface EnhancedTIRResult { /** Very Low: <54 mg/dL (3.0 mmol/L) - Level 2 Hypoglycemia */ readonly veryLow: RangeMetrics; /** Low: 54-69 mg/dL (3.0-3.8 mmol/L) - Level 1 Hypoglycemia */ readonly low: RangeMetrics; /** In Range: 70-180 mg/dL (3.9-10.0 mmol/L) - Target Range */ readonly inRange: RangeMetrics; /** High: 181-250 mg/dL (10.1-13.9 mmol/L) - Level 1 Hyperglycemia */ readonly high: RangeMetrics; /** Very High: >250 mg/dL (>13.9 mmol/L) - Level 2 Hyperglycemia */ readonly veryHigh: RangeMetrics; /** Assessment against clinical targets */ readonly meetsTargets: TargetAssessment; /** Summary statistics */ readonly summary: TIRSummary; } /** * Options for Enhanced TIR calculation. * Allows customization for different clinical populations and use cases. */ interface EnhancedTIROptions { /** Population type for target assessment (default: 'standard') */ readonly population?: TIRPopulation; /** Glucose unit for input validation (default: 'mg/dL') */ readonly unit?: GlucoseUnit; /** Override for the very low threshold (<54 mg/dL). Value must be provided in mg/dL. */ readonly veryLowThreshold?: number; /** Override for the low threshold (54-69 mg/dL). Value must be provided in mg/dL. */ readonly lowThreshold?: number; /** Override for the high threshold (181-250 mg/dL). Value must be provided in mg/dL. */ readonly highThreshold?: number; /** Override for the very high threshold (>250 mg/dL). Value must be provided in mg/dL. */ readonly veryHighThreshold?: number; } /** * Pregnancy-specific Time-in-Range result. * Uses tighter target range per ADA 2024 guidelines for pregnancy. * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)} */ interface PregnancyTIRResult { /** In Range: 63-140 mg/dL (3.5-7.8 mmol/L) */ readonly inRange: RangeMetrics; /** Below Range: <63 mg/dL (3.5 mmol/L) */ readonly belowRange: RangeMetrics; /** Above Range: >140 mg/dL (7.8 mmol/L) */ readonly aboveRange: RangeMetrics; /** Whether metrics meet pregnancy-specific targets (TIR >70%, TBR <4%, TAR <25%) */ readonly meetsPregnancyTargets: boolean; /** Clinical recommendations */ readonly recommendations: readonly string[]; /** Summary statistics */ readonly summary: TIRSummary; } /** * Options for Pregnancy TIR calculation. */ interface PregnancyTIROptions { /** Glucose unit for input validation (default: 'mg/dL') */ readonly unit?: GlucoseUnit; } /** * Converts clinical average glucose (mg/dL) to estimated A1C (percentage). * Used for clinical analytics and patient reporting. * @param avgMgDl - Average glucose in mg/dL * @returns Estimated A1C value (percentage) * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html */ declare function estimateA1CFromAvgGlucose(avgMgDl: number): number; /** * Converts clinical A1C value (percentage) to estimated average glucose (mg/dL). * Used for clinical analytics and patient reporting. * @param a1c - A1C value (percentage) * @returns Estimated average glucose in mg/dL * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html */ declare function estimateAvgGlucoseFromA1C(a1c: number): number; /** * Estimates eAG (estimated average glucose, mg/dL) from clinical A1C value. * Throws if input is negative. Used for clinical and research reporting. * @param a1c - A1C value (percentage) * @returns Estimated average glucose (mg/dL) * @throws {Error} If a1c is negative * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html */ declare function estimateEAG(a1c: number): number; /** * Estimates A1C from average glucose. * @param avgGlucose - Average glucose value * @param unit - Glucose unit (mg/dL or mmol/L) * @returns Estimated A1C * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html */ declare function estimateA1CFromAverage(avgGlucose: number, unit?: GlucoseUnit): number; /** * Converts A1C to Glucose Management Indicator (GMI). * @param a1c - A1C value * @returns GMI value * @see https://diatribe.org/glucose-management-indicator-gmi */ declare function a1cToGMI(a1c: number): number; /** * Estimate Glucose Management Indicator (GMI) from average glucose. * @param valueOrOptions - Glucose value, string, or options object * @param unit - Glucose unit (if value is a number) * @returns GMI value * @throws {Error} If unit is required but not provided when input is a number. * @throws {Error} If the glucose unit is unsupported. * @throws {Error} If the glucose value is not a positive number. * @see https://diatribe.org/glucose-management-indicator-gmi */ declare function estimateGMI(valueOrOptions: number | string | EstimateGMIOptions, unit?: GlucoseUnit): number; /** * Converts clinical glucose value from mg/dL to mmol/L. * Used for international interoperability and reporting. * @param val - Glucose value in mg/dL * @returns Value in mmol/L * @throws {Error} If val is not a finite number or is negative/zero * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html * * @example * ```typescript * const result = mgDlToMmolL(180) * console.log(result) // 10.0 * ``` */ declare function mgDlToMmolL(val: number): number; /** * Converts clinical glucose value from mmol/L to mg/dL. * Used for international interoperability and reporting. * @param val - Glucose value in mmol/L * @returns Value in mg/dL * @throws {Error} If val is not a finite number or is negative/zero * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html * * @example * ```typescript * const result = mmolLToMgDl(5.5) * console.log(result) // 99 * ``` */ declare function mmolLToMgDl(val: number): number; /** * Converts clinical glucose value between mg/dL and mmol/L. * Used for clinical interoperability and analytics. * @param value - Glucose value (number) * @param unit - Current glucose unit ('mg/dL' or 'mmol/L') * @returns Object with converted value and new unit * @throws {Error} If value is not a finite number or is negative/zero * @throws {Error} If unit is not a supported glucose unit * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ declare function convertGlucoseUnit({ value, unit, }: { value: number; unit: GlucoseUnit; }): ConversionResult; /** * Formats a clinical glucose value with unit and optional rounding. * Used for clinical reporting, charting, and data export. * @param val - Glucose value (number) * @param unit - Glucose unit ('mg/dL' or 'mmol/L') * @param options - Formatting options: { digits?: number; suffix?: boolean } (default: { digits: 0, suffix: true }) * @returns Formatted glucose string (e.g., '5.5 mmol/L', '120 mg/dL') * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ declare function formatGlucose(val: number, unit: GlucoseUnit, options?: { digits?: number; suffix?: boolean; }): string; /** * Formats a value as a clinical percentage string (e.g., '85.0%'). * Used for reporting TIR, CV, and other clinical metrics. * @param val - Value to format (fraction or percent) * @param digits - Number of decimal places (default: 1) * @returns Formatted percentage string (e.g., '85.0%') */ declare function formatPercentage(val: number, digits?: number): string; /** * Formats a UTC ISO 8601 timestamp to a local-readable date/time string. * Used for clinical charting, logs, and reports. Supports optional IANA time zone. * @param iso - ISO 8601 timestamp string (e.g., '2024-03-20T10:00:00Z') * @param timeZone - Optional IANA time zone (e.g., 'America/New_York') * @returns Localized date/time string (e.g., 'Mar 20, 2024, 06:00 AM') * @throws {RangeError} If the ISO string is invalid or cannot be parsed */ declare function formatDate(iso: string, timeZone?: string): string; /** * Checks if a glucose value is below the hypoglycemia threshold for the given unit. * Used for detecting low glucose events. * @param val - Glucose value (number) * @param unit - Glucose unit ('mg/dL' or 'mmol/L'), default: 'mg/dL' * @param thresholds - Optional custom thresholds ({ mgdl?: number; mmoll?: number }) * @returns True if value is below the hypoglycemia threshold * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare function isHypo(val: number, unit?: GlucoseUnit, thresholds?: { mgdl?: number; mmoll?: number; }): boolean; /** * Checks if a glucose value is above the hyperglycemia threshold for the given unit. * Used for detecting high glucose events. * @param val - Glucose value (number) * @param unit - Glucose unit ('mg/dL' or 'mmol/L'), default: 'mg/dL' * @param thresholds - Optional custom thresholds ({ mgdl?: number; mmoll?: number }) * @returns True if value is above the hyperglycemia threshold * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare function isHyper(val: number, unit?: GlucoseUnit, thresholds?: { mgdl?: number; mmoll?: number; }): boolean; /** * Returns a glucose status label ('low', 'normal', or 'high') based on thresholds for the given unit. * Used for charting, alerts, and reporting. * @param val - Glucose value (number) * @param unit - Glucose unit ('mg/dL' or 'mmol/L'), default: 'mg/dL' * @param thresholds - Optional custom thresholds for hypo/hyper ({ hypo?: { mgdl?: number; mmoll?: number }, hyper?: { mgdl?: number; mmoll?: number } }) * @returns 'low', 'normal', or 'high' based on configured thresholds * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html */ declare function getGlucoseLabel(val: number, unit?: GlucoseUnit, thresholds?: { hypo?: { mgdl?: number; mmoll?: number; }; hyper?: { mgdl?: number; mmoll?: number; }; }): 'low' | 'normal' | 'high'; /** * Parses a glucose string (e.g., "100 mg/dL", "5.5 mmol/L") into value and unit. * Used for robust input validation and data ingestion. * @param input - String in the format "value unit" (e.g., "100 mg/dL") * @returns Object with numeric value and validated unit * @throws {Error} If input string is invalid or not in expected format * @example * parseGlucoseString("100 mg/dL") // { value: 100, unit: "mg/dL" } * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ declare function parseGlucoseString(input: string): { value: number; unit: GlucoseUnit; }; /** * Validates a glucose value and unit. * Ensures value is a positive finite number and unit is supported. * @param value - Glucose value to validate * @param unit - Glucose unit to validate * @returns True if value and unit are valid */ declare function isValidGlucoseValue(value: unknown, unit: unknown): boolean; /** * Clinical type guard for EstimateGMIOptions. * Validates that the input matches the required shape for GMI estimation options (numeric value, string unit). * Useful for ensuring safe handling of clinical glucose data and interoperability with analytics functions. * @param input - Candidate value to validate. * @returns True if input is a valid EstimateGMIOptions object. */ declare function isEstimateGMIOptions(input: unknown): input is EstimateGMIOptions; /** * Validates a clinical glucose string (e.g., "100 mg/dL", "5.5 mmol/L"). * Ensures the string is in a recognized clinical format for glucose values, supporting safe parsing and conversion. * @param input - Value to check as a clinical glucose string. * @returns True if input is a valid glucose string for clinical use. * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html */ declare function isValidGlucoseString(input: unknown): input is string; /** * Validates a clinical fasting insulin value (µIU/mL). * Ensures value is a positive finite number and within plausible physiological range. * @param value - Candidate insulin value * @returns True if value is a valid fasting insulin (µIU/mL) * @see https://www.ncbi.nlm.nih.gov/books/NBK279396/ (normal fasting insulin: ~2-25 µIU/mL, but allow wider plausible range for outliers) */ declare function isValidInsulin(value: unknown): value is number; /** * Calculates clinical Time in Range (TIR) metrics for glucose readings. * Returns the percentage of readings in, below, and above the specified clinical target range. * @param readings - Array of glucose readings to analyze * @param target - Object specifying the target range ({ min, max }) * @returns Object with in-range, below-range, and above-range percentages * @see https://care.diabetesjournals.org/content/42/8/1593 */ declare function calculateTIR(readings: GlucoseReading[], target: { min: number; max: number; }): TIRResult; /** * Generates a clinical summary string from a TIRResult object. * Used for reporting and visualization of TIR analytics. * @param result - TIR result breakdown to summarize * @returns String summarizing in-range, below-range, and above-range percentages (e.g., 'In Range: 70%, Below: 10%, Above: 20%') */ declare function getTIRSummary(result: TIRResult): string; /** * Groups glucose readings by date (YYYY-MM-DD). * @param readings - Array of glucose readings to group. * @returns An object mapping each date string to an array of readings for that day. */ declare function groupByDay(readings: GlucoseReading[]): Record<string, GlucoseReading[]>; /** * Calculates the percentage of glucose readings within a specified numeric range. * Used for clinical TIR analytics and custom range assessments. * @param readings - Array of glucose values (numbers) to analyze * @param lower - Lower bound of the target range (inclusive) * @param upper - Upper bound of the target range (inclusive) * @returns Percentage of readings within the specified range (0-100) */ declare function calculateTimeInRange(readings: number[], lower: number, upper: number): number; /** * Calculates Mean Amplitude of Glycemic Excursions (MAGE). * Implements Service FJ et al. (1970) methodology. * @param readings - Array of glucose values (mg/dL or mmol/L) * @param options - Configuration options for MAGE calculation * @returns MAGE value, or NaN if insufficient data or no valid excursions * @see https://pubmed.ncbi.nlm.nih.gov/5469118/ (Service FJ, et al. 1970) * @see https://journals.sagepub.com/doi/10.1177/19322968211061165 (Fernandes NJ, et al. 2022) * @see https://care.diabetesjournals.org/content/42/8/1593 (ADA 2019) * @example * // Basic usage * glucoseMAGE([100, 120, 80, 160, 90, 140, 70, 180]) * // Advanced usage * glucoseMAGE(readings, { shortWindow: 5, longWindow: 32, direction: 'auto' }) * @remarks * - Minimum 24 data points recommended (1 day of hourly readings) * - Best suited for continuous glucose monitoring (CGM) data * - Not recommended for sparse or irregular measurements * - Uses dual moving averages, three-point excursion definition, and prevents double-counting. */ declare function glucoseMAGE$1(readings: number[], options?: MAGEOptions): number; /** * Configuration options for MAGE calculation. * @property shortWindow - Short moving average window (default: 5) * @property longWindow - Long moving average window (default: 32) * @property direction - Excursion direction: 'auto', 'ascending', or 'descending' */ interface MAGEOptions { /** Short moving average window size (default: 5, recommended range: 1-7) */ shortWindow?: number; /** Long moving average window size (default: 32, recommended range: 16-38) */ longWindow?: number; /** * Direction of excursions to count: * - 'auto': Use first excursion type that exceeds SD threshold (Service 1970 default) * - 'ascending': Count only ascending excursions (MAGE+) * - 'descending': Count only descending excursions (MAGE-) */ direction?: 'auto' | 'ascending' | 'descending'; } /** * Calculates the unbiased sample standard deviation (SD) of glucose values. * Uses n-1 in the denominator (sample SD), as recommended in research guidelines. * * @param readings Array of glucose values (numbers) * @returns Standard deviation, or NaN if fewer than 2 values * @throws {TypeError} If readings is not an array * @see {@link https://care.diabetesjournals.org/content/42/8/1593 ADA 2019: Glycemic Targets} * @see {@link https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7445493/ ISPAD 2019} * @example * ```ts * glucoseStandardDeviation([100, 120, 140]) // 20 * glucoseStandardDeviation([]) // NaN * ``` * @remarks * - If readings contains <2 values, returns NaN (not enough data for SD). * - Handles NaN/Infinity values by propagating them in the result. */ declare function glucoseStandardDeviation(readings: number[]): number; /** * Calculates the coefficient of variation (CV) for glucose values. * CV = (SD / mean) × 100. Used to assess glycemic variability. * * @param readings Array of glucose values (numbers) * @returns Coefficient of variation as a percentage, or NaN if <2 values or mean is 0 * @throws {TypeError} If readings is not an array * @see {@link https://care.diabetesjournals.org/content/42/8/1593 ADA 2019: Glycemic Targets} * @example * ```ts * glucoseCoefficientOfVariation([100, 120, 140]) // 18.26 * glucoseCoefficientOfVariation([100]) // NaN * glucoseCoefficientOfVariation([]) // NaN * ``` * @remarks * - If readings contains <2 values or mean is 0, returns NaN. * - Handles NaN/Infinity values by propagating them in the result. */ declare function glucoseCoefficientOfVariation(readings: number[]): number; /** * Calculates specified percentiles from an array of glucose values using the nearest-rank method. * Used for glucose variability assessment. * @param readings - Array of glucose values (numbers) * @param percentiles - Array of percentiles to calculate (e.g., [10, 25, 50, 75, 90]) * @returns Object mapping percentile to value, or {} if input is empty * @throws {TypeError} If readings or percentiles is not an array * @see https://en.wikipedia.org/wiki/Percentile * @see https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7445493/ (ISPAD 2019) * @example * glucosePercentiles([100, 120, 140, 160, 180], [10, 50, 90]) // { 10: 100, 50: 140, 90: 180 } * glucosePercentiles([], [10, 50, 90]) // {} * @remarks * - Returns the value at the nearest-rank for each percentile. * - If readings is empty, returns an empty object. * - Percentiles outside [0, 100] are ignored. */ declare function glucosePercentiles(readings: number[], percentiles: number[]): Record<number, number>; /** * Calculates Mean Amplitude of Glycemic Excursions (MAGE) for glucose values. * Implements Service FJ et al. (1970) methodology. * @param readings - Array of glucose values (mg/dL or mmol/L) * @param options - Optional configuration for MAGE calculation * @returns MAGE value, or NaN if insufficient data or no valid excursions * @see https://pubmed.ncbi.nlm.nih.gov/5469118/ (Service FJ, et al. 1970) * @see https://journals.sagepub.com/doi/10.1177/19322968211061165 (Fernandes NJ, et al. 2022) * @see https://care.diabetesjournals.org/content/42/8/1593 (ADA 2019) * @example * glucoseMAGE([100, 120, 80, 160, 90, 140, 70, 180]) * glucoseMAGE(readings, { direction: 'ascending', shortWindow: 5, longWindow: 32 }) * @remarks * - Minimum 24 data points recommended (1 day of hourly readings) * - Best suited for continuous glucose monitoring (CGM) data * - Not recommended for sparse or irregular measurements * - Uses dual moving averages, three-point excursion definition, and prevents double-counting. */ declare function glucoseMAGE(readings: number[], options?: MAGEOptions): number; /** * Calculates Enhanced Time-in-Range metrics per International Consensus 2019. * * Provides detailed breakdown of glucose readings across five clinical ranges: * - Very Low (<54 mg/dL / 3.0 mmol/L): Level 2 Hypoglycemia * - Low (54-69 mg/dL / 3.0-3.8 mmol/L): Level 1 Hypoglycemia * - In Range (70-180 mg/dL / 3.9-10.0 mmol/L): Target Range * - High (181-250 mg/dL / 10.1-13.9 mmol/L): Level 1 Hyperglycemia * - Very High (>250 mg/dL / >13.9 mmol/L): Level 2 Hyperglycemia * * @param readings - Array of glucose readings with timestamp, value, and unit * @param options - Optional configuration for thresholds and population type * @returns Enhanced TIR result with detailed metrics and target assessment * * @example * ```typescript * const readings: GlucoseReading[] = [ * { value: 120, unit: 'mg/dL', timestamp: '2024-01-01T08:00:00Z' }, * { value: 95, unit: 'mg/dL', timestamp: '2024-01-01T08:05:00Z' }, * // ... more readings * ]; * const result = calculateEnhancedTIR(readings); * console.log(`TIR: ${result.inRange.percentage}%`); * console.log(`Meets targets: ${result.meetsTargets.tirMeetsGoal}`); * ``` * * @throws {Error} If readings array is empty * @throws {Error} If readings contain invalid glucose values * * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)} * * @remarks * - Requires minimum 24 hours of data for meaningful results * - Consensus targets: TIR ≥70%, TBR Level 1 <4%, TBR Level 2 <1%, TAR Level 1 <25%, TAR Level 2 <5% * - Verify data quality and sensor accuracy before drawing conclusions * - Readings are assumed to be evenly distributed for duration calculations * * @category Enhanced TIR * @public */ declare function calculateEnhancedTIR(readings: GlucoseReading[], options?: EnhancedTIROptions): EnhancedTIRResult; /** * Calculates Pregnancy-specific Time-in-Range metrics per ADA 2024 guidelines. * * Uses tighter target range for pregnancy: 63-140 mg/dL (3.5-7.8 mmol/L). * Consensus targets: TIR >70%, TBR <4% (Level 1) and <1% (Level 2), TAR <25%. * * @param readings - Array of glucose readings with timestamp, value, and unit * @param options - Optional configuration for glucose unit * @returns Pregnancy TIR result with target assessment and recommendations * * @example * ```typescript * const readings: GlucoseReading[] = [ * { value: 100, unit: 'mg/dL', timestamp: '2024-01-01T08:00:00Z' }, * // ... more readings * ]; * const result = calculatePregnancyTIR(readings); * console.log(`TIR: ${result.inRange.percentage}%`); * console.log(`Meets pregnancy targets: ${result.meetsPregnancyTargets}`); * ``` * * @throws {Error} If readings array is empty * @throws {Error} If readings contain invalid glucose values * * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)} * * @remarks * - Target range: 63-140 mg/dL (3.5-7.8 mmol/L) * - Tighter targets per published guidelines * - This is informational only and does not constitute medical advice * - Applies to Type 1, Type 2, and gestational diabetes during pregnancy * * @category Pregnancy TIR * @public */ declare function calculatePregnancyTIR(readings: GlucoseReading[], options?: PregnancyTIROptions): PregnancyTIRResult; /** * @file src/metrics/bgi.ts * * Low Blood Glucose Index (LBGI) and High Blood Glucose Index (HBGI). * * These risk indices quantify the risk of hypo- and hyperglycemia from a * series of glucose readings. They are asymmetric transforms that weight * low values more heavily (LBGI) or high values more heavily (HBGI). * * Formula (Kovatchev et al. 2006): * f(G) = 1.509 * (ln(G)^1.084 - 5.381) * rl(G) = 10 * f(G)^2 if f(G) < 0, else 0 (low risk) * rh(G) = 10 * f(G)^2 if f(G) > 0, else 0 (high risk) * LBGI = mean(rl) * HBGI = mean(rh) * * Input values must be in mg/dL. Values <= 0 are skipped. * * @see https://doi.org/10.2337/dc06-1085 Kovatchev et al. (2006) */ /** * Computes the blood glucose symmetry function f(G). * Used by LBGI, HBGI, and ADRR calculations. * @internal */ declare function fbg(glucoseMgDl: number): number; /** * Calculates the Low Blood Glucose Index (LBGI). * * Quantifies the risk and extent of hypoglycemia from a glucose trace. * Higher values indicate greater hypoglycemia risk. * * Risk categories (Kovatchev 2006): * - < 1.1: low risk * - 1.1 - 2.5: moderate risk * - > 2.5: high risk * * @param readings - Array of glucose values in mg/dL * @returns LBGI value, or NaN if no valid readings * @see https://doi.org/10.2337/dc06-1085 */ declare function glucoseLBGI(readings: number[]): number; /** * Calculates the High Blood Glucose Index (HBGI). * * Quantifies the risk and extent of hyperglycemia from a glucose trace. * Higher values indicate greater hyperglycemia risk. * * Risk categories (Kovatchev 2006): * - < 4.5: low risk * - 4.5 - 9.0: moderate risk * - > 9.0: high risk * * @param readings - Array of glucose values in mg/dL * @returns HBGI value, or NaN if no valid readings * @see https://doi.org/10.2337/dc06-1085 */ declare function glucoseHBGI(readings: number[]): number; /** * @file src/metrics/adrr.ts * * Average Daily Risk Range (ADRR). * * ADRR combines the maximum daily hypo and hyper risk values to produce * a single composite risk score. It captures the amplitude of extreme * glucose excursions on each day and averages them. * * Formula (Kovatchev et al. 2006): * For each day d: DR(d) = max(rl over day d) + max(rh over day d) * ADRR = mean(DR) across all days * * Risk categories: * - < 20: low risk * - 20 - 40: moderate risk * - > 40: high risk * * @see https://doi.org/10.2337/dc06-1085 Kovatchev et al. (2006) */ /** * Calculates the Average Daily Risk Range (ADRR). * * @param readings - Array of GlucoseReading objects with timestamps * @returns ADRR value, or NaN if no valid readings * @see https://doi.org/10.2337/dc06-1085 */ declare function calculateADRR(readings: GlucoseReading[]): number; /** * @file src/metrics/grade.ts * * Glycemic Risk Assessment Diabetes Equation (GRADE). * * GRADE produces a single risk score from a glucose value, and its * partitioned variants break down what percentage of the total GRADE * score comes from euglycemic, hyperglycemic, and hypoglycemic readings. * * Formula (Hill et al. 2007): * GRADE(G) = 425 * (log10(log10(G_mmol)) + 0.16)^2 * where G_mmol = G_mgdl / 18.0182 * * Input values must be in mg/dL. Values <= 0 are skipped. * * @see https://doi.org/10.1111/j.1464-5491.2007.02119.x Hill et al. (2007) */ /** Result of the partitioned GRADE calculation. */ interface GRADEResult { /** Mean GRADE score across all valid readings */ readonly grade: number; /** % of total GRADE contributed by euglycemic readings (70-140 mg/dL) */ readonly gradeEuglycemia: number; /** % of total GRADE contributed by hyperglycemic readings (>140 mg/dL) */ readonly gradeHyperglycemia: number; /** % of total GRADE contributed by hypoglycemic readings (<70 mg/dL) */ readonly gradeHypoglycemia: number; } /** * Calculates the GRADE score and its partitioned components. * * @param readings - Array of glucose values in mg/dL * @param hypoThreshold - Upper bound for hypoglycemia (default: 70 mg/dL) * @param hyperThreshold - Lower bound for hyperglycemia (default: 140 mg/dL) * @returns GRADE result with overall score and percentage breakdown, or NaN fields if no valid data * @see https://doi.org/10.1111/j.1464-5491.2007.02119.x */ declare function calculateGRADE(readings: number[], hypoThreshold?: number, hyperThreshold?: number): GRADEResult; /** * @file src/metrics/gri.ts * * Glycemia Risk Index (GRI). * * The GRI is a composite CGM metric that captures both hypo- and * hyperglycemia risk in a single 0-100 score. It is derived from four * TIR zone percentages using the formula from Klonoff et al. (2023). * * Formula: * GRI = (3.0 * VLow) + (2.4 * Low) + (1.6 * VHigh) + (0.8 * High) * clamped to [0, 100] * * Where: * VLow = % time < 54 mg/dL (TBR Level 2) * Low = % time 54-69 mg/dL (TBR Level 1) * VHigh = % time > 250 mg/dL (TAR Level 2) * High = % time 181-250 mg/dL (TAR Level 1) * * Risk zones: * A (lowest risk): GRI <= 20 * B: 20 < GRI <= 40 * C: 40 < GRI <= 60 * D: 60 < GRI <= 80 * E (highest risk): GRI > 80 * * @see https://doi.org/10.1177/19322968221085273 Klonoff et al. (2023) */ /** Input percentages for GRI calculation. */ interface GRIInput { /** % time < 54 mg/dL */ readonly veryLowPercent: number; /** % time 54-69 mg/dL */ readonly lowPercent: number; /** % time 181-250 mg/dL */ readonly highPercent: number; /** % time > 250 mg/dL */ readonly veryHighPercent: number; } /** GRI result with numeric score and risk zone. */ interface GRIResult { readonly score: number; readonly zone: 'A' | 'B' | 'C' | 'D' | 'E'; readonly hypoComponent: number; readonly hyperComponent: number; } /** * Calculates the Glycemia Risk Index (GRI) from TIR zone percentages. * * @param input - Percentages for each out-of-range zone * @returns GRI result with composite score and risk zone * @see https://doi.org/10.1177/19322968221085273 */ declare function calculateGRI(input: GRIInput): GRIResult; /** * @file src/metrics/jindex.ts * * J-Index: a composite measure of both mean glucose and variability. * * Formula (Wojcicki 1995): * J = 0.001 * (mean + SD)^2 * * Input values must be in mg/dL. * * @see https://doi.org/10.1055/s-2007-979906 Wojcicki (1995) */ /** * Calculates the J-Index for a glucose trace. * * The J-Index captures both central tendency and variability in a * single score. Higher values indicate worse glycemic control. * * @param readings - Array of glucose values in mg/dL * @returns J-Index value, or NaN if fewer than 2 valid readings * @see https://doi.org/10.1055/s-2007-979906 */ declare function calculateJIndex(readings: number[]): number; /** * @file src/metrics/modd.ts * * Mean of Daily Differences (MODD). * * MODD quantifies day-to-day glucose variability by comparing glucose values * at matching times on consecutive days. It measures how consistently glucose * behaves across days — lower values indicate more predictable patterns. * * Formula (Service & Nelson, 1980): * MODD = mean(|G(t) - G(t - 24h)|) for all t where both values exist * * Readings are matched across days by finding the closest timestamp within * a configurable tolera