code wiki / (root) / nx_ice_recrystal_test.nx

nx_ice_recrystal_test.nx source

↩ module page · 191 lines · 10819 B

1// nx_ice_recrystal_test.nx -- gate for ice recrystallisation. 2// 3// THE LIAR-KILL IS THE CUBE-LAW ROUND TRIP (T2/T3): recover k from a pair of 4// observed crystal sizes, then run the model forward and land back on the 5// observation it was recovered from. A wrong exponent or a wrong cube root 6// cannot survive that, because both directions use the full arithmetic. 7// 8// THE FINDING IS T7: two storage histories with the SAME mean temperature 9// give different ice cream. It is measured, with a flat-history control 10// (T8) proving the difference comes from the cycling and not from the model 11// adding damage to everything it touches. 12// expect_exit: 0 license_tier: ORIGINAL 13import "nx_syscalls.nx" 14import "nx_icecream.nx" 15import "nx_ice_recrystal.nx" 16 17func tw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 18func tn(v: i64) -> i64 { let b: *u8 = sys_mmap(24); var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } let t: *u8 = sys_mmap(24); 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 { b[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, b, k); return 0 } 19func iabs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 20 21func main() -> i64 { 22 var pass: i64 = 0 23 var total: i64 = 0 24 25 // --- T1 The integer cube root is EXACT where it must be, and it is the 26 // inverse of the cube on real values rather than near them. --- 27 total = total + 1 28 var ok1: i64 = 1 29 if ir_icbrt(0) != 0 { ok1 = 0 } 30 if ir_icbrt(1000) != 10 { ok1 = 0 } 31 if ir_icbrt(15625000) != 250 { ok1 = 0 } 32 if ir_icbrt(125000000) != 500 { ok1 = 0 } 33 if ir_icbrt(ir_cube(371)) != 371 { ok1 = 0 } 34 // and it FLOORS rather than rounding up: one below a perfect cube must 35 // not report the cube's root, or a size just under threshold reads over. 36 if ir_icbrt(ir_cube(371) - 1) != 370 { ok1 = 0 } 37 tw("T1 integer cube root exact on perfect cubes and floors below them: " as *u8) 38 if ok1 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 39 40 // --- T2 ***THE ROUND TRIP*** Recover k from an observation, run the 41 // model forward over the same interval, land on the observation. --- 42 total = total + 1 43 let k_obs: i64 = ir_k_from_observation(250, 400, 90) 44 let fwd: i64 = ir_size_isothermal(250, k_obs, 90) 45 tw("T2 k recovered from 25um->40um over 90d = " as *u8); tn(k_obs) 46 tw("; model forward 90d returns " as *u8); tn(fwd); tw(" (want 400): " as *u8) 47 if iabs(fwd - 400) <= 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 48 49 // --- T3 NON-VACUITY of T2: the cube law is not a straight line. At 50 // HALF the time the diameter must be well ABOVE the halfway 51 // point, because growth in diameter decelerates as D^(1/3). 52 // A linear model would put it at 325. --- 53 total = total + 1 54 let half: i64 = ir_size_isothermal(250, k_obs, 45) 55 tw("T3 at half the time D=" as *u8); tn(half) 56 tw(" -- linear would say 325, cube law says >340 (growth decelerates): " as *u8) 57 var ok3: i64 = 1 58 if half <= 340 { ok3 = 0 } 59 if half >= 400 { ok3 = 0 } 60 if ok3 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 61 62 // --- T4 k REFUSES an impossible observation. Crystals do not 63 // un-ripen, so a shrinking measurement is an error in the data, 64 // and turning it into a negative rate would produce a shelf life 65 // that grows with time. --- 66 total = total + 1 67 var ok4: i64 = 1 68 if ir_k_from_observation(400, 250, 90) != IR_INVALID { ok4 = 0 } 69 if ir_k_from_observation(250, 250, 90) != IR_INVALID { ok4 = 0 } 70 if ir_k_from_observation(250, 400, 0) != IR_INVALID { ok4 = 0 } 71 if ir_k_is_recovered(k_obs) != 1 { ok4 = 0 } 72 if ir_k_is_recovered(IR_K_DEFAULT) != 0 { ok4 = 0 } 73 tw("T4 shrinking/static/zero-time observations REFUSED, and recovered-vs-default k is distinguishable: " as *u8) 74 if ok4 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 75 76 // --- T5 ***THE COMPOSITION*** The cycled ice fraction comes out of the 77 // freezing curve this lane already validated, not out of a new 78 // constant. A warm excursion must melt a real, positive share of 79 // the ice, and a deeper excursion must melt more. --- 80 total = total + 1 81 let m: *NxIceMix = nx_ice_mix_new() 82 m.fat_g = 120 83 m.msnf_g = 100 84 m.sugar_g = 150 85 m.total_g = 1000 86 let cyc_small: i64 = ir_cycled_fraction_permil(m, 0 - 20000, 0 - 16000) 87 let cyc_big: i64 = ir_cycled_fraction_permil(m, 0 - 20000, 0 - 10000) 88 tw("T5 cycled ice fraction from the freezing curve: -20/-16C = " as *u8); tn(cyc_small) 89 tw(" permil, -20/-10C = " as *u8); tn(cyc_big); tw(" permil: " as *u8) 90 var ok5: i64 = 1 91 if cyc_small <= 0 { ok5 = 0 } 92 if cyc_big <= cyc_small { ok5 = 0 } 93 if cyc_big >= 1000 { ok5 = 0 } 94 if ok5 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 95 96 // --- T6 A cycle that does not warm melts nothing. This is the control 97 // that keeps T5 meaningful: the fraction tracks the excursion, 98 // it is not a constant the model applies regardless. --- 99 total = total + 1 100 var ok6: i64 = 1 101 if ir_cycled_fraction_permil(m, 0 - 20000, 0 - 20000) != 0 { ok6 = 0 } 102 if ir_cycled_fraction_permil(m, 0 - 16000, 0 - 20000) != 0 { ok6 = 0 } 103 tw("T6 a non-excursion melts nothing (equal and inverted ends both 0): " as *u8) 104 if ok6 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 105 106 // --- T7 ***THE FINDING, MEASURED.*** Two 60-day histories with the 107 // SAME mean temperature: one steady at -18 C, one cycling 108 // -20/-16 C twice a week. Chemically they are near-identical. 109 // As ice cream they are not, and the model must say so. --- 110 total = total + 1 111 let steady: i64 = ir_size_isothermal(250, k_obs, 60) 112 let cycled: i64 = ir_size_combined(250, k_obs, 60, cyc_small, 17) 113 tw("T7 60 days, SAME mean temperature: steady -18C -> D=" as *u8); tn(steady) 114 tw(" cycling -20/-16C x17 -> D=" as *u8); tn(cycled) 115 tw(" (0.1um); grades " as *u8); tn(ir_texture_grade(steady)) 116 tw(" vs " as *u8); tn(ir_texture_grade(cycled)); tw(": " as *u8) 117 var ok7: i64 = 1 118 if cycled <= steady { ok7 = 0 } 119 // the difference must be large enough to matter, not a rounding artefact 120 if (cycled - steady) * 100 / steady < 5 { ok7 = 0 } 121 if ir_texture_grade(cycled) <= ir_texture_grade(steady) { ok7 = 0 } 122 if ok7 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 123 124 // --- T8 NEGATIVE CONTROL for T7. Zero-amplitude cycling must reduce 125 // EXACTLY to the isothermal answer. Without this, T7 could be 126 // passing because the model adds damage to any history handed to 127 // it, which would make the finding an artefact. --- 128 total = total + 1 129 let flat: i64 = ir_size_combined(250, k_obs, 60, 0, 17) 130 tw("T8 zero-amplitude cycling reduces to isothermal: " as *u8); tn(flat) 131 tw(" vs " as *u8); tn(steady); tw(": " as *u8) 132 if flat == steady { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 133 134 // --- T9 MANY SMALL EXCURSIONS BEAT ONE BIG ONE, because the damage 135 // compounds per cycle. This is the operationally useful claim: 136 // a cabinet that cycles often is worse than one that occasionally 137 // goes warmer, and the model must reproduce that ordering. --- 138 total = total + 1 139 let many_small: i64 = ir_size_after_cycles(250, cyc_small, 20) 140 let one_big: i64 = ir_size_after_cycles(250, cyc_big, 1) 141 tw("T9 twenty small excursions D=" as *u8); tn(many_small) 142 tw(" vs one large excursion D=" as *u8); tn(one_big); tw(" -- many small is worse: " as *u8) 143 if many_small > one_big { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 144 145 // --- T10 The sensory verdict is FAIL-CLOSED and the thresholds order 146 // correctly. An unusable diameter must read COARSE: reporting 147 // "smooth" for a state we cannot evaluate is the single answer 148 // that would ship a defective product. --- 149 total = total + 1 150 var ok10: i64 = 1 151 if ir_texture_grade(250) != 0 { ok10 = 0 } 152 if ir_texture_grade(IR_DETECT_D) != 1 { ok10 = 0 } 153 if ir_texture_grade(IR_COARSE_D) != 2 { ok10 = 0 } 154 if ir_texture_grade(IR_INVALID) != 2 { ok10 = 0 } 155 if ir_texture_grade(0) != 2 { ok10 = 0 } 156 tw("T10 texture grade ordered and fail-closed (unknown reads COARSE, never smooth): " as *u8) 157 if ok10 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 158 159 // --- T11 Shelf life in both currencies -- days of steady storage, and 160 // number of excursions -- and each must agree with the forward 161 // model at its own answer rather than being a separate formula. --- 162 total = total + 1 163 let days_ok: i64 = ir_days_to_detectable(250, k_obs) 164 let cyc_ok: i64 = ir_cycles_to_detectable(250, cyc_small) 165 tw("T11 steady storage lasts " as *u8); tn(days_ok) 166 tw(" days; survives " as *u8); tn(cyc_ok); tw(" excursions: " as *u8) 167 var ok11: i64 = 1 168 if days_ok <= 0 { ok11 = 0 } 169 if cyc_ok <= 0 { ok11 = 0 } 170 if ir_size_isothermal(250, k_obs, days_ok) < IR_DETECT_D - 2 { ok11 = 0 } 171 if ir_size_after_cycles(250, cyc_small, cyc_ok) < IR_DETECT_D - 2 { ok11 = 0 } 172 if ir_size_isothermal(250, k_obs, days_ok - 1) >= IR_DETECT_D { ok11 = 0 } 173 if ok11 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 174 175 // --- T12 ***THE HONEST BOUNDARY.*** MKT is the right tool for chemical 176 // degradation and the WRONG tool here, because collapsing a 177 // history to one temperature discards the oscillation that IS the 178 // mechanism. Declared as a function so no planner can feed an 179 // MKT in and trust the answer. The asserted coupling is likewise 180 // flagged rather than buried. --- 181 total = total + 1 182 var ok12: i64 = 1 183 if ir_mkt_describes_recrystallisation() != 0 { ok12 = 0 } 184 if ir_coupling_is_asserted() != 1 { ok12 = 0 } 185 tw("T12 MKT declared INAPPLICABLE to recrystallisation, geometric coupling declared ASSERTED: " as *u8) 186 if ok12 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 187 188 tw("ICE-RECRYSTAL-GATE passed " as *u8); tn(pass); tw("/" as *u8); tn(total) 189 if pass == total { tw(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 190 tw(" verdict=RED\n" as *u8); sys_exit(1); return 1 191}