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}