nx_spend_analyze_gate.nx source
↩ module page · 57 lines · 3611 B
1// nx_spend_analyze_gate.nx -- proves the spend analyzer on a hand-computed monthly budget:
2// housing $1500 (need,recurring), groceries $400 (need), dining $300 (want), subscriptions $80 (want,recurring),
3// entertainment $120 (want), savings $200 (save,recurring). Total $2600.
4// Verifies class totals + 50/30/20 percentages, the largest-want cut target, subscription drain (+ annual cost),
5// and the situation-aware "how much to cut from wants to hit a savings goal". Exit 0 iff all pass. license_tier: ORIGINAL
6import "nx_gate.nx"
7import "nx_spend_analyze.nx"
8
9func chk(name: *u8, got: i64, want: i64, st: *i64) -> i64 {
10 if got == want { st[0] = st[0] + 1; gw(" PASS " as *u8); gw(name); gw("\n" as *u8) }
11 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) }
12 return 0
13}
14
15func main() -> i64 {
16 let st: *i64 = sys_mmap(16) as *i64
17 st[0] = 0; st[1] = 0
18
19 let cats: *i64 = sys_mmap(8 * SP_FIELDS * 8) as *i64
20 cats[0] = 150000; cats[1] = SP_NEED; cats[2] = 1 // housing
21 cats[3] = 40000; cats[4] = SP_NEED; cats[5] = 0 // groceries
22 cats[6] = 30000; cats[7] = SP_WANT; cats[8] = 0 // dining
23 cats[9] = 8000; cats[10] = SP_WANT; cats[11] = 1 // subscriptions
24 cats[12] = 12000; cats[13] = SP_WANT; cats[14] = 0 // entertainment
25 cats[15] = 20000; cats[16] = SP_SAVE; cats[17] = 1 // savings
26 let n: i64 = 6
27
28 chk("total spend = $2600" as *u8, sp_total(cats, n), 260000, st)
29 chk("needs total = $1900" as *u8, sp_total_by_class(cats, n, SP_NEED), 190000, st)
30 chk("wants total = $500" as *u8, sp_total_by_class(cats, n, SP_WANT), 50000, st)
31 chk("savings total = $200" as *u8, sp_total_by_class(cats, n, SP_SAVE), 20000, st)
32
33 chk("needs = 7307 bps (73.07%)" as *u8, sp_class_pct_bps(cats, n, SP_NEED), 7307, st)
34 chk("wants = 1923 bps (19.23%)" as *u8, sp_class_pct_bps(cats, n, SP_WANT), 1923, st)
35 chk("savings = 769 bps (7.69%)" as *u8, sp_class_pct_bps(cats, n, SP_SAVE), 769, st)
36 chk("savings gap vs 20% = -1231 bps (under-saving)" as *u8, sp_class_gap_bps(cats, n, SP_SAVE, B5030_SAVE), 0 - 1231, st)
37 chk("needs gap vs 50% = +2307 bps (housing-heavy)" as *u8, sp_class_gap_bps(cats, n, SP_NEED, B5030_NEEDS), 2307, st)
38
39 chk("largest want = idx 2 (dining $300)" as *u8, sp_largest_want(cats, n), 2, st)
40
41 chk("recurring total = $1780" as *u8, sp_recurring_total(cats, n), 178000, st)
42 chk("cancellable subscriptions = $80" as *u8, sp_recurring_want_total(cats, n), 8000, st)
43 chk("subscription annual drain = $960" as *u8, sp_recurring_want_annual(cats, n), 96000, st)
44
45 chk("cutting wants 30% frees $150" as *u8, sp_potential_savings(cats, n, 3000), 15000, st)
46
47 // situation-aware: to lift savings from $200 to $500, cut $300 from wants (feasible: wants = $500)
48 chk("required want-cut for $500 goal = $300" as *u8, sp_required_want_cut(50000, 20000, 50000), 30000, st)
49 chk("$500 goal reachable by wants = 1" as *u8, sp_target_reachable_by_wants(50000, 20000, 50000), 1, st)
50 // to lift to $800 would need $600 cut but only $500 of wants exist -> not reachable by wants alone
51 chk("$800 goal reachable by wants = 0" as *u8, sp_target_reachable_by_wants(80000, 20000, 50000), 0, st)
52
53 gw("nx_spend_analyze_gate: PASS=" as *u8); gn(st[0]); gw(" FAIL=" as *u8); gn(st[1]); gw("\n" as *u8)
54 if st[1] == 0 { gw("nx_spend_analyze: GREEN (categorize / 50-30-20 / subscription drain / situation-aware cut)\n" as *u8); return 0 }
55 gw("nx_spend_analyze: RED\n" as *u8)
56 return 1
57}