nx_u256_test.nx source
↩ module page · 179 lines · 5663 B
1// nx_u256_test.nx -- KAT for 256-bit unsigned big-int primitives.
2// Covers load/store byte-order, add/sub carry/borrow propagation
3// across limbs (including all-limb carry), unsigned compare,
4// zero/eq predicates, and aliasing-safety of arithmetic ops.
5//
6// expect_exit: 0
7// license_tier: ORIGINAL
8
9import "nx_syscalls.nx"
10import "nx_u256.nx"
11
12func main() -> i64 {
13 let a: *i64 = u256_alloc()
14 let b: *i64 = u256_alloc()
15 let r: *i64 = u256_alloc()
16 let tmp: *i64 = u256_alloc()
17
18 // ---- Test A: zero + one ----
19 u256_zero(a)
20 if u256_is_zero(a) != 1 { return 1 }
21 u256_one(a)
22 if u256_is_zero(a) != 0 { return 2 }
23 if a[0] != 1 { return 3 }
24
25 // ---- Test B: copy + eq ----
26 u256_one(a)
27 u256_copy(b, a)
28 if u256_eq(a, b) != 1 { return 4 }
29 b[0] = 2
30 if u256_eq(a, b) != 0 { return 5 }
31
32 // ---- Test C: load_be / store_be round-trip ----
33 let bytes: *u8 = sys_mmap(32)
34 var i: i64 = 0
35 while i < 32 {
36 bytes[i] = (0x10 + i) as u8
37 i = i + 1
38 }
39 u256_load_be(a, bytes)
40 // bytes[0..3] = 0x10 0x11 0x12 0x13 -> limb[7] = 0x10111213
41 if a[7] != 0x10111213 { return 10 }
42 // bytes[28..31] = 0x2C 0x2D 0x2E 0x2F -> limb[0] = 0x2C2D2E2F
43 if a[0] != 0x2C2D2E2F { return 11 }
44 // Middle limb check: bytes[12..15] = 0x1C 0x1D 0x1E 0x1F -> limb[4] = 0x1C1D1E1F
45 if a[4] != 0x1C1D1E1F { return 12 }
46
47 let bytes2: *u8 = sys_mmap(32)
48 u256_store_be(bytes2, a)
49 var j: i64 = 0
50 while j < 32 {
51 if (bytes2[j] & 0xff) != (bytes[j] & 0xff) { return 20 + j }
52 j = j + 1
53 }
54
55 // ---- Test D: add no-carry (1 + 1 = 2) ----
56 u256_one(a)
57 u256_one(b)
58 let c1: i64 = u256_add_with_carry(r, a, b)
59 if c1 != 0 { return 60 }
60 if r[0] != 2 { return 61 }
61
62 // ---- Test E: add with limb-boundary carry ----
63 // a = 0xFFFFFFFF (max single limb)
64 // b = 1
65 // expected: r[0] = 0, r[1] = 1
66 u256_zero(a); a[0] = 0xFFFFFFFF
67 u256_one(b)
68 let c2: i64 = u256_add_with_carry(r, a, b)
69 if c2 != 0 { return 70 }
70 if r[0] != 0 { return 71 }
71 if r[1] != 1 { return 72 }
72
73 // ---- Test F: add with cascading carry across all 8 limbs ----
74 // a = 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
75 // (all limbs maxed; equals 2^256 - 1)
76 // b = 1
77 // expected: r = 0, carry-out = 1
78 u256_zero(a)
79 i = 0
80 while i < 8 { a[i] = 0xFFFFFFFF; i = i + 1 }
81 u256_one(b)
82 let c3: i64 = u256_add_with_carry(r, a, b)
83 if c3 != 1 { return 80 }
84 if u256_is_zero(r) != 1 { return 81 }
85
86 // ---- Test G: subtract no-borrow (2 - 1 = 1) ----
87 u256_zero(a); a[0] = 2
88 u256_one(b)
89 let bo1: i64 = u256_sub_with_borrow(r, a, b)
90 if bo1 != 0 { return 90 }
91 if r[0] != 1 { return 91 }
92
93 // ---- Test H: subtract with limb-boundary borrow ----
94 // a = 0x1 0000 0000 (limb[1]=1, limb[0]=0; represents 2^32)
95 // b = 1
96 // expected: r[0] = 0xFFFFFFFF, r[1] = 0
97 u256_zero(a); a[1] = 1
98 u256_one(b)
99 let bo2: i64 = u256_sub_with_borrow(r, a, b)
100 if bo2 != 0 { return 100 }
101 if r[0] != 0xFFFFFFFF { return 101 }
102 if r[1] != 0 { return 102 }
103
104 // ---- Test I: subtract producing underflow ----
105 // a = 0, b = 1 -> r = 2^256 - 1, borrow = 1
106 u256_zero(a)
107 u256_one(b)
108 let bo3: i64 = u256_sub_with_borrow(r, a, b)
109 if bo3 != 1 { return 110 }
110 i = 0
111 while i < 8 {
112 if r[i] != 0xFFFFFFFF { return 111 + i }
113 i = i + 1
114 }
115
116 // ---- Test J: add then subtract round-trip ----
117 // a = arbitrary, b = arbitrary, (a + b) - b should == a
118 u256_load_be(a, bytes) // a = 0x10111213 ... 0x2C2D2E2F
119 u256_zero(b)
120 b[0] = 0x12345678; b[2] = 0xABCDEF01; b[5] = 0x55555555
121 u256_add_with_carry(r, a, b)
122 u256_sub_with_borrow(tmp, r, b)
123 if u256_eq(tmp, a) != 1 { return 120 }
124
125 // ---- Test K: unsigned compare ----
126 u256_zero(a)
127 u256_one(b)
128 if u256_cmp(a, b) != (0 - 1) { return 130 }
129 if u256_cmp(b, a) != 1 { return 131 }
130 if u256_cmp(a, a) != 0 { return 132 }
131
132 // Compare with different high limbs
133 u256_zero(a); a[7] = 1
134 u256_zero(b); b[7] = 2
135 if u256_cmp(a, b) != (0 - 1) { return 133 }
136 if u256_cmp(b, a) != 1 { return 134 }
137
138 // Compare at MSB but equal-high, different-low
139 u256_zero(a); a[7] = 0xDEADBEEF; a[0] = 1
140 u256_zero(b); b[7] = 0xDEADBEEF; b[0] = 2
141 if u256_cmp(a, b) != (0 - 1) { return 135 }
142 if u256_cmp(b, a) != 1 { return 136 }
143
144 // ---- Test L: in-place add (out aliases a) ----
145 u256_load_be(a, bytes)
146 u256_load_be(b, bytes)
147 u256_copy(tmp, a)
148 let c4: i64 = u256_add_with_carry(a, a, b)
149 // After in-place add: a should equal (tmp + tmp). Verify via re-do.
150 let r2: *i64 = u256_alloc()
151 u256_add_with_carry(r2, tmp, tmp)
152 if u256_eq(a, r2) != 1 { return 140 }
153 if c4 != 0 { return 141 } // 2x the original doesn't overflow given byte values
154
155 // ---- Test M: in-place sub (out aliases a) ----
156 u256_load_be(a, bytes)
157 u256_load_be(b, bytes)
158 u256_sub_with_borrow(a, a, b)
159 if u256_is_zero(a) != 1 { return 150 }
160
161 // ---- Test N: large-value BE round-trip via known value ----
162 // value = 2^255 = 0x80 00 00 00 ... 00 (32 bytes)
163 let big: *u8 = sys_mmap(32)
164 var k: i64 = 0
165 while k < 32 { big[k] = 0; k = k + 1 }
166 big[0] = 0x80 as u8
167 u256_load_be(a, big)
168 if a[7] != 0x80000000 { return 160 }
169 if a[0] != 0 { return 161 }
170 let big2: *u8 = sys_mmap(32)
171 u256_store_be(big2, a)
172 k = 0
173 while k < 32 {
174 if (big2[k] & 0xff) != (big[k] & 0xff) { return 170 + k }
175 k = k + 1
176 }
177
178 return 0
179}