sketch_holt_test.nx source
↩ module page · 86 lines · 3060 B
1// sketch_holt_test.nx -- Holt's linear forecast verification.
2
3import "syscalls.nx"
4import "sketch_holt.nx"
5import "sketch_types.nx"
6
7func iabs(x: i64) -> i64 {
8 if x < 0 { return -x }
9 return x
10}
11
12func main() -> i64 {
13 // ---- alloc + bounds ----
14 let h: *Holt = nx_holt_alloc(200000, 100000) // alpha=0.2, beta=0.1
15 if h == (0 as *Holt) { return __syscall(93, 5, 0, 0, 0, 0, 0) }
16 if nx_holt_alloc(0, 100000) != (0 as *Holt) {
17 return __syscall(93, 6, 0, 0, 0, 0, 0)
18 }
19 if nx_holt_alloc(2000000, 100000) != (0 as *Holt) {
20 return __syscall(93, 7, 0, 0, 0, 0, 0)
21 }
22
23 // ---- first sample initializes level ----
24 nx_holt_add(h, 100)
25 if nx_holt_level(h) != 100 { return __syscall(93, 10, 0, 0, 0, 0, 0) }
26 if nx_holt_trend(h) != 0 { return __syscall(93, 11, 0, 0, 0, 0, 0) }
27 if nx_holt_count(h) != 1 { return __syscall(93, 12, 0, 0, 0, 0, 0) }
28
29 // ---- second sample with constant offset: trend emerges ----
30 nx_holt_add(h, 110)
31 // level_carry = 100 + 0 = 100; new_level = 0.2*110 + 0.8*100 = 22+80 = 102
32 // level_delta = 102 - 100 = 2
33 // new_trend = 0.1 * 2 + 0.9 * 0 = 0
34 if iabs(nx_holt_level(h) - 102) > 1 { return __syscall(93, 20, 0, 0, 0, 0, 0) }
35 // Trend integer-truncates to 0 here.
36
37 // ---- trending data: linear ramp y = 10*t ----
38 let h2: *Holt = nx_holt_alloc(500000, 500000) // alpha=0.5, beta=0.5
39 var i: i64 = 1
40 while i <= 50 {
41 nx_holt_add(h2, 10 * i)
42 i = i + 1
43 }
44 // After enough data, level should be near 500 (10*50), trend ~10.
45 let lvl: i64 = nx_holt_level(h2)
46 if iabs(lvl - 500) > 50 {
47 return __syscall(93, 30, 0, 0, 0, 0, 0)
48 }
49 let trnd: i64 = nx_holt_trend(h2)
50 if iabs(trnd - 10) > 3 {
51 return __syscall(93, 31, 0, 0, 0, 0, 0)
52 }
53
54 // ---- forecast: 5 steps ahead ----
55 let f5: i64 = nx_holt_forecast(h2, 5)
56 // level + 5 * trend ~ 500 + 50 = 550.
57 if iabs(f5 - 550) > 80 {
58 return __syscall(93, 40, 0, 0, 0, 0, 0)
59 }
60
61 // ---- typed envelope ----
62 let q: *ApproxI64 = nx_holt_query_forecast(h2, 10)
63 if q.envelope_kind != NX_ENV_ABS {
64 return __syscall(93, 50, 0, 0, 0, 0, 0)
65 }
66 if q.param_a != 10 {
67 return __syscall(93, 51, 0, 0, 0, 0, 0)
68 }
69 if q.maturity != NX_MATURITY_PRODUCTION {
70 return __syscall(93, 52, 0, 0, 0, 0, 0)
71 }
72
73 // ---- spike rejection: heavy smoothing dampens spike ----
74 let h3: *Holt = nx_holt_alloc(50000, 50000) // alpha=0.05, beta=0.05
75 nx_holt_add(h3, 100)
76 nx_holt_add(h3, 100)
77 nx_holt_add(h3, 10000) // huge spike
78 // With heavy smoothing, level should remain near 100 (spike barely affects).
79 // After 3 samples: alpha=0.05 -> spike contributes only 5%.
80 let lvl_spike: i64 = nx_holt_level(h3)
81 if lvl_spike > 700 { // spike dampened to less than ~7% of true
82 return __syscall(93, 60, 0, 0, 0, 0, 0)
83 }
84
85 return 0
86}