nx_beneish_gate.nx source
↩ module page · 97 lines · 5344 B
1// nx_beneish_gate.nx -- BENEISH M-SCORE GATE (forensic manipulation detection).
2// Proves the exact M-score on a benign firm and a manipulator, the -1.78 threshold classification, and
3// the fail-closed composition: a missing index yields INCOMPLETE, never a fraud accusation on partial data.
4// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
5
6import "nx_beneish_lib.nx"
7
8func bg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
9func bg_putn(v: i64) -> i64 {
10 let t: *u8 = sys_mmap(32)
11 var o: i64 = 0
12 var m: i64 = v
13 if m < 0 { t[o] = 45 as u8; o = o + 1; m = 0 - m }
14 let d: *u8 = sys_mmap(32)
15 var k: i64 = 0
16 if m == 0 { d[0] = 48 as u8; k = 1 }
17 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
18 var i: i64 = 0
19 while i < k { t[o] = d[k - 1 - i]; o = o + 1; i = i + 1 }
20 sys_write(1, t, o)
21 return 0
22}
23func bg_ck(cnt: *i64, name: *u8, got: i64, want: i64) -> i64 {
24 if got == want {
25 cnt[0] = cnt[0] + 1
26 bg_puts(" PASS " as *u8); bg_puts(name); bg_puts(" = " as *u8); bg_putn(got); bg_puts("\n" as *u8)
27 return 1
28 }
29 cnt[1] = cnt[1] + 1
30 bg_puts(" FAIL " as *u8); bg_puts(name); bg_puts(" got " as *u8); bg_putn(got)
31 bg_puts(" want " as *u8); bg_putn(want); bg_puts("\n" as *u8)
32 return 0
33}
34func bg_ckstr(cnt: *i64, name: *u8, got: *u8, want: *u8) -> i64 {
35 if mt_streq(got, want) == 1 {
36 cnt[0] = cnt[0] + 1
37 bg_puts(" PASS " as *u8); bg_puts(name); bg_puts(" = " as *u8); bg_puts(got); bg_puts("\n" as *u8)
38 return 1
39 }
40 cnt[1] = cnt[1] + 1
41 bg_puts(" FAIL " as *u8); bg_puts(name); bg_puts(" got " as *u8); bg_puts(got); bg_puts("\n" as *u8)
42 return 0
43}
44
45func main(argc: i64, argv: *i64) -> i64 {
46 let cnt: *i64 = sys_mmap(16) as *i64
47 cnt[0] = 0
48 cnt[1] = 0
49
50 bg_puts("NISHI-BENEISH-GATE (M-score forensic manipulation detection, integer-exact)\n" as *u8)
51
52 // ---- M1: a BENIGN firm -- all indices = 1.000 (1000 bp), zero accruals. M x1000 = -2480. ----
53 // hand-check: -4840000 + (920+528+404+892+115-172-327)*1000 + 4679*0 = -4840000 + 2360000 = -2480000; /1000 = -2480.
54 let bm: i64 = ben_mscore(1000, 1000, 1000, 1000, 1000, 1000, 0, 1000)
55 bg_ck(cnt, "M1 benign firm M-score x1000 = -2480" as *u8, bm, 0 - 2480)
56 bg_ck(cnt, "M1a benign -> NOT a manipulator" as *u8, ben_is_manipulator(bm), 0)
57 bg_ckstr(cnt, "M1b verdict = LIKELY-CLEAN" as *u8, ben_verdict_str(bm), "LIKELY-CLEAN" as *u8)
58
59 // ---- M2: a MANIPULATOR -- inflated receivables (DSRI 1.5), sales growth (SGI 1.3), high accruals (TATA 0.1=100bp) ----
60 // -4840000 + 920*1500 + 528*1000 + 404*1000 + 892*1300 + 115*1000 - 172*1000 + 4679*100 - 327*1000
61 // = -4840000 + 1380000 + 528000 + 404000 + 1159600 + 115000 - 172000 + 467900 - 327000 = -1284500; /1000 = -1284.
62 let mm: i64 = ben_mscore(1500, 1000, 1000, 1300, 1000, 1000, 100, 1000)
63 bg_ck(cnt, "M2 manipulator M-score x1000 = -1284" as *u8, mm, 0 - 1284)
64 bg_ck(cnt, "M2a M -1.284 > -1.78 threshold -> MANIPULATOR flagged" as *u8, ben_is_manipulator(mm), 1)
65 bg_ckstr(cnt, "M2b verdict = MANIPULATION-RISK" as *u8, ben_verdict_str(mm), "MANIPULATION-RISK" as *u8)
66
67 // ---- M3: the THRESHOLD is exact -- M just above -1.78 flags, just below does not ----
68 // construct via TATA (coefficient 4.679): find tata making M cross -1780.
69 // baseline (indices 1000, tata 0) M x1000 = -2480. each +1 tata adds 4679/1000 = 4.679 to M x1000.
70 // to reach -1780 from -2480 need +700 -> tata ~ 700000/4679 ~ 149.6. tata=150 -> add 4679*150/1000=701.85 -> M=-1778.15 -> -1778 > -1780 flag.
71 let just_over: i64 = ben_mscore(1000, 1000, 1000, 1000, 1000, 1000, 150, 1000)
72 bg_ck(cnt, "M3 tata=150 -> just over threshold -> flagged" as *u8, ben_is_manipulator(just_over), 1)
73 let just_under: i64 = ben_mscore(1000, 1000, 1000, 1000, 1000, 1000, 140, 1000)
74 bg_ck(cnt, "M3a tata=140 -> just under threshold -> not flagged" as *u8, ben_is_manipulator(just_under), 0)
75
76 // ---- M4: an individual index moves the score in the right direction (higher DSRI -> higher M) ----
77 var rises: i64 = 0
78 if ben_mscore(1200, 1000, 1000, 1000, 1000, 1000, 0, 1000) > ben_mscore(1000, 1000, 1000, 1000, 1000, 1000, 0, 1000) { rises = 1 }
79 bg_ck(cnt, "M4 higher receivables index raises the M-score" as *u8, rises, 1)
80
81 // ---- M5: FAIL-CLOSED -- a missing index yields INCOMPLETE, never a fraud verdict on partial data ----
82 let inc: i64 = ben_mscore(1000, BEN_NA, 1000, 1000, 1000, 1000, 0, 1000)
83 bg_ck(cnt, "M5 missing index -> BEN_INCOMPLETE" as *u8, inc, BEN_INCOMPLETE)
84 bg_ck(cnt, "M5a INCOMPLETE is NOT a manipulator accusation" as *u8, ben_is_manipulator(inc), 0)
85 bg_ckstr(cnt, "M5b verdict = INCOMPLETE (no fraud call on partial facts)" as *u8, ben_verdict_str(inc), "INCOMPLETE" as *u8)
86
87 bg_puts("nx_beneish_gate: pass=" as *u8); bg_putn(cnt[0])
88 bg_puts(" fail=" as *u8); bg_putn(cnt[1]); bg_puts("\n" as *u8)
89 if cnt[1] == 0 {
90 bg_puts("BENEISH nx_beneish: VERDICT=GREEN (exact M-score; manipulator flagged, benign cleared, INCOMPLETE on missing data)\n" as *u8)
91 sys_exit(0)
92 return 0
93 }
94 bg_puts("BENEISH nx_beneish: VERDICT=RED\n" as *u8)
95 sys_exit(1)
96 return 1
97}