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>