nx_debt_strategy_gate.nx source
↩ module page · 65 lines · 3318 B
1// nx_debt_strategy_gate.nx -- proves the debt-payoff strategist on hand-verified fixtures: monthly interest,
2// avalanche vs snowball target selection, a full single-debt payoff (3 months, $1.52 interest), the avalanche-
3// minimises-interest invariant on a 2-debt case, and the underwater (can't-meet-minimums) hardship signal.
4// Exits 0 iff all pass. license_tier: ORIGINAL
5import "nx_gate.nx"
6import "nx_debt_strategy.nx"
7
8func chk(name: *u8, got: i64, want: i64, st: *i64) -> i64 {
9 if got == want { st[0] = st[0] + 1; gw(" PASS " as *u8); gw(name); gw("\n" as *u8) }
10 else { st[1] = st[1] + 1; gw(" FAIL " as *u8); gw(name); gw(" got=" as *u8); gn(got); gw(" want=" as *u8); gn(want); gw("\n" as *u8) }
11 return 0
12}
13
14func main() -> i64 {
15 let st: *i64 = sys_mmap(16) as *i64
16 st[0] = 0; st[1] = 0
17
18 // -- monthly simple interest (cents) --
19 chk("month_interest($100 @ 12%) = 100c" as *u8, ds_month_interest(10000, 1200), 100, st)
20 chk("month_interest($2000 @ 10%) = 1666c" as *u8, ds_month_interest(200000, 1000), 1666, st)
21
22 // -- single debt: $100 @ 12% APR, $50/mo min, no extra -> 3 months, $1.52 total interest --
23 let d1: *i64 = sys_mmap(8 * 4) as *i64
24 d1[0] = 10000; d1[1] = 1200; d1[2] = 5000
25 let o1: *i64 = sys_mmap(16) as *i64
26 ds_simulate(d1, 1, 0, DS_AVALANCHE, o1)
27 chk("single-debt payoff months = 3" as *u8, o1[0], 3, st)
28 chk("single-debt total interest = 152c" as *u8, o1[1], 152, st)
29
30 // -- target selection: balances [1000,500], APRs [20%,10%] --
31 let bal: *i64 = sys_mmap(8 * 4) as *i64
32 bal[0] = 100000; bal[1] = 50000
33 let dt: *i64 = sys_mmap(8 * 8) as *i64
34 dt[0] = 100000; dt[1] = 2000; dt[2] = 1000
35 dt[3] = 50000; dt[4] = 1000; dt[5] = 1000
36 chk("avalanche target = highest APR (idx 0)" as *u8, ds_avalanche_target(bal, dt, 2), 0, st)
37 chk("snowball target = lowest balance (idx 1)" as *u8, ds_snowball_target(bal, 2), 1, st)
38
39 // -- avalanche vs snowball on a 2-debt case: A=$200 @ 24%, B=$100 @ 0%, +$80/mo extra --
40 let d2: *i64 = sys_mmap(8 * 8) as *i64
41 d2[0] = 20000; d2[1] = 2400; d2[2] = 1000
42 d2[3] = 10000; d2[4] = 0; d2[5] = 1000
43 let oa: *i64 = sys_mmap(16) as *i64
44 let os: *i64 = sys_mmap(16) as *i64
45 ds_simulate(d2, 2, 8000, DS_AVALANCHE, oa)
46 ds_simulate(d2, 2, 8000, DS_SNOWBALL, os)
47 chk("avalanche total interest = 680c" as *u8, oa[1], 680, st)
48 chk("snowball total interest = 1023c" as *u8, os[1], 1023, st)
49 var inv: i64 = 0
50 if oa[1] <= os[1] { inv = 1 }
51 chk("INVARIANT: avalanche interest <= snowball interest" as *u8, inv, 1, st)
52
53 // -- underwater: $1000 @ 60% APR, $1/mo min -> cannot even cover interest --
54 let d3: *i64 = sys_mmap(8 * 4) as *i64
55 d3[0] = 100000; d3[1] = 6000; d3[2] = 100
56 chk("underwater: can_meet_minimums = 0" as *u8, ds_can_meet_minimums(d3, 1, 0), 0, st)
57 let o3: *i64 = sys_mmap(16) as *i64
58 ds_simulate(d3, 1, 0, DS_AVALANCHE, o3)
59 chk("underwater: months = -1 (hardship signal)" as *u8, o3[0], 0 - 1, st)
60
61 gw("nx_debt_strategy_gate: PASS=" as *u8); gn(st[0]); gw(" FAIL=" as *u8); gn(st[1]); gw("\n" as *u8)
62 if st[1] == 0 { gw("nx_debt_strategy: GREEN (avalanche/snowball, hand-verified, underwater-aware)\n" as *u8); return 0 }
63 gw("nx_debt_strategy: RED\n" as *u8)
64 return 1
65}