// amino_acids.sy -- The 20 standard amino acids as native Scyla.
//
// Every protein on earth is built from these 20 building blocks. Each record
// carries the one-letter and three-letter codes (IUPAC 1968/1984 nomenclature),
// the full name, the molecular formula, the monoisotopic + average molecular
// weight, side-chain class (nonpolar, polar, acidic, basic), and hydrophobicity
// on the Kyte-Doolittle scale.
//
// This module makes protein sequences a first-class Scyla type. Given an mRNA
// string, the codons.sy module can translate it to a string of one-letter codes,
// and this module can then compute the resulting peptide's molecular weight
// from the periodic table without any external library.
//
// Sources:
//   - IUPAC-IUB Joint Commission on Biochemical Nomenclature, 1984
//   - Kyte J, Doolittle RF. J Mol Biol 157:105-132, 1982 (hydrophobicity)
//   - Lide DR. CRC Handbook of Chemistry and Physics, 85th ed. (MW)
//   - Creighton TE. Proteins: Structures and Molecular Properties, 2nd ed.
//
// Marjorie McCubbins + Aether, April 2026
// Patent pending: 64/034,536 (continuation-in-part)

// ═══════════════════════════════════════════════════════
// STRUCT
// ═══════════════════════════════════════════════════════

struct AminoAcid {
    address: Int,           // 1-20
    code1: String,          // single-letter IUPAC code (A, R, N, ...)
    code3: String,          // three-letter IUPAC code (Ala, Arg, Asn, ...)
    name: String,           // full name
    formula: String,        // molecular formula (free amino acid form)
    mass: Float,            // average molecular weight, g/mol
    residue_mass: Float,    // mass after losing H2O on peptide bond formation
    side_chain: String,     // nonpolar, polar, acidic, basic, aromatic
    hydrophobicity: Float,  // Kyte-Doolittle scale (-4.5 to +4.5)
    essential: Bool,        // essential amino acid in human nutrition
}

fn aa(addr: Int, c1: String, c3: String, nm: String, f: String, m: Float, rm: Float, sc: String, h: Float, e: Bool) -> AminoAcid {
    return AminoAcid { address: addr, code1: c1, code3: c3, name: nm, formula: f, mass: m, residue_mass: rm, side_chain: sc, hydrophobicity: h, essential: e }
}

fn unknown_aa() -> AminoAcid {
    return aa(0, "X", "Xaa", "Unknown", "", 0.0, 0.0, "", 0.0, false)
}

// ═══════════════════════════════════════════════════════
// LOOKUP BY ADDRESS (1 - 20)
// ═══════════════════════════════════════════════════════
//
// Ordering: alphabetical by single-letter code (standard biochemistry reference order)

fn lookup_amino_acid(addr: Int) -> AminoAcid {
    if addr == 1  { return aa(1,  "A", "Ala", "Alanine",       "C3H7NO2",    89.09,  71.08, "nonpolar",  1.8, false) }
    if addr == 2  { return aa(2,  "R", "Arg", "Arginine",      "C6H14N4O2", 174.20, 156.19, "basic",    -4.5, false) }
    if addr == 3  { return aa(3,  "N", "Asn", "Asparagine",    "C4H8N2O3",  132.12, 114.10, "polar",    -3.5, false) }
    if addr == 4  { return aa(4,  "D", "Asp", "Aspartate",     "C4H7NO4",   133.10, 115.09, "acidic",   -3.5, false) }
    if addr == 5  { return aa(5,  "C", "Cys", "Cysteine",      "C3H7NO2S",  121.16, 103.14, "polar",     2.5, false) }
    if addr == 6  { return aa(6,  "E", "Glu", "Glutamate",     "C5H9NO4",   147.13, 129.12, "acidic",   -3.5, false) }
    if addr == 7  { return aa(7,  "Q", "Gln", "Glutamine",     "C5H10N2O3", 146.15, 128.13, "polar",    -3.5, false) }
    if addr == 8  { return aa(8,  "G", "Gly", "Glycine",       "C2H5NO2",    75.07,  57.05, "nonpolar", -0.4, false) }
    if addr == 9  { return aa(9,  "H", "His", "Histidine",     "C6H9N3O2",  155.16, 137.14, "basic",    -3.2, true)  }
    if addr == 10 { return aa(10, "I", "Ile", "Isoleucine",    "C6H13NO2",  131.17, 113.16, "nonpolar",  4.5, true)  }
    if addr == 11 { return aa(11, "L", "Leu", "Leucine",       "C6H13NO2",  131.17, 113.16, "nonpolar",  3.8, true)  }
    if addr == 12 { return aa(12, "K", "Lys", "Lysine",        "C6H14N2O2", 146.19, 128.17, "basic",    -3.9, true)  }
    if addr == 13 { return aa(13, "M", "Met", "Methionine",    "C5H11NO2S", 149.21, 131.19, "nonpolar",  1.9, true)  }
    if addr == 14 { return aa(14, "F", "Phe", "Phenylalanine", "C9H11NO2",  165.19, 147.18, "aromatic",  2.8, true)  }
    if addr == 15 { return aa(15, "P", "Pro", "Proline",       "C5H9NO2",   115.13,  97.12, "nonpolar", -1.6, false) }
    if addr == 16 { return aa(16, "S", "Ser", "Serine",        "C3H7NO3",   105.09,  87.08, "polar",    -0.8, false) }
    if addr == 17 { return aa(17, "T", "Thr", "Threonine",     "C4H9NO3",   119.12, 101.10, "polar",    -0.7, true)  }
    if addr == 18 { return aa(18, "W", "Trp", "Tryptophan",    "C11H12N2O2",204.23, 186.21, "aromatic", -0.9, true)  }
    if addr == 19 { return aa(19, "Y", "Tyr", "Tyrosine",      "C9H11NO3",  181.19, 163.18, "aromatic", -1.3, false) }
    if addr == 20 { return aa(20, "V", "Val", "Valine",        "C5H11NO2",  117.15,  99.13, "nonpolar",  4.2, true)  }
    return unknown_aa()
}

// ═══════════════════════════════════════════════════════
// LOOKUP BY ONE-LETTER CODE
// ═══════════════════════════════════════════════════════

fn amino_acid_by_code1(c: String) -> AminoAcid {
    if c == "A" { return lookup_amino_acid(1) }
    if c == "R" { return lookup_amino_acid(2) }
    if c == "N" { return lookup_amino_acid(3) }
    if c == "D" { return lookup_amino_acid(4) }
    if c == "C" { return lookup_amino_acid(5) }
    if c == "E" { return lookup_amino_acid(6) }
    if c == "Q" { return lookup_amino_acid(7) }
    if c == "G" { return lookup_amino_acid(8) }
    if c == "H" { return lookup_amino_acid(9) }
    if c == "I" { return lookup_amino_acid(10) }
    if c == "L" { return lookup_amino_acid(11) }
    if c == "K" { return lookup_amino_acid(12) }
    if c == "M" { return lookup_amino_acid(13) }
    if c == "F" { return lookup_amino_acid(14) }
    if c == "P" { return lookup_amino_acid(15) }
    if c == "S" { return lookup_amino_acid(16) }
    if c == "T" { return lookup_amino_acid(17) }
    if c == "W" { return lookup_amino_acid(18) }
    if c == "Y" { return lookup_amino_acid(19) }
    if c == "V" { return lookup_amino_acid(20) }
    return unknown_aa()
}

// ═══════════════════════════════════════════════════════
// LOOKUP BY THREE-LETTER CODE
// ═══════════════════════════════════════════════════════

fn amino_acid_by_code3(c: String) -> AminoAcid {
    if c == "Ala" { return lookup_amino_acid(1) }
    if c == "Arg" { return lookup_amino_acid(2) }
    if c == "Asn" { return lookup_amino_acid(3) }
    if c == "Asp" { return lookup_amino_acid(4) }
    if c == "Cys" { return lookup_amino_acid(5) }
    if c == "Glu" { return lookup_amino_acid(6) }
    if c == "Gln" { return lookup_amino_acid(7) }
    if c == "Gly" { return lookup_amino_acid(8) }
    if c == "His" { return lookup_amino_acid(9) }
    if c == "Ile" { return lookup_amino_acid(10) }
    if c == "Leu" { return lookup_amino_acid(11) }
    if c == "Lys" { return lookup_amino_acid(12) }
    if c == "Met" { return lookup_amino_acid(13) }
    if c == "Phe" { return lookup_amino_acid(14) }
    if c == "Pro" { return lookup_amino_acid(15) }
    if c == "Ser" { return lookup_amino_acid(16) }
    if c == "Thr" { return lookup_amino_acid(17) }
    if c == "Trp" { return lookup_amino_acid(18) }
    if c == "Tyr" { return lookup_amino_acid(19) }
    if c == "Val" { return lookup_amino_acid(20) }
    return unknown_aa()
}

// ═══════════════════════════════════════════════════════
// PEPTIDE MOLECULAR WEIGHT
// ═══════════════════════════════════════════════════════
//
// When amino acids form peptide bonds, each bond releases one water molecule
// (H2O, mass 18.015 g/mol). A peptide of n residues has (n-1) peptide bonds
// and therefore n-1 fewer water molecules than the sum of free amino acid
// masses. Equivalently: peptide MW = sum of residue masses + 18.015 (the
// terminal H + OH that remain).

fn peptide_mass(sequence: String) -> Float {
    let n = str_len(sequence)
    let total = 18.015
    let i = 0
    while i < n {
        let c = str_substring(sequence, i, i + 1)
        let aa_rec = amino_acid_by_code1(c)
        total = total + aa_rec.residue_mass
        i = i + 1
    }
    return total
}

// ═══════════════════════════════════════════════════════
// QUICK-ACCESS HELPERS
// ═══════════════════════════════════════════════════════

fn amino_acid_count() -> Int {
    return 20
}

fn is_essential(c: String) -> Bool {
    return amino_acid_by_code1(c).essential
}
