Annotation of gnutrition/budget.c, revision 1.1
1.1 ! asm 1: // SPDX-License-Identifier: GPL-3.0-or-later
! 2: /*
! 3: * $Id$
! 4: *
! 5: * budget.c - USDA Food Pattern budget system for GNUtrition
! 6: *
! 7: * Copyright (C) 2026 Free Software Foundation, Inc.
! 8: *
! 9: * Author: Jason Self <jself@gnu.org>
! 10: * Anton McClure <asm@gnu.org>
! 11: */
! 12:
! 13: #include "budget.h"
! 14: #include "i18n.h"
! 15:
! 16: #include <math.h>
! 17: #include <stdio.h>
! 18:
! 19: /* USDA Healthy US-Style Eating Pattern table.
! 20: Source: Dietary Guidelines for Americans, 2020-2025, Appendix 3.
! 21: Columns: kcal, vegetables (cup-eq), fruits (cup-eq),
! 22: grains (oz-eq), dairy (cup-eq), protein (oz-eq),
! 23: oils (grams). */
! 24: static const struct daily_budget usda_table[] =
! 25: {
! 26: { 1000, 1.0, 1.0, 3.0, 2.0, 2.0, 13.0 },
! 27: { 1200, 1.5, 1.0, 4.0, 2.0, 2.0, 17.0 },
! 28: { 1400, 1.5, 1.5, 5.0, 2.5, 4.0, 17.0 },
! 29: { 1600, 2.0, 1.5, 5.0, 2.5, 5.0, 22.0 },
! 30: { 1800, 2.5, 1.5, 6.0, 3.0, 5.0, 24.0 },
! 31: { 2000, 2.5, 2.0, 6.0, 3.0, 5.5, 27.0 },
! 32: { 2200, 3.0, 2.0, 7.0, 3.0, 6.0, 29.0 },
! 33: { 2400, 3.0, 2.0, 8.0, 3.0, 6.5, 32.0 },
! 34: { 2600, 3.5, 2.0, 9.0, 3.0, 6.5, 34.0 },
! 35: { 2800, 3.5, 2.5, 10.0, 3.0, 7.0, 36.0 },
! 36: { 3000, 4.0, 2.5, 10.0, 3.0, 7.0, 40.0 },
! 37: { 3200, 4.0, 2.5, 10.0, 3.0, 7.0, 44.0 },
! 38: };
! 39:
! 40: #define TABLE_SIZE (sizeof usda_table / sizeof usda_table[0])
! 41:
! 42: /* Activity-factor multipliers (Mifflin-St Jeor convention). */
! 43: static const double activity_factors[] =
! 44: {
! 45: 1.2, /* ACTIVITY_SEDENTARY */
! 46: 1.375, /* ACTIVITY_LIGHT */
! 47: 1.55, /* ACTIVITY_MODERATE */
! 48: 1.725, /* ACTIVITY_VERY_ACTIVE */
! 49: 1.9 /* ACTIVITY_EXTRA_ACTIVE */
! 50: };
! 51:
! 52: /* Linearly interpolate between A and B by fraction T (0.0 to 1.0). */
! 53: static double
! 54: lerp (double a, double b, double t)
! 55: {
! 56: return a + (b - a) * t;
! 57: }
! 58:
! 59: int
! 60: budget_round_to_pattern (int kcal_raw)
! 61: {
! 62: int rounded;
! 63:
! 64: rounded = ((kcal_raw + 100) / 200) * 200;
! 65: if (rounded < 1000)
! 66: rounded = 1000;
! 67: if (rounded > 3200)
! 68: rounded = 3200;
! 69: return rounded;
! 70: }
! 71:
! 72: int
! 73: budget_estimate_calories (int age_years, double height_cm,
! 74: double weight_kg,
! 75: enum activity_level activity,
! 76: enum user_gender gender)
! 77: {
! 78: double bmr;
! 79: double tdee;
! 80: double af;
! 81: double go;
! 82:
! 83: /* Mifflin-St Jeor with neutral "midpoint" default.
! 84: Male constant is +5, female is -161; midpoint is -78.
! 85: BMR = 10 * weight_kg + 6.25 * height_cm - 5 * age - 78 */
! 86: switch (gender)
! 87: {
! 88: default: go = -78; break;
! 89: case GENDER_FEMALE: go = -161; break;
! 90: case GENDER_MALE: go = 5; break;
! 91: }
! 92: bmr = (10.0 * weight_kg) + (6.25 * height_cm) - (5.0 * age_years) + go;
! 93:
! 94: if ((int) activity >= 0
! 95: && (int) activity < (int) (sizeof activity_factors
! 96: / sizeof activity_factors[0]))
! 97: af = activity_factors[activity];
! 98: else
! 99: af = activity_factors[ACTIVITY_SEDENTARY];
! 100:
! 101: tdee = bmr * af;
! 102:
! 103: return budget_round_to_pattern ((int) round (tdee));
! 104: }
! 105:
! 106: struct daily_budget
! 107: budget_for_calories (int kcal)
! 108: {
! 109: struct daily_budget b;
! 110: size_t i;
! 111: double t;
! 112:
! 113: /* Clamp to table range. */
! 114: if (kcal <= usda_table[0].calories)
! 115: return usda_table[0];
! 116:
! 117: if (kcal >= usda_table[TABLE_SIZE - 1].calories)
! 118: return usda_table[TABLE_SIZE - 1];
! 119:
! 120: /* Find the bracketing entries and interpolate. */
! 121: for (i = 0; i < TABLE_SIZE - 1; i++)
! 122: {
! 123: if (kcal >= usda_table[i].calories
! 124: && kcal <= usda_table[i + 1].calories)
! 125: {
! 126: t = (double) (kcal - usda_table[i].calories)
! 127: / (double) (usda_table[i + 1].calories
! 128: - usda_table[i].calories);
! 129: b.calories = kcal;
! 130: b.vegetables = lerp (usda_table[i].vegetables,
! 131: usda_table[i + 1].vegetables, t);
! 132: b.fruits = lerp (usda_table[i].fruits,
! 133: usda_table[i + 1].fruits, t);
! 134: b.grains = lerp (usda_table[i].grains,
! 135: usda_table[i + 1].grains, t);
! 136: b.dairy = lerp (usda_table[i].dairy,
! 137: usda_table[i + 1].dairy, t);
! 138: b.protein = lerp (usda_table[i].protein,
! 139: usda_table[i + 1].protein, t);
! 140: b.oils = lerp (usda_table[i].oils,
! 141: usda_table[i + 1].oils, t);
! 142: return b;
! 143: }
! 144: }
! 145:
! 146: /* Should not reach here; the loop above covers all cases. */
! 147: return usda_table[TABLE_SIZE - 1];
! 148: }
! 149:
! 150: struct daily_budget
! 151: budget_get_default (void)
! 152: {
! 153: return budget_for_calories (2000);
! 154: }
! 155:
! 156: void
! 157: budget_print (const struct daily_budget *budget,
! 158: const struct daily_budget *consumed)
! 159: {
! 160: printf (_("Daily budget (%d kcal USDA Healthy US-Style Eating Pattern):\n\n"),
! 161: budget->calories);
! 162: printf (_("%-20s %10s %10s %10s\n"),
! 163: _("Food Group"), _("Budget"), _("Consumed"), _("Remaining"));
! 164: printf ("%-20s %10s %10s %10s\n",
! 165: "--------------------", "----------",
! 166: "----------", "----------");
! 167: printf (_("%-20s %8.1f %s %8.1f %s %8.1f %s\n"),
! 168: _("Vegetables"),
! 169: budget->vegetables, _("c "),
! 170: consumed->vegetables, _("c "),
! 171: budget->vegetables - consumed->vegetables, _("c "));
! 172: printf (_("%-20s %8.1f %s %8.1f %s %8.1f %s\n"),
! 173: _("Fruits"),
! 174: budget->fruits, _("c "),
! 175: consumed->fruits, _("c "),
! 176: budget->fruits - consumed->fruits, _("c "));
! 177: printf (_("%-20s %8.1f %s %8.1f %s %8.1f %s\n"),
! 178: _("Grains"),
! 179: budget->grains, _("oz"),
! 180: consumed->grains, _("oz"),
! 181: budget->grains - consumed->grains, _("oz"));
! 182: printf (_("%-20s %8.1f %s %8.1f %s %8.1f %s\n"),
! 183: _("Dairy"),
! 184: budget->dairy, _("c "),
! 185: consumed->dairy, _("c "),
! 186: budget->dairy - consumed->dairy, _("c "));
! 187: printf (_("%-20s %8.1f %s %8.1f %s %8.1f %s\n"),
! 188: _("Protein Foods"),
! 189: budget->protein, _("oz"),
! 190: consumed->protein, _("oz"),
! 191: budget->protein - consumed->protein, _("oz"));
! 192: printf (_("%-20s %8.1f %s %8.1f %s %8.1f %s\n"),
! 193: _("Oils"),
! 194: budget->oils, _("g "),
! 195: consumed->oils, _("g "),
! 196: budget->oils - consumed->oils, _("g "));
! 197: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>