==== Part A: the CAS templates in desktop Giac, against fractions, SymPy and statsmodels ====
PASS snoring, as entered                          SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 95339/4757909, a = 80277/4757909
PASS snoring, 1,2,3                               SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 53492/1511327, a = -51775/3022654
PASS snoring, midranks                            SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 109323/2069801372, a = -33598615/1574215128
PASS sleep U (made up), as entered                SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 1/120, a = 59/720
PASS sleep U (made up), 1,2,3                     SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 1/120, a = 83/720
PASS sleep U (made up), midranks                  SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 17/729600, a = 540589/4377600
PASS convictions (made up), as entered            SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 373/4380, a = 199/1095
PASS convictions (made up), 1,2,3                 SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 373/4380, a = 141/1460
PASS convictions (made up), midranks              SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 2639/7342500, a = 4480753/44055000
PASS scores with decimals 0.5, 1.25, 2.125, 4     SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 14173/119130, a = 24295/190608
PASS negative scores -2 .. 2                      SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 163/1921, a = 919/3842
PASS two levels with the same p (ties)            SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 1/20, a = 1/30
PASS a zero cell                                  SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 1/4, a = -1/4
PASS two levels                                   SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 9/50, a = -3/50
PASS six levels, large prime totals               SSE ok, d/da d/db ok, solve ok, WLS = OLS(0/1) = exact ok   b = 608752/21894613, a = 1627669/21894613
PASS a score of 1/3 (more than six decimals): no CAS text at all
PASS the longest call (solve: the SSE twice) fits trCasMx = 4000   SSE text up to 167 characters
PASS 3a: in the Prime's printing every solve answer is {[a,b]} (CASProbe T36), the SSE and its derivatives are unchanged   ['{[80277/4757909,95339/4757909]}']

==== Part B: the PPL program in the harness (ppl_run_v3d) ====
PASS trCxMake builds exactly this SSE text, 15 tables
PASS the PPL reads solve's answer: a and b = the exact fractions, every table   []
PASS the program's trB, trA0 = the exact fractions to 1e-12 (the decimals beside them)
PASS page 4 (Giac), snoring: badge, SSE, both derivatives, both fractions beside the decimals   ['Least squares as calculus', 'exact (CAS)', 'The plotted line makes this SSE smallest:', 'SSE = sum n(p - a - bx)^2; p = cases/n, x = score', 'SSE = 2484×a^2+6796×a×b-220×a+12310×b^2-608×b', '+63080529409/7933174634', 'Smallest where both slopes are 0 (CAS diff):', 'dSSE/da = 4968×a+6796×b-220 = 0', 'dSSE/db = 6796×a+24620×b-608 = 0', 'Solved exactly (CAS solve), beside the decimals:', 'b = 95339/4757909   (program 0.02003800)', 'a = 80277/4757909   (program 0.01687233)', "= least squares on all N people's 0/1 outcomes;", 'the trend χ² asks whether this b is 0.', '4/4, ⇆ or tap = page, Esc = menu']
PASS page 4: four CAS calls, all answered
PASS 3a: every text asks for string(...), every answer quoted, the solve in braces; no CAS variable assigned, no CAS call of a variable   "{[80277/4757909,95339/4757909]}"
PASS no text below y 206 on page 4 (the soft keys are at 218)
PASS page 4 twice: 4 CAS calls, not 8 (the answers are kept)   4
PASS Scores key on page 4 (twice, to midranks): asked again, exact midrank line, the note   ['b = 109323/2069801372   (program 0.0000528181)', 'a = -33598615/1574215128   (program -0.02134309)', "= least squares on all N people's 0/1 outcomes;", 'the trend χ² asks whether this b is 0.', 'Exact: score 1698.5 = 16985/10; nothing rounded.', '4/4, ⇆ or tap = page, Esc = menu']
PASS cas=fail: decimal badge, the normal equations with the program's sums, no error   ['Least squares as calculus', 'decimal', 'The plotted line makes this SSE smallest:', 'SSE = sum n(p - a - bx)^2; p = cases/n, x = score', 'The CAS did not answer: no exact fractions.', 'Both slopes of the SSE = 0, the normal equations:', 'N a + (sum n x) b = A', '  2484 a + 3398 b = 110', '(sum n x) a + (sum n x^2) b = sum x a', '  3398 a + 12310 b = 304', "Solving them (the program's decimals):", '  b = 0.02003800,  a = 0.01687233', "= least squares on all N people's 0/1 outcomes;", 'the trend χ² asks whether this b is 0.', '4/4, ⇆ or tap = page, Esc = menu']
PASS a drag on plot 1 (level 3 down): counts changed, NO CAS call during it   cases [24, 35, 9, 30], 4 calls
PASS page 4 after the drag: the dragged table's exact line   ['b = 5825/365993   (program 0.01591560)']
PASS Restore on page 4: the first table's line again
PASS a score of 1/3: "needs over 6 decimals", the decimal page, no CAS call
PASS stale-key run: menu, example, pages 1-4, Enter on page 4 = menu, quit; nothing skipped   result 'TrendTest: X2 trend = 72.6588 (1 df), p < 0.0001; Pearson X2 = 72.7821 (3 df)', skips []
PASS 3a: beta 3 in v3b: page 4 says "decimal" and gives no fractions -- the emulator's finding

