/* rdi.c - computes Recommeded Daily Intakes for various nutrients */

/* Copyright (C) 1999 Ian Haywood */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */

/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the */
/* GNU General Public License for more details, in the Copying */
/* section of the manual  */

#include <gnome.h>
#include <glade/glade.h>
#include <gnome-xml/tree.h>
#include <gnome-xml/parser.h>
#include <ctype.h>

#include "text_util.h"
#include "food.h"
#include "recipe.h"
#include "rdi.h"

struct age_data /* RDI for a given age and above */

{
  float age; /* -1=end of dataset */
  float man, woman; /* value for each sex */
};

#define MAX_AGE_DATA 30 /* maximum age_data for a nutrient */

struct rdi

{
  int idx; /* Index in the Nutrition_t structure */
  gboolean by_weight; 
  /* FALSE= units per day, TRUE = units per day per kg body weight */
  float pregnancy; /* increments for each of these states */
  float lactation;
  struct age_data data[MAX_AGE_DATA]; /* RDIs by age */
};

/* General RDI tables */
/* Reference: Bender, D.A. and Bender, A.E., `Nutrition: a reference handbook'
 * 1st ed., Oxford University Press, 1997, ISBN 0-19-262368-0. All page numbers
 * are for this book */
/* Unless otherwise stated, American values are chosen where Bender gives
 * a choice, and the lower value is generally chosen where a range is
 * given.
 * BUGS: more data on cholesterol, fatty acids and fibre */

#define NUM_NORMAL_RDIS 32

struct rdi rdi_table[NUM_NORMAL_RDIS]=
{
  {
    0, /* Protein  pg 182 */
    TRUE,
    6, 17.5, /* EU values */
    {{0.3, 1.85, 1.85}, {0.58, 1.65, 1.65}, {0.83, 1.48, 1.48},
     {1.0, 1.26, 1.26}, {1.5, 1.17, 1.17}, {2, 1.13, 1.13}, 
     {3, 1.09, 1.09}, {4, 1.06, 1.06}, {5, 1.02, 1.02}, 
     {6, 1.01, 1.01}, {9, 0.99, 0.99}, {10, 0.99, 1}, 
     {11, 0.98, 0.98}, {12, 1, 0.96}, {13, 0.97, 0.94},
     {14, 0.96, 0.90}, {15, 0.92, 0.87}, {16, 0.90, 0.83},
     {17, 0.86, 0.80}, {18, 0.75, 0.75}, {-1, 0, 0}}
  },
  /* NOTE: Fats defined by special algorithm */
  {
    11, /* Fibre  pg 117 */
    FALSE,
    0, 0,
    {{18, 30, 30}, {-1, 0, 0}} /* Paediatric value not available */
  },
  /* Metals */
  {
    12, /* Ca pg 63 */
    FALSE,
    400, 400,
    {{0, 400, 400}, {0.5, 600, 600}, {1, 800, 800}, {11, 1200, 1200},
     {25, 800, 800}, {-1, 0, 0}}
  },
  { 
    13, /* Fe, pg 63 */
    FALSE,
    15, 0,
    {{0, 6, 6}, {0.5, 10, 10}, {11, 12, 15}, {19, 10, 15}, 
     {51, 10, 10}, {-1, 0, 0}}
  },
  {
    14, /* Mg, pg 63*/
    FALSE,
    40, 75,
    {{0, 40, 40}, {0.5, 60, 60}, {1, 80, 80}, {4, 120, 120}, 
     {7, 170, 170}, {11, 270, 280}, {15, 400, 300}, {19, 350, 280},
     {-1, 0, 0}}
  },
  { 
    15, /* P, pg 63 */
    FALSE,
    400, 400,
    {{0, 300, 300}, {0.5, 500, 500}, {1, 800, 800}, {11, 1200, 1200},
     {25, 800, 800}, {-1, 0, 0}} 
  },
  {
    16, /* K , pg 430*/
    FALSE,
    0, 0,
    {{0, 500, 500}, {0.5, 700, 700}, {1, 1000, 1000}, {2, 1400, 1400},
     {6, 1600, 1600}, {10, 2000, 2000}, {-1, 0, 0}}
  },
  {
    17, /* Na pg 430 */
    FALSE,
    0, 0,
    {{0, 120, 120}, {0.5, 200, 200}, {1, 225, 225}, {2, 300, 300},
     {6, 400, 400}, {10, 500, 500}, {-1, 0, 0}}
  },
  {
    18, /* Zn, pg 435 */
    FALSE,
    3, 7,
    {{0, 5, 5}, {1, 10, 10}, {11, 15, 12}, {-1, 0, 0}}
  },
  {
    19, /* Cu, pg 431 */
    FALSE,
    0, 0.3, /* UK value */
    /* back to US */
    {{0, 0.4, 0.4}, {0.5, 0.6}, {1, 0.7, 0.7}, {4, 1.0, 1.0}, 
     {11, 1.5, 1.5}, {-1, 0, 0}}
  },
  {
    20, /* Mn pg 435*/
    FALSE,
    0,0,
    /* UK values */
    {{0, 0.3, 0.3}, {0.5, 0.6, 0.6}, {1, 0.7, 0.7}, {4, 1.0, 1.0},
     {11, 1.5, 1.5}, {-1, 0, 0}}
  },
  { 
    21, /* Se pg 63*/
    FALSE,
    10, 20,
    {{0, 10, 10}, {0.5, 15, 15}, {1, 20, 20}, {7, 30, 30}, 
     {11, 40, 45}, {15, 50, 50}, {19, 70, 55}, {-1, 0, 0}}
  },

