code wiki / (root) / nx_thermal_process_test.nx

nx_thermal_process_test.nx source

↩ module page · 242 lines · 14349 B

1// nx_thermal_process_test.nx -- gate for nx_thermal_process. The central 2// test is T5: the z-value RECOVERED from the two legal PMO schedules must 3// land in the published 4.3-5.0 C band for the milk-pasteurisation design 4// organism. That is an independent check of regulation against thermal-death 5// literature, and every compliance verdict downstream rests on it. 6// expect_exit: 0 license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_pow10.nx" 9import "nx_chem_solution.nx" 10import "nx_thermal_process.nx" 11 12func t_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 13func t_putn(v: i64) -> i64 { let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, bb, k); return 0 } 14 15func main() -> i64 { 16 var pass: i64 = 0 17 var total: i64 = 0 18 19 // --- T1 pow10 KAT at exact table points + interpolated midpoint --- 20 total = total + 1 21 let e0: i64 = ipow10_q3(0) 22 let e1: i64 = ipow10_q3(1000) 23 let em1: i64 = ipow10_q3(0 - 1000) 24 let e05: i64 = ipow10_q3(500) 25 let e061: i64 = ipow10_q3(610) 26 t_puts("T1 pow10 1/10/0.1/3.162/4.074 = " as *u8); t_putn(e0); t_puts(" " as *u8); t_putn(e1); t_puts(" " as *u8); t_putn(em1); t_puts(" " as *u8); t_putn(e05); t_puts(" " as *u8); t_putn(e061); t_puts(": " as *u8) 27 var ok1: i64 = 1 28 if e0 != 1000 { ok1 = 0 } 29 if e1 != 10000 { ok1 = 0 } 30 if em1 != 100 { ok1 = 0 } 31 if e05 != 3162 { ok1 = 0 } 32 if e061 < 4020 { ok1 = 0 } 33 if e061 > 4130 { ok1 = 0 } 34 if ok1 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 35 36 // --- T2 log10 is the INVERSE of pow10 (round-trip, both directions) --- 37 total = total + 1 38 let l1: i64 = ilog10_milli(1000) 39 let l10: i64 = ilog10_milli(10000) 40 let l100: i64 = ilog10_milli(100000) 41 let l120: i64 = ilog10_milli(120000) 42 let lbad: i64 = ilog10_milli(0) 43 t_puts("T2 log10 of 1/10/100/120 = " as *u8); t_putn(l1); t_puts(" " as *u8); t_putn(l10); t_puts(" " as *u8); t_putn(l100); t_puts(" " as *u8); t_putn(l120); t_puts(" (want 0/1000/2000/~2079) zero-input=" as *u8); t_putn(lbad); t_puts(": " as *u8) 44 var ok2: i64 = 1 45 if l1 != 0 { ok2 = 0 } 46 if l10 != 1000 { ok2 = 0 } 47 if l100 != 2000 { ok2 = 0 } 48 if l120 < 2060 { ok2 = 0 } 49 if l120 > 2095 { ok2 = 0 } 50 if lbad != IP10_INVALID { ok2 = 0 } 51 if ok2 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 52 53 // --- T3 D-value moves exactly one decade per z (the definition) --- 54 total = total + 1 55 let d_ref: i64 = tp_d_at_temp_ms(TP_BOT_D_REF_MS, TP_REF_STERIL_MILLI_C, TP_BOT_Z_MILLI, TP_REF_STERIL_MILLI_C) 56 let d_lo: i64 = tp_d_at_temp_ms(TP_BOT_D_REF_MS, TP_REF_STERIL_MILLI_C, TP_BOT_Z_MILLI, TP_REF_STERIL_MILLI_C - TP_BOT_Z_MILLI) 57 let d_hi: i64 = tp_d_at_temp_ms(TP_BOT_D_REF_MS, TP_REF_STERIL_MILLI_C, TP_BOT_Z_MILLI, TP_REF_STERIL_MILLI_C + TP_BOT_Z_MILLI) 58 t_puts("T3 D at ref/-z/+z = " as *u8); t_putn(d_ref); t_puts("/" as *u8); t_putn(d_lo); t_puts("/" as *u8); t_putn(d_hi); t_puts(" want 12600/126000/1260: " as *u8) 59 var ok3: i64 = 1 60 if d_ref != 12600 { ok3 = 0 } 61 if d_lo != 126000 { ok3 = 0 } 62 if d_hi != 1260 { ok3 = 0 } 63 if ok3 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 64 65 // --- T4 the 12D cook IS 12 log reductions, by computation --- 66 total = total + 1 67 let cook: i64 = tp_bot_12d_ms() 68 let logs: i64 = tp_log_reductions_q3(cook, d_ref) 69 t_puts("T4 12D cook=" as *u8); t_putn(cook); t_puts(" ms (2.52 min) delivers logs x1000=" as *u8); t_putn(logs); t_puts(" want 12000: " as *u8) 70 if cook == 151200 { if logs == 12000 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 71 72 // --- T5 THE KEYSTONE: z RECOVERED from the two legal PMO schedules --- 73 // Exact arithmetic on the schedule pair gives 8.8889/log10(120) = 4.275 C. 74 // Our integer path returns 4.279 -- accurate to 0.1%, so the numerics are 75 // sound. That value sits just BELOW the commonly quoted 4.3-5.0 C band for 76 // milk pasteurisation; the gap is in the rounded literature figure, not in 77 // the computation, so the band asserted here is 4.2-5.0 and the label says 78 // so. A test whose printed claim differs from its check is worthless. 79 total = total + 1 80 let z: i64 = tp_pasteurization_z_milli() 81 t_puts("T5 z implied by PMO 145F/30min vs 161F/15s = " as *u8); t_putn(z); t_puts(" milli-C (exact 4275), asserted band 4200..5000, lit 4300..5000: " as *u8) 82 var ok5: i64 = 1 83 if z < 4200 { ok5 = 0 } 84 if z > 5000 { ok5 = 0 } 85 var d5: i64 = z - 4275 86 if d5 < 0 { d5 = 0 - d5 } 87 if d5 > 10 { ok5 = 0 } 88 if ok5 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 89 90 // --- T6 the +5F rule preserves z (regulation is internally coherent) --- 91 total = total + 1 92 let zmix: i64 = tp_implied_z_milli(tp_sched_time_ms(TP_SCHED_MIX_VAT), tp_sched_temp_milli(TP_SCHED_MIX_VAT), tp_sched_time_ms(TP_SCHED_MIX_HTST), tp_sched_temp_milli(TP_SCHED_MIX_HTST)) 93 t_puts("T6 z from the BUMPED ice-cream-mix pair = " as *u8); t_putn(zmix); t_puts(" (same as milk " as *u8); t_putn(z); t_puts("): " as *u8) 94 if zmix == z { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 95 96 // --- T7 implied-z REFUSES a malformed schedule pair --- 97 total = total + 1 98 let z_bad1: i64 = tp_implied_z_milli(15000, 71667, 1800000, 62778) 99 let z_bad2: i64 = tp_implied_z_milli(1800000, 71667, 15000, 62778) 100 let z_bad3: i64 = tp_implied_z_milli(1800000, 62778, 0, 71667) 101 t_puts("T7 reversed/inverted/zero pairs = " as *u8); t_putn(z_bad1); t_puts("/" as *u8); t_putn(z_bad2); t_puts("/" as *u8); t_putn(z_bad3); t_puts(" (all 0): " as *u8) 102 var ok7: i64 = 1 103 if z_bad1 != 0 { ok7 = 0 } 104 if z_bad2 != 0 { ok7 = 0 } 105 if z_bad3 != 0 { ok7 = 0 } 106 if ok7 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 107 108 // --- T8 both legal MILK legs pass their own compliance test --- 109 total = total + 1 110 let c_vat: i64 = tp_pasteurization_compliant(TP_MILK_VAT_TEMP_MILLI, TP_MILK_VAT_TIME_MS, 0) 111 let c_htst: i64 = tp_pasteurization_compliant(TP_MILK_HTST_TEMP_MILLI, TP_MILK_HTST_TIME_MS, 0) 112 let eq_vat: i64 = tp_pasteurization_equiv_ms(TP_MILK_VAT_TEMP_MILLI, TP_MILK_VAT_TIME_MS, 0) 113 t_puts("T8 milk vat compliant=" as *u8); t_putn(c_vat); t_puts(" (equiv " as *u8); t_putn(eq_vat); t_puts(" ms vs 15000 needed) HTST=" as *u8); t_putn(c_htst); t_puts(": " as *u8) 114 if c_vat == 1 { if c_htst == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 115 116 // --- T9 THE +5F RULE, MECHANICALLY: the SAME process is legal for 117 // milk and ILLEGAL for ice cream mix --- 118 total = total + 1 119 let milk_ok: i64 = tp_pasteurization_compliant(TP_MILK_HTST_TEMP_MILLI, TP_MILK_HTST_TIME_MS, 0) 120 let mix_no: i64 = tp_ice_cream_mix_compliant(TP_MILK_HTST_TEMP_MILLI, TP_MILK_HTST_TIME_MS) 121 let mix_ok: i64 = tp_ice_cream_mix_compliant(tp_sched_temp_milli(TP_SCHED_MIX_HTST), TP_MILK_HTST_TIME_MS) 122 t_puts("T9 72C/15s -> milk=" as *u8); t_putn(milk_ok); t_puts(" ice-cream-mix=" as *u8); t_putn(mix_no); t_puts(" ; bumped 74.4C/15s -> mix=" as *u8); t_putn(mix_ok); t_puts(" want 1/0/1: " as *u8) 123 var ok9: i64 = 1 124 if milk_ok != 1 { ok9 = 0 } 125 if mix_no != 0 { ok9 = 0 } 126 if mix_ok != 1 { ok9 = 0 } 127 if ok9 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 128 129 // --- T10 the bumped temps are DERIVED and land on 150F / 166F --- 130 total = total + 1 131 let mv: i64 = tp_sched_temp_milli(TP_SCHED_MIX_VAT) 132 let mh: i64 = tp_sched_temp_milli(TP_SCHED_MIX_HTST) 133 t_puts("T10 mix vat=" as *u8); t_putn(mv); t_puts(" (150F=65556) mix HTST=" as *u8); t_putn(mh); t_puts(" (166F=74444): " as *u8) 134 var d10a: i64 = mv - 65556 135 if d10a < 0 { d10a = 0 - d10a } 136 var d10b: i64 = mh - 74444 137 if d10b < 0 { d10b = 0 - d10b } 138 if d10a <= 2 { if d10b <= 2 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 139 140 // --- T11 an under-process is REFUSED (negative control) --- 141 total = total + 1 142 let weak: i64 = tp_ice_cream_mix_compliant(70000, 15000) 143 let weak2: i64 = tp_pasteurization_compliant(TP_MILK_HTST_TEMP_MILLI, 1000, 0) 144 let zero: i64 = tp_pasteurization_compliant(TP_MILK_HTST_TEMP_MILLI, 0, 0) 145 t_puts("T11 70C/15s mix=" as *u8); t_putn(weak); t_puts(" 72C/1s milk=" as *u8); t_putn(weak2); t_puts(" zero-hold=" as *u8); t_putn(zero); t_puts(" (all 0): " as *u8) 146 var ok11: i64 = 1 147 if weak != 0 { ok11 = 0 } 148 if weak2 != 0 { ok11 = 0 } 149 if zero != 0 { ok11 = 0 } 150 if ok11 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 151 152 // --- T12 F0 at the reference temperature is the hold itself --- 153 total = total + 1 154 let f0_ref: i64 = tp_f0_ms(151200, TP_REF_STERIL_MILLI_C) 155 let f0_cool: i64 = tp_f0_ms(600000, 115000) 156 t_puts("T12 F0 at 121.1C/2.52min=" as *u8); t_putn(f0_ref); t_puts(" ms; 10 min at 115C=" as *u8); t_putn(f0_cool); t_puts(" ms (true ~147300): " as *u8) 157 var ok12: i64 = 1 158 if f0_ref != 151200 { ok12 = 0 } 159 if f0_cool < 144000 { ok12 = 0 } 160 if f0_cool > 150000 { ok12 = 0 } 161 if ok12 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 162 163 // --- T13 bot-cook adequacy: standard F0=3min passes, weak retort fails --- 164 total = total + 1 165 let a_std: i64 = tp_bot_cook_adequate(TP_F0_STANDARD_MS) 166 let a_min: i64 = tp_bot_cook_adequate(151200) 167 let a_weak: i64 = tp_bot_cook_adequate(tp_f0_ms(180000, 118000)) 168 t_puts("T13 adequate at F0=3min:" as *u8); t_putn(a_std); t_puts(" at exactly 12D:" as *u8); t_putn(a_min); t_puts(" at 118C/3min:" as *u8); t_putn(a_weak); t_puts(" want 1/1/0: " as *u8) 169 var ok13: i64 = 1 170 if a_std != 1 { ok13 = 0 } 171 if a_min != 1 { ok13 = 0 } 172 if a_weak != 0 { ok13 = 0 } 173 if ok13 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 174 175 // --- T14 THE BARRIER DECISION, and it shares the 4.6 line --- 176 total = total + 1 177 let b_acid: i64 = tp_barrier_required(4000, 1) 178 let b_bot: i64 = tp_barrier_required(6500, 1) 179 let b_cold: i64 = tp_barrier_required(6500, 0) 180 let line: i64 = CS_HIGH_ACID_PH_MILLI 181 t_puts("T14 pH4.0 shelf-stable=" as *u8); t_putn(b_acid); t_puts(" pH6.5 shelf-stable=" as *u8); t_putn(b_bot); t_puts(" pH6.5 chilled=" as *u8); t_putn(b_cold); t_puts(" (line from nx_chem_solution=" as *u8); t_putn(line); t_puts("): " as *u8) 182 var ok14: i64 = 1 183 if b_acid != TP_BARRIER_ACID { ok14 = 0 } 184 if b_bot != TP_BARRIER_BOT_COOK { ok14 = 0 } 185 if b_cold != TP_BARRIER_REFRIGERATION { ok14 = 0 } 186 if line != 4600 { ok14 = 0 } 187 if ok14 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 188 189 // --- T15 the barrier flips EXACTLY at pH 4.6, not near it --- 190 total = total + 1 191 let just_under: i64 = tp_barrier_required(4599, 1) 192 let exactly: i64 = tp_barrier_required(4600, 1) 193 t_puts("T15 pH 4.599=" as *u8); t_putn(just_under); t_puts(" pH 4.600=" as *u8); t_putn(exactly); t_puts(" (acid then bot-cook): " as *u8) 194 if just_under == TP_BARRIER_ACID { if exactly == TP_BARRIER_BOT_COOK { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 195 196 // --- T16 ICE CREAM MIX end to end: low-acid, chilled, pasteurised --- 197 // A retort would be pointless; treating it as high-acid would be unsafe. 198 total = total + 1 199 let mix_ph: i64 = 6600 200 let adequate: i64 = tp_process_adequate(mix_ph, 0, 0, 1) 201 let unpast: i64 = tp_process_adequate(mix_ph, 0, 0, 0) 202 let as_shelf: i64 = tp_process_adequate(mix_ph, 1, 0, 1) 203 t_puts("T16 mix pH6.6 chilled+pasteurised=" as *u8); t_putn(adequate); t_puts(" unpasteurised=" as *u8); t_putn(unpast); t_puts(" claimed shelf-stable w/o retort=" as *u8); t_putn(as_shelf); t_puts(" want 1/0/0: " as *u8) 204 var ok16: i64 = 1 205 if adequate != 1 { ok16 = 0 } 206 if unpast != 0 { ok16 = 0 } 207 if as_shelf != 0 { ok16 = 0 } 208 if ok16 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 209 210 // --- T17 a shelf-stable low-acid food is adequate ONLY with the cook --- 211 total = total + 1 212 let canned_ok: i64 = tp_process_adequate(6600, 1, TP_F0_STANDARD_MS, 0) 213 let canned_no: i64 = tp_process_adequate(6600, 1, 100000, 1) 214 let sorbet: i64 = tp_process_adequate(3500, 1, 0, 1) 215 t_puts("T17 low-acid canned F0=3min=" as *u8); t_putn(canned_ok); t_puts(" under-cooked+pasteurised=" as *u8); t_putn(canned_no); t_puts(" pH3.5 sorbet pasteurised=" as *u8); t_putn(sorbet); t_puts(" want 1/0/1: " as *u8) 216 var ok17: i64 = 1 217 if canned_ok != 1 { ok17 = 0 } 218 if canned_no != 0 { ok17 = 0 } 219 if sorbet != 1 { ok17 = 0 } 220 if ok17 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 221 222 // --- T18 lethality is MONOTONE in both temperature and time --- 223 total = total + 1 224 var tt: i64 = 60000 225 var prev: i64 = 0 - 1 226 var ok18: i64 = 1 227 while tt <= 90000 { 228 let e: i64 = tp_pasteurization_equiv_ms(tt, 15000, 0) 229 if e < prev { ok18 = 0 } 230 prev = e 231 tt = tt + 2000 232 } 233 let short: i64 = tp_pasteurization_equiv_ms(72000, 10000, 0) 234 let long: i64 = tp_pasteurization_equiv_ms(72000, 20000, 0) 235 if long <= short { ok18 = 0 } 236 t_puts("T18 equivalent lethality monotone in temp (60..90 C) and in hold: " as *u8) 237 if ok18 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 238 239 t_puts("THERMAL-PROCESS-GATE passed " as *u8); t_putn(pass); t_puts("/" as *u8); t_putn(total) 240 if pass == total { t_puts(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 241 t_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 242}