G. beta 3b against beta 3a
PASS beta 3b: routines that differ from beta 3a: ['trDot', 'trExamples'] (want trDot, trExamples)
PASS beta 3b: outside those routines only comments differ
PASS beta 3b: the #PYTHON blocks are unchanged
  routines that differ from beta 3a: trDot, trExamples; everything else differs only in comments

W. 3b: the Examples lines fit; solid dots
PASS 3b: every CHOOSE line shown is 30 characters or fewer (51 seen, longest 29)
PASS 3b: the shortened Examples lines are shown
PASS 3b: the three examples still load the same data and labels: [(4, [24, 35, 21, 30], 'Example: snoring and heart disease'), (5, [38, 40, 36, 44, 44], 'ILLUSTRATIVE (made-up counts): sleep, illness'), (4, [72, 66, 51, 88], 'ILLUSTRATIVE (made up): convictions, re-arrest')]
PASS 3b: every CHOOSE line in the source is 30 characters or fewer (longest 29)
PASS 3b: trDot = three RECT_P covering exactly a radius-3 disc (37 px)
PASS 3b: the only ARC_P left are the reference circle (r 3) and the selection ring (r 6, 7): [3.0]
PASS 3b: at most one small ARC_P (the hollow reference) per frame (1 in 2 frames)
  Examples lines seen: ['Snoring and heart disease', 'Sleep: a U shape (made up)', 'Prior convictions (made up)']