  /* Vitamins */

  {
    22, /* Vitamin A, pg 63 */
    FALSE,
    0, 500,
    {{0, 375, 365}, {1, 400, 400}, {4, 500, 500}, {7, 700, 700},
     {11, 1000, 800}, {-1, 0, 0}}
  },
  {
    24, /* Vitamin E, pg 63 */
    FALSE,
    2, 2,
    {{0, 3, 3}, {0.5, 4, 4}, {1, 6, 6}, {4, 7, 7}, {11, 10, 8},
     {-1, 0, 0}}
  },
  {
    25, /* Vitamin C, pg 63 */
    FALSE,
    10, 35,
    {{0, 30, 30}, {0.5, 35, 35}, {1, 40, 40}, {4, 45, 45}, 
     {11, 50, 50}, {15, 60, 60}, {-1, 0, 0}}
  },
  {
    26, /* Thiamine, pg 63 */
    FALSE,
    0.4, 0.5,
    {{0, 0.3, 0.3}, {0.5, 0.4, 0.4}, {1, 0.7, 0.7}, {4, 0.9, 0.9},
     {7, 1.0, 1.0}, {11, 1.3, 1.1}, {15, 1.5, 1.1}, {51, 1.2, 1.1},
     {-1, 0, 0}}
  },
  {
    27, /* riboflavin, pg 63 */
    FALSE,
    0.3, 0.5,
    {{0, 0.4, 0.4}, {0.5, 0.5, 0.5}, {1, 0.8, 0.8}, {4, 1.1, 1.1},
     {7, 1.2, 1.2}, {11, 1.5, 1.3}, {15, 1.8, 1.3}, {19, 1.7, 1.3},
     {51, 1.4, 1.3}, {-1, 0, 0}}
  },
  {
    28, /* niacin, pg 63 */
    FALSE,
    2, 5,
    {{0, 5, 5}, {0.5, 6, 6}, {1, 9, 9}, {4, 12, 12}, {7, 13, 13},
     {11, 17, 15}, {15, 20, 15}, {19, 19, 15}, {51, 15, 13},
     {-1, 0, 0}}
  },
  {
    29, /* pantotheic acid, pg 392 */
    FALSE,
    0, 0,
    {{0, 2, 2}, {0.5, 3, 3}, {7, 4, 4}, {-1, 0, 0}}
  },
  {
    30, /* vitamin B-6, pg 63 */
    FALSE,
    0.6, 0.5,
    {{0, 0.3, 0.3}, {0.5, 0.5, 0.5}, {1, 0.7, 0.7}, {4, 1.1, 1.1},
     {7, 1.4, 1.4}, {11, 1.7, 1.4}, {15, 2.0, 1.5}, {19, 2.0, 1.6},
     {-1, 0, 0}}
  },
  {
    31, /* folate, pg 63 */
    FALSE,
    220, 100,
    {{0, 25, 25}, {0.5, 35, 35}, {1, 50, 50}, {4, 75, 75}, 
     {7, 100, 100}, {11, 150, 150}, {15, 200, 180}, {-1, 0, 0}}
  },
  {
    32, /* vitamin B-12, pg 63 */
    FALSE,
    0.2, 0.6,
    {{0, 0.3, 0.3}, {0.5, 0.5, 0.5}, {1, 0.7, 0.7}, {4, 1, 1}, 
     {7, 1.4, 1.4}, {11, 2.0, 2.0}, {-1, 0, 0}}
  },
					     
  /* The essential amino acids. Paediatric values are provided for
   * the ages 3-4 months, 2 years and 10-12 years. I have 
   * extended these to age-ranges: 3 mths up, 2yrs up, 10 yrs up
   * and adult. All pg 183 (adults), 184 (children) */
  {
    33, /* Trp */
    TRUE,
    0, 0,
    {{0.25, 17, 17}, {2, 12.5, 12.5}, {10, 3.5, 3.5}, {-1, 0, 0}}
  },
  {
    34, /* Thr */
    TRUE,
    0, 0,
    {{0.25, 87, 87}, {2, 37, 37}, {10, 28, 28}, {18, 7, 7}, 
     {-1, 0, 0}}
  },
  {
    35, /* Ile */
    TRUE,
    0, 0,
    {{0.25, 70, 70}, {2, 31, 31}, {10, 28, 28}, {18, 10, 10}, 
     {-1, 0, 0}}
  },
  {
    36, /* Leu */
    TRUE,
    0, 0,
    {{0.25, 161, 161}, {2, 73, 73}, {10, 44, 44}, {18, 14, 14},
     {-1, 0, 0}}
  },
  {
    37, /* Lys */
    TRUE,
    0, 0,
    {{0.25, 103, 103}, {2, 64, 64}, {10, 44, 44}, {18, 12, 12},
     {-1, 0, 0}}
  },
  {
    38, /* Cys + Met (Cys is 39) */
    TRUE,
    0, 0,
    {{0.25, 58, 58}, {2, 27, 27}, {10, 22, 22}, {18, 13, 13},
     {-1, 0, 0}}
  },
  {
    40, /* Phe + Tyr (Tyr is 41) */
    TRUE,
    0, 0,
    {{0.25, 125, 125}, {2, 69, 69}, {10, 22, 22}, {18, 14, 14},
     {-1, 0, 0}}
  },
  {
    42,/* Val */
    TRUE,
    0, 0,
    {{0.25, 93, 93}, {2, 38, 38}, {10, 25, 25}, {18, 10, 10},
     {-1, 0, 0}}
  },
  {
    44, /* His */
    TRUE,
    0, 0, 
    {{0, 8, 8}, {-1, 0, 0}}
    /* WARNING: Histidine is essential for children, in quantities
     * greater than adults, but I can't get appropriate values */
  },
  {
    51, /* Cholesterol (pg140) */
    FALSE,
    0, 0,
    {{18, 300, 300}, {-1, 0, 0}}
  }
};

/* Indexes for non-standard values */

#define CHO 2
#define FAT 1
#define ENERGY 9
#define SUGAR 10
#define M_UNSAT_FAT 76
#define P_UNSAT_FAT 77
#define SAT_FAT 52
#define LINO 63 /* (18:2) */
#define A_LINO 64 /* (18:3) */
#define MET 38
#define CYS 39
#define PHE 40
#define TYR 41
#define KJ2KCAL 0.24
#define EN_CHO (4.27 / KJ2KCAL) /* calorific value of carbohydrates */
#define EN_FAT (9.0 / KJ2KCAL) /* calorific value of fat */
#define EN_KCAL 4 /* index for energy as kcal */

float 
lookup (struct age_data *, gfloat age, gboolean gender); 

/* returns value for the given age on a table (array of age_data). 
 * 0 = no valid entry */

gfloat 
normal_rdi (int i, gfloat age, gfloat weight, gboolean female,
	    gboolean preg, gboolean lac);

/* returns RDI for which a struct rdi is defined */

/* RDIs with special algorithms (no struct rdi defined) */

gfloat 
energy (gfloat, gfloat, gboolean);

/* returns recommended energy intake for the user */    

float 
lookup (struct age_data *data, gfloat age, gboolean gender)

{
  float entry = 0;
  float *dum;
  
  dum = &entry;
  while (data->age != -1)
    {
      if (age >= data->age)
	{
	  if (gender)
	    *dum = data->woman;
	  else
	    *dum = data->man;
	}
      data++;
    }
  data--;
  return *dum;
}

gfloat 
normal_rdi (int i, gfloat age, gfloat weight, gboolean female,
	    gboolean preg, gboolean lac)

{
  gfloat r;

  r = lookup (rdi_table[i].data, age, female);
  if (rdi_table[i].by_weight)
    r *= weight;
  if (preg)
    r += rdi_table[i].pregnancy;
  if (lac)
    r += rdi_table[i].lactation;

  return r;
}


gfloat 
energy (gfloat age, gfloat weight, gboolean female)

{

  /* energy consumption variees according to activity. Values
     presented here represent a "sedentary lifestyle" ;-) */

  struct age_data child[10] =
    /* age : boys : girls */
  { 
    {0, 410, 410}, /* NOTE kJ per kg body weight */ 
    {1, 414, 431}, /* defined up to age 10 */
    {2, 414, 406}, /* pg 89 */
    {3, 393, 377}, 
    {4, 377, 364},
    {5, 364, 352},
    {6, 352, 331},
    {7, 331, 301},
    {8, 306, 276},
    {9, 285, 247}
  };
  
/* for adults, much more interesting. First BMR (basal metabolic rate)
   is computed using the equation A (weight) + B, where A and B are
   dependent upon the age and sex. (pg 82) The BMR is converted into energy
   requirements by a fourth variable C, also age- and sex-dependent (pg 89)*/
  
  struct age_data A[5]=
    
    /* age: men : women */
  {
    {10, 0.0732, 0.0510},
    {18, 0.0640, 0.0615},
    {30, 0.0485, 0.0364},
    {60, 0.0565, 0.0439},
    {-1, 0, 0}
  };
  
  struct age_data B[5]=
    
    /* age : men :women */
    
  {
    {10, 2.72, 3.12},
    {18, 2.84, 2.08},
    {30, 3.67, 3.47},
    {60, 2.04, 2.49},
    {-1, 0, 0}
  };
  
  struct age_data C[11]=
    
    /* age:men:women */
    
  {
    {10, 1.74, 1.59},
    {11, 1.67, 1.55},
    {12, 1.61, 1.51},
    {13, 1.56, 1.47},
    {14, 1.49, 1.46},
    {15, 1.44, 1.47},
    {16, 1.40, 1.48},
    {17, 1.40, 1.50},
    {18, 1.41, 1.42},
    {60, 1.40, 1.40},
    {-1, 0, 0}
  };
  
  static float BMR;
  static float e;
  
  if (age < 10)
    e = lookup (child, age, female);
  else
    {
      BMR = (lookup (A, age, female) * weight)
	+ lookup (B, age, female);
      e = BMR * lookup (C, age, female);
    }
  e *= 1000; /* tables are in MJ, we want kJ */
  return e;
}

 
void
rdi_calc_rdi (Nutrition_t *nutr, int age, int weight, 
	     gboolean female, gboolean preg, gboolean lac)

{
  gfloat c;
  gint i;
      nutr->goal[ENERGY] = energy (age, weight, female);
      /* CHO RDI expressed as being 53% of total energy intake (pg 122) */
      c = 0.53 * nutr->goal[ENERGY]; 
      /* simple enough, however, this is ENERGY, not grams of CHO */
      nutr->goal[CHO] = c / EN_CHO;
      /* converts to grams of CHO. EN_CHO = energy as CHO, from database. */
      nutr->goal[SUGAR] = 0.10 * nutr->goal[ENERGY] / EN_CHO;
      /* Fat RDI also expressed as 30% of energy pg 140 */
      nutr->goal[FAT] = (0.3 * nutr->goal[ENERGY]) / EN_FAT;
      /* same again for saturated fat, 10% pg 133 */
      nutr->goal[SAT_FAT] = (0.1 * nutr->goal[ENERGY]) / EN_FAT;
      /* monounsaturated, 12%  (UK value, pg133) */
      nutr->goal[M_UNSAT_FAT] = (0.1 * nutr->goal[ENERGY]) / EN_FAT;
      /* polyunsatured 6% (UK value, pg133) */
      nutr->goal[P_UNSAT_FAT] = (0.1 * nutr->goal[ENERGY]) / EN_FAT;
      /* linoleic acid 1% (UK value, pg133)*/
      nutr->goal[LINO] = (0.01 * nutr->goal[ENERGY]) / EN_FAT;
      /* alpha-linolenic acid 0.2% (UK value, pg133) */
      nutr->goal[A_LINO] = (0.002 * nutr->goal[ENERGY]) / EN_FAT;
      /* Note all of these fat values assume calorific value same as for fat
       * aggregate */ 

      for (i = 0; i<NUM_NORMAL_RDIS; i++) /* do all normal values */
	nutr->goal[rdi_table[i].idx] = normal_rdi (i, age, weight, female, preg, lac);

      /* Cys and Met, can be interconverted by hepatic
       * enzymes, so RDIs are issued for the total. Multiply by
       * Met value to provide illusion of a Met RDI */

	  /* Allow case when nutr->total[] values == 0.0. Make goals
	   * half-and-half, allow for interconvertability in
	   * compute_percent_nutrient_goal(). ED 1/8/00. */

       nutr->goal[MET] = 0.5 * normal_rdi (27, age, weight, female, preg, lac);
       nutr->goal[CYS] = nutr->goal[MET];

       nutr->goal[PHE] = 0.5 * normal_rdi (28, age, weight, female, preg, lac);
       nutr->goal[TYR] = nutr->goal[PHE];

	   /* divide protein values by 1000 to covert from milligrams to
		* grams. ED: 4/8/00. */
	   for ( i=33; i<51; i++)
	   {
		   nutr->goal[i] /= 1000.0;
	   }

      nutr->goal[EN_KCAL] = nutr->goal[ENERGY] * KJ2KCAL;
}
