code wiki / (root) / nx_u256_test.nx

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}