R. 3c: the second emulator round (harness v3d)
PASS R1 trRepl('{[1,2]}', '}', '') = '{[1,2]': the pattern at the END (solve's closing brace)
PASS R1 trRepl('{[1,2]}', '{', '') = '[1,2]}': the pattern at the START
PASS R1 trRepl('abc', 'abc', '') = '': the pattern is the WHOLE text
PASS R1 trRepl('abc', 'abc', 'xyz') = 'xyz': the whole text, replaced
PASS R1 trRepl('abc', '', 'X') = 'abc': an EMPTY pattern: the text unchanged (no endless loop)
PASS R1 trRepl('', 'a', 'b') = '': an EMPTY text
PASS R1 trRepl('tra*trb*tra', 'tra', 'a') = 'a*trb*a': twice, at both ends
PASS R1 trRepl('2*a*b', '*', '×') = '2×a×b': in the middle, twice
PASS R1 trRepl('}}}', '}', '') = '': only the pattern, three times
PASS R1 beta 3b in v3d: its trRepl stops with the INSTRING error whenever the text runs out -- the pattern at the end, the whole text, an empty text, only the pattern ([('{[1,2]}', '}'), ('abc', 'abc'), ('', 'a'), ('}}}', '}')]); a pattern at the start works -- the emulator's page-4 crash
PASS R2 trCas with the CAS failing: "" and no error (got '')
PASS R2 trCxClean("") = "" (no INSTRING on the empty text)
PASS R2 trCxSol on 10 answers (empty, {}, [], "tra=" with nothing after it, two solutions, the Prime's and Giac's forms): the right halves, no error []
PASS R2 beta 3b in v3d: trCas with the CAS failing stops (PPLError: INSTRING: Invalid input (the first argument is an empty string -- emulator, 17 Oct 2026)) -- every fallback was broken
PASS R3 Snoring, results page 4 with the CAS: opens, "exact (CAS)", b = 95339/4757909, a = 80277/4757909, quit returns the summary (None)
PASS R3 beta 3b, the same run in v3d: the CAS answers all 4 calls, then "PPLError: INSTRING: Invalid input (the first argument is an " -- the emulator's finding
PASS R4 cas=fail: page 4 shows "decimal" and the normal equations, no error (None)
PASS R4 beta 3b, the same cas=fail run in v3d: stops (PPLError: INSTRING: Invalid input (the first argument is an )
PASS R5 canned, solve gives {} (no solution): beta 3d the decimal page, no error; beta 3b: PPLError: INSTRING: Invalid input (the first argum
PASS R5 canned, solve gives {[tra=,trb=1/2]}: beta 3d the decimal page, no error; beta 3b: PPLError: INSTRING: Invalid input (the first argum
PASS R5 canned, every answer an empty string: beta 3d the decimal page, no error; beta 3b: PPLError: INSTRING: Invalid input (the first argum
PASS R5 canned, the Prime's own answers (control): beta 3d the exact page, no error; beta 3b: PPLError: INSTRING: Invalid input (the first argum
PASS R6 the proportions plot, snoring level 1: 'L1: x = 0, 24/1379 = 0.017, line 0.017 (+0.1 SE)', no scientific form
PASS R6 beta 3b, the same run: 'L1: x = 0, 24/1379 = 0.017, line 0.017 (+9.6ᴇ-2 SE)' -- the emulator's line, character for character
PASS R7 Help, Esc on page 1: back to the menu (pages seen [1])
PASS R7 Help, a key then Esc on page 2: back to the menu (pages seen [1, 2])
PASS R7 Help, any keys: pages 1, 2, 3, then the menu ([1, 2, 3])
PASS R7 the help footers say it: [['any key = page 2, Esc = menu'], ['any key = page 3, Esc = menu'], ['any key = menu']]
PASS R7 beta 3b: Esc on help page 1 turned the page (pages seen [1, 2, 3], the idle Esc then turns the rest) -- the emulator's finding
PASS R8 snoring (the emulator's table): every page, both plots at every level, Scores, page 4 (CAS and cas=fail), Quit -- no error, no small number in scientific form (98 frames)
PASS R8 small doses 0, 0.001, 0.005, 0.01: every page, both plots at every level, Scores, page 4 (CAS and cas=fail), Quit -- no error, no small number in scientific form (98 frames)
PASS R8 a dragged extreme: 50/50, 0/50, 0/50 (OR 0.0001): every page, both plots at every level, Scores, page 4 (CAS and cas=fail), Quit -- no error, no small number in scientific form (94 frames)
PASS R8 a rare outcome: 1, 2, 3 of 20000 each: every page, both plots at every level, Scores, page 4 (CAS and cas=fail), Quit -- no error, no small number in scientific form (94 frames); from 1E12 up: ['7.00018ᴇ13']
PASS R8 almost flat: 100, 101, 100 of 1000: every page, both plots at every level, Scores, page 4 (CAS and cas=fail), Quit -- no error, no small number in scientific form (94 frames)
PASS R8 midranks on a million people: every page, both plots at every level, Scores, page 4 (CAS and cas=fail), Quit -- no error, no small number in scientific form (94 frames); from 1E12 up: ['3.20000ᴇ17']
PASS R8 scores -2/3, 1/3, 4/3 (sum n x cancels): every page, both plots at every level, Scores, page 4 (CAS and cas=fail), Quit -- no error, no small number in scientific form (94 frames)
PASS R8 beta 3b, the same run (pages 1-3 and the plots; its page 4 stops): scientific forms ['1.00ᴇ-3', '5.00ᴇ-3', '3.8ᴇ-2', '7.4ᴇ-2']
PASS R8 beta 3b, the same run (pages 1-3 and the plots; its page 4 stops): scientific forms ['1.908ᴇ-6', '9.803ᴇ-5']
PASS R8 beta 3b, the same run (pages 1-3 and the plots; its page 4 stops): scientific forms ['3.8ᴇ-16', '9.6ᴇ-17', '2.500000ᴇ-9', '8.826ᴇ-6', '1.67ᴇ-5', '1.667ᴇ-5']
PASS R8 beta 3b, the same run (pages 1-3 and the plots; its page 4 stops): scientific forms ['7.39ᴇ-3', '3.5ᴇ-2', '7.0ᴇ-2']
PASS R8 beta 3b, the same run (pages 1-3 and the plots; its page 4 stops): scientific forms ['4.285714ᴇ-9']
PASS R8 beta 3b, the same run (pages 1-3 and the plots; its page 4 stops): scientific forms ['8.42ᴇ-3', '8.48ᴇ-3', '1.6ᴇ-2', '4.5ᴇ-2', '5.0ᴇ-2', '8.6ᴇ-2']
PASS R8 beta 3b, the same run (pages 1-3 and the plots; its page 4 stops): scientific forms ['6.4ᴇ-2', '8.1ᴇ-2', '9.6ᴇ-2']
PASS R9 every STRING(x, 2, d) left with d > 0 (14) is a justified site; the rest go through trFx (29 calls): extra [], missing []
PASS R9 every STRING(x, 2, 0) prints a whole number (counts, levels, df, lengths; doV >= 1, xsV and nmV in their FP = 0 branches): ['DIM(clT)', 'cfN', 'cqDf', 'doV', 'enK', 'nmV', 'rsI', 'trA(rsI)', 'trA(trSel)', 'trDFD', 'trK', 'trK - 1', 'trSel', 'trSum(enA)', 'trSum(enN)', 'trTot(rsI)', 'trTot(trSel)', 'xsV']
PASS R9 INSTRING first arguments: ['"0123456789"', 'cR', 'ccR', 'cxX', 'rnR', 'rpS', 'soS', 'trCxSa', 'trCxSb']
PASS R9 the guards are in place: cR (trCas), rpS and rpO (trRepl), ccR (trCxClean), soS and the halves (trCxSol); cxX is never empty (trCxMake returns first), rnR always holds a "/" (trCxMake calls trRatNote only then), trCxSb is non-empty (soK < DIM(soS)) []
  LEFT / MID / RIGHT calls in the code (counts and starts reviewed by hand, see the CHANGES): 6 / 15 / 0
PASS G 3c: routines that differ from beta 3b (comments included): ['TrendTest', 'trCas', 'trChiPanel', 'trCxClean', 'trCxSol', 'trDec', 'trDrawProp', 'trFx', 'trHelp', 'trNum', 'trOrStr', 'trPage1', 'trPageChi', 'trRepl', 'trWaitAny', 'trXStr']
PASS G 3c: beta 3b has 50 routines; the other 35 are byte-identical in beta 3c, comments included; one new (trFx), none removed
PASS G 3c: outside those routines only comments differ (the header)
PASS G 3c: trCas = beta 3b's with only the brief's DIM guard (the IFERR line and the quote strip byte-identical)
PASS G 3c: trRepl is the brief's replace loop, line for line
PASS G 3c: no #PYTHON block, as before

D. 3d: TR_P as a lower tail (improvement 1), C-T1 to C-T4
PASS D1 12-digit model, snoring: beta 3d TR_P = 1.54116e-17, chi2.sf(72.6588, 1) = 1.54116e-17 (relative error 2.0e-13)
PASS D1 12-digit model, snoring: beta 3c TR_P = 0.0 -- 1 - CHISQUARE_CDF is exactly 0 (the review's finding)
PASS D1 12-digit model, 31 tables with X2 from 0 to 168: beta 3d trPT = chi2.sf to 4.0e-12 relative, every one
PASS D1 the same tables in beta 3c: 16 of 31 give p = exactly 0 (the largest X2 with p > 0 was 47.6)
PASS D1 double precision, 200 random tables: beta 3d trPT = chi2.sf to 5.5e-14 relative
PASS D1 the same 200 tables: every displayed trend p (trPStr) is the same string in 3d as in 3c []
PASS D2 trPT is set once, as 2 * NORMALD_CDF(0, 1, -(trX2T^0.5)); TR_P := trPT; no CHISQUARE_CDF(1, ...) left
PASS D2 trPStr is only ever given trPT (trend), trPP (Pearson), trPD (departure) or the panel's cqPv: ['cqPv', 'psP', 'trPD', 'trPP', 'trPT']
PASS D2 page 2's trend panel is handed trPT: ['14, 154, 1, trX2T, trPT, trMAG, "trend"', '170, 310, trDFD, trX2D, trPD, trGRN, "de']
PASS D2 Examples > Snoring > Quit: 'TrendTest: X2 trend = 72.6588 (1 df), p < 0.0001; Pearson X2 = 72.7821 (3 df)'; TR_P = 1.54116e-17
PASS D3 C-T1, the sleep example by the program's own trCompute: sum of squared residuals 53.1141 = departure 53.1149 - trend/(N-1) 0.000768
PASS D3 C-T1: the header says the residuals sum to Pearson - Armitage's N r^2, the departure shown is Pearson - Mantel's, with 53.1149 and 53.1141
PASS D3 beta 3c's header said "the squared residuals add up to Pearson - Armitage, i.e. the departure statistic"
PASS D4 C-T2: help page 3's last five lines as drawn: ['Same test elsewhere: Epi Info StatCalc prints', "Mantel's (1963) (N-1) r^2, as this page does;", 'R prop.trend.test and SAS PROC FREQ / TREND', "print Armitage's (1955) N r^2: 72.688 for", 'snoring, 72.659 here.  The ratio is N/(N-1).']
PASS D4 72.688 is the program's own trend for snoring, 72.658824, times N/(N-1) = 2484/2483: 72.688087
PASS D4 the five new lines are at most 46 characters (longest 45; the page's longest line 46)
PASS D4 page 1's headline is still Mantel's: "Trend (1 df):  χ² = 72.659"
PASS D4 beta 3c's page 3 named R and SAS without saying they print N r^2: ["trend'; R prop.trend.test(x, n, score);", 'SAS PROC FREQ / TREND; JMP Cochran-Armitage.']
PASS D5 C-T3: the main menu's third line is 'Plot: proportions, trend line' (29 characters; the longest line 29)
PASS D5 item 3 still opens the proportions plot (16 frames of it)
PASS D5 beta 3c showed 'Plot: proportions + trend line' (30 characters)
PASS D6 C-T4, 24.5 cases: refused with "Cases and totals count people: whole numbers only.", nothing loaded ('TrendTest: no table')
PASS D6 beta 3c accepted 24.5 cases: 'TrendTest: X2 trend = 5.5486 (1 df), p = 0.0185; P'
PASS D6 C-T4, a total of 100.5: refused with "Cases and totals count people: whole numbers only.", nothing loaded ('TrendTest: no table')
PASS D6 whole counts with fractional scores (0.5, 1.5, 2.5) are still accepted: 'TrendTest: X2 trend = 5.9294 (1 df), p = 0.0149; Pearson X2 '
  check_TrendTest.py on beta 3d: 66 checks passed, 3 failed; 9 notes; failed: ['D1 the help page says the two differ by N/(N-1)', 'E2 the three p-values are 1 - CHISQUARE_CDF', 'F6 banner says beta 3c']
  check_TrendTest.py on beta 3c: 69 checks passed, 0 failed; 9 notes; failed: []
PASS D7 the review's check_TrendTest.py on beta 3d: every check passes but the three that test the text 3d changes on purpose (C-T2's line, improvement 1's site, the banner): ['D1 the help page says the two differ by N/(N-1)', 'E2 the three p-values are 1 - CHISQUARE_CDF', 'F6 banner says beta 3c']
PASS D7 on beta 3c: 69 checks passed, 0 failed; 9 notes
PASS G 3d: routines that differ from beta 3c (comments included): ['TrendTest', 'trCompute', 'trEnter', 'trHelp'] (want TrendTest, trCompute, trEnter, trHelp)
PASS G 3d: the other 47 routines are byte-identical, comments included; none added or removed
PASS G 3d: trCompute = beta 3c's with the one trPT line replaced (and its comment)
PASS G 3d: trEnter = beta 3c's plus the FP check, its message and a comment
PASS G 3d: trHelp = beta 3c's with page 3's five gray lines replaced (and a comment)
PASS G 3d: TrendTest = beta 3c's with the one menu line (and a comment)
PASS G 3d: outside those routines only comments differ (the header, notes)
PASS G 3d: banner beta 3d; no #PYTHON block
PASS G 3d: the header carries Roger's DISCLAIMER line under the terms; no on-screen string states the terms, so no page needs "Teaching aid; not for legal use."
  routines that differ from beta 3c: TrendTest, trCompute, trEnter, trHelp; 47 byte-identical
PASS R10 h.sci_strings over all 296 beta 3d runs: 0 small (must be 0); from 1E12 up only trNum's six significant digits on the decimal page: ['3.20000ᴇ17', '7.00018ᴇ13']
  beta 3d sci_strings (every run): ['7.00018ᴇ13', '3.20000ᴇ17']
  the older versions' sci_strings in this script (51 distinct): ['1.000ᴇ-4', '1.00ᴇ-3', '1.500ᴇ-4', '1.667ᴇ-5', '1.67ᴇ-5', '1.6ᴇ-2', '1.908ᴇ-6', '2.500000ᴇ-9', '2.742ᴇ-5', '2.832ᴇ-4', '3.5ᴇ-2', '3.646ᴇ-4', '3.8ᴇ-16', '3.8ᴇ-2', '4.285714ᴇ-9', '4.410ᴇ-4', '4.44ᴇ-3', '4.5ᴇ-2', '4.7ᴇ-2', '5.0000ᴇ-5']

PASS: 134 checks, 0 failure(s), 0 skipped
