export interface IGILLookupsBySex {
  Male: IGILLookups,
  Female: IGILLookups
}

export interface IGILLookups {
  3: [number, number, number]
  3.25: [number, number, number]
  3.5: [number, number, number]
  3.75: [number, number, number]
  4: [number, number, number]
  4.25: [number, number, number]
  4.5: [number, number, number]
  4.75: [number, number, number]
  5: [number, number, number]
  5.25: [number, number, number]
  5.5: [number, number, number]
  5.75: [number, number, number]
  6: [number, number, number]
  6.25: [number, number, number]
  6.5: [number, number, number]
  6.75: [number, number, number]
  7: [number, number, number]
  7.25: [number, number, number]
  7.5: [number, number, number]
  7.75: [number, number, number]
  8: [number, number, number]
  8.25: [number, number, number]
  8.5: [number, number, number]
  8.75: [number, number, number]
  9: [number, number, number]
  9.25: [number, number, number]
  9.5: [number, number, number]
  9.75: [number, number, number]
  10: [number, number, number]
  10.25: [number, number, number]
  10.5: [number, number, number]
  10.75: [number, number, number]
  11: [number, number, number]
  11.25: [number, number, number]
  11.5: [number, number, number]
  11.75: [number, number, number]
  12: [number, number, number]
  12.25: [number, number, number]
  12.5: [number, number, number]
  12.75: [number, number, number]
  13: [number, number, number]
  13.25: [number, number, number]
  13.5: [number, number, number]
  13.75: [number, number, number]
  14: [number, number, number]
  14.25: [number, number, number]
  14.5: [number, number, number]
  14.75: [number, number, number]
  15: [number, number, number]
  15.25: [number, number, number]
  15.5: [number, number, number]
  15.75: [number, number, number]
  16: [number, number, number]
  16.25: [number, number, number]
  16.5: [number, number, number]
  16.75: [number, number, number]
  17: [number, number, number]
  17.25: [number, number, number]
  17.5: [number, number, number]
  17.75: [number, number, number]
  18: [number, number, number]
  18.25: [number, number, number]
  18.5: [number, number, number]
  18.75: [number, number, number]
  19: [number, number, number]
  19.25: [number, number, number]
  19.5: [number, number, number]
  19.75: [number, number, number]
  20: [number, number, number]
  20.25: [number, number, number]
  20.5: [number, number, number]
  20.75: [number, number, number]
  21: [number, number, number]
  21.25: [number, number, number]
  21.5: [number, number, number]
  21.75: [number, number, number]
  22: [number, number, number]
  22.25: [number, number, number]
  22.5: [number, number, number]
  22.75: [number, number, number]
  23: [number, number, number]
  23.25: [number, number, number]
  23.5: [number, number, number]
  23.75: [number, number, number]
  24: [number, number, number]
  24.25: [number, number, number]
  24.5: [number, number, number]
  24.75: [number, number, number]
  25: [number, number, number]
  25.25: [number, number, number]
  25.5: [number, number, number]
  25.75: [number, number, number]
  26: [number, number, number]
  26.25: [number, number, number]
  26.5: [number, number, number]
  26.75: [number, number, number]
  27: [number, number, number]
  27.25: [number, number, number]
  27.5: [number, number, number]
  27.75: [number, number, number]
  28: [number, number, number]
  28.25: [number, number, number]
  28.5: [number, number, number]
  28.75: [number, number, number]
  29: [number, number, number]
  29.25: [number, number, number]
  29.5: [number, number, number]
  29.75: [number, number, number]
  30: [number, number, number]
  30.25: [number, number, number]
  30.5: [number, number, number]
  30.75: [number, number, number]
  31: [number, number, number]
  31.25: [number, number, number]
  31.5: [number, number, number]
  31.75: [number, number, number]
  32: [number, number, number]
  32.25: [number, number, number]
  32.5: [number, number, number]
  32.75: [number, number, number]
  33: [number, number, number]
  33.25: [number, number, number]
  33.5: [number, number, number]
  33.75: [number, number, number]
  34: [number, number, number]
  34.25: [number, number, number]
  34.5: [number, number, number]
  34.75: [number, number, number]
  35: [number, number, number]
  35.25: [number, number, number]
  35.5: [number, number, number]
  35.75: [number, number, number]
  36: [number, number, number]
  36.25: [number, number, number]
  36.5: [number, number, number]
  36.75: [number, number, number]
  37: [number, number, number]
  37.25: [number, number, number]
  37.5: [number, number, number]
  37.75: [number, number, number]
  38: [number, number, number]
  38.25: [number, number, number]
  38.5: [number, number, number]
  38.75: [number, number, number]
  39: [number, number, number]
  39.25: [number, number, number]
  39.5: [number, number, number]
  39.75: [number, number, number]
  40: [number, number, number]
  40.25: [number, number, number]
  40.5: [number, number, number]
  40.75: [number, number, number]
  41: [number, number, number]
  41.25: [number, number, number]
  41.5: [number, number, number]
  41.75: [number, number, number]
  42: [number, number, number]
  42.25: [number, number, number]
  42.5: [number, number, number]
  42.75: [number, number, number]
  43: [number, number, number]
  43.25: [number, number, number]
  43.5: [number, number, number]
  43.75: [number, number, number]
  44: [number, number, number]
  44.25: [number, number, number]
  44.5: [number, number, number]
  44.75: [number, number, number]
  45: [number, number, number]
  45.25: [number, number, number]
  45.5: [number, number, number]
  45.75: [number, number, number]
  46: [number, number, number]
  46.25: [number, number, number]
  46.5: [number, number, number]
  46.75: [number, number, number]
  47: [number, number, number]
  47.25: [number, number, number]
  47.5: [number, number, number]
  47.75: [number, number, number]
  48: [number, number, number]
  48.25: [number, number, number]
  48.5: [number, number, number]
  48.75: [number, number, number]
  49: [number, number, number]
  49.25: [number, number, number]
  49.5: [number, number, number]
  49.75: [number, number, number]
  50: [number, number, number]
  50.25: [number, number, number]
  50.5: [number, number, number]
  50.75: [number, number, number]
  51: [number, number, number]
  51.25: [number, number, number]
  51.5: [number, number, number]
  51.75: [number, number, number]
  52: [number, number, number]
  52.25: [number, number, number]
  52.5: [number, number, number]
  52.75: [number, number, number]
  53: [number, number, number]
  53.25: [number, number, number]
  53.5: [number, number, number]
  53.75: [number, number, number]
  54: [number, number, number]
  54.25: [number, number, number]
  54.5: [number, number, number]
  54.75: [number, number, number]
  55: [number, number, number]
  55.25: [number, number, number]
  55.5: [number, number, number]
  55.75: [number, number, number]
  56: [number, number, number]
  56.25: [number, number, number]
  56.5: [number, number, number]
  56.75: [number, number, number]
  57: [number, number, number]
  57.25: [number, number, number]
  57.5: [number, number, number]
  57.75: [number, number, number]
  58: [number, number, number]
  58.25: [number, number, number]
  58.5: [number, number, number]
  58.75: [number, number, number]
  59: [number, number, number]
  59.25: [number, number, number]
  59.5: [number, number, number]
  59.75: [number, number, number]
  60: [number, number, number]
  60.25: [number, number, number]
  60.5: [number, number, number]
  60.75: [number, number, number]
  61: [number, number, number]
  61.25: [number, number, number]
  61.5: [number, number, number]
  61.75: [number, number, number]
  62: [number, number, number]
  62.25: [number, number, number]
  62.5: [number, number, number]
  62.75: [number, number, number]
  63: [number, number, number]
  63.25: [number, number, number]
  63.5: [number, number, number]
  63.75: [number, number, number]
  64: [number, number, number]
  64.25: [number, number, number]
  64.5: [number, number, number]
  64.75: [number, number, number]
  65: [number, number, number]
  65.25: [number, number, number]
  65.5: [number, number, number]
  65.75: [number, number, number]
  66: [number, number, number]
  66.25: [number, number, number]
  66.5: [number, number, number]
  66.75: [number, number, number]
  67: [number, number, number]
  67.25: [number, number, number]
  67.5: [number, number, number]
  67.75: [number, number, number]
  68: [number, number, number]
  68.25: [number, number, number]
  68.5: [number, number, number]
  68.75: [number, number, number]
  69: [number, number, number]
  69.25: [number, number, number]
  69.5: [number, number, number]
  69.75: [number, number, number]
  70: [number, number, number]
  70.25: [number, number, number]
  70.5: [number, number, number]
  70.75: [number, number, number]
  71: [number, number, number]
  71.25: [number, number, number]
  71.5: [number, number, number]
  71.75: [number, number, number]
  72: [number, number, number]
  72.25: [number, number, number]
  72.5: [number, number, number]
  72.75: [number, number, number]
  73: [number, number, number]
  73.25: [number, number, number]
  73.5: [number, number, number]
  73.75: [number, number, number]
  74: [number, number, number]
  74.25: [number, number, number]
  74.5: [number, number, number]
  74.75: [number, number, number]
  75: [number, number, number]
  75.25: [number, number, number]
  75.5: [number, number, number]
  75.75: [number, number, number]
  76: [number, number, number]
  76.25: [number, number, number]
  76.5: [number, number, number]
  76.75: [number, number, number]
  77: [number, number, number]
  77.25: [number, number, number]
  77.5: [number, number, number]
  77.75: [number, number, number]
  78: [number, number, number]
  78.25: [number, number, number]
  78.5: [number, number, number]
  78.75: [number, number, number]
  79: [number, number, number]
  79.25: [number, number, number]
  79.5: [number, number, number]
  79.75: [number, number, number]
  80: [number, number, number]
  80.25: [number, number, number]
  80.5: [number, number, number]
  80.75: [number, number, number]
  81: [number, number, number]
  81.25: [number, number, number]
  81.5: [number, number, number]
  81.75: [number, number, number]
  82: [number, number, number]
  82.25: [number, number, number]
  82.5: [number, number, number]
  82.75: [number, number, number]
  83: [number, number, number]
  83.25: [number, number, number]
  83.5: [number, number, number]
  83.75: [number, number, number]
  84: [number, number, number]
  84.25: [number, number, number]
  84.5: [number, number, number]
  84.75: [number, number, number]
  85: [number, number, number]
  85.25: [number, number, number]
  85.5: [number, number, number]
  85.75: [number, number, number]
  86: [number, number, number]
  86.25: [number, number, number]
  86.5: [number, number, number]
  86.75: [number, number, number]
  87: [number, number, number]
  87.25: [number, number, number]
  87.5: [number, number, number]
  87.75: [number, number, number]
  88: [number, number, number]
  88.25: [number, number, number]
  88.5: [number, number, number]
  88.75: [number, number, number]
  89: [number, number, number]
  89.25: [number, number, number]
  89.5: [number, number, number]
  89.75: [number, number, number]
  90: [number, number, number]
  90.25: [number, number, number]
  90.5: [number, number, number]
  90.75: [number, number, number]
  91: [number, number, number]
  91.25: [number, number, number]
  91.5: [number, number, number]
  91.75: [number, number, number]
  92: [number, number, number]
  92.25: [number, number, number]
  92.5: [number, number, number]
  92.75: [number, number, number]
  93: [number, number, number]
  93.25: [number, number, number]
  93.5: [number, number, number]
  93.75: [number, number, number]
  94: [number, number, number]
  94.25: [number, number, number]
  94.5: [number, number, number]
  94.75: [number, number, number]
  95: [number, number, number]
}

export interface IGLICoefficients {
  q0: number,
  q1: number,
  a0: number,
  a1: number,
  a2: number,
  a3: number,
  a4: number,
  a5: number,
  a6: number,
  p0: number,
  p1: number,
  p2: number,
  p3: number,
  p4: number,
  p5: number
}

export interface IGLICoefficientsBySex {
  Male: IGLICoefficients,
  Female: IGLICoefficients
}

export interface IGLIFunctionResults {
  L: number,
  M: number,
  S: number,
  LLN: number,
  percent: number,
  zscore: number
}

export interface IGLIFunction {
  ({ age, sex, height, ethnicity, measured}: { age: number, sex: sex, height: number, ethnicity: ethnicity, measured: number }): IGLIFunctionResults;
}

export function GLIFunction(coefficients: IGLICoefficientsBySex, lookups: IGILLookupsBySex, age: number, sex: sex, height: number, ethnicity: ethnicity, measured: number): IGLIFunctionResults {
  const c = coefficients[sex];

  const AfrAm = ethnicity === "AfrAm" ? 1 : 0; 
  const NEAsia = ethnicity === "NEAsia" ? 1 : 0;
  const SEAsia = ethnicity === "SEAsia" ? 1 : 0;
  const Other = ethnicity === "Other" ? 1 : 0;

  const Lspline = lookups[sex][findIndex(age)][0]; // @TODO implements lookup for 3-95 vs. second method
  const Mspline = lookups[sex][findIndex(age)][1];
  const Sspline = lookups[sex][findIndex(age)][2];

  const L = c['q0'] + c['q1'] * Math.log(age) + Lspline;
  const M = Math.exp(c['a0'] + c['a1'] * Math.log(height) + c['a2'] * Math.log(age) + c['a3'] * AfrAm + c['a4'] * NEAsia + c['a5'] * SEAsia + c['a6'] * Other + Mspline);
  const S = Math.exp(c['p0'] + c['p1'] * Math.log(age) + c['p2'] * AfrAm + c['p3'] * NEAsia + c['p4'] * SEAsia + c['p5'] * Other + Sspline)

  const LLN = Math.exp(Math.log(M) + Math.log(1 - 1.645 * L * S) / L)
  const percent = (measured / M) * 100;
  const zscore = (Math.pow((measured / M), L) - 1) / (L * S);

  return {
    L,
    M,
    S,
    LLN,
    percent,
    zscore
  }
} 

// @TODO is there a way to avoid this duplication so the runtime has access?
export const sexes = ["Male", "Female"];
export type sex = "Male" | "Female"

export const ethnicities = ["Caucasian", "AfrAm", "NEAsia", "SEAsia", "Other"];
export type ethnicity = "Caucasian" | "AfrAm" | "NEAsia" | "SEAsia" | "Other";

export function findIndex(age: number): keyof IGILLookups {
  if (age % 0.25 === 0) {
    // @TODO is there a way to prove that this is going to be a lookup-able value?
    // @ts-ignore
    return age;
  } else {
    // @TODO same thing
    // @ts-ignore 
    return age - (age % 0.25);
  }
}