nx_symmetry_group_test.nx source
↩ module page · 107 lines · 4314 B
1// nx_symmetry_group_test.nx -- smoke for multi-axis symmetry.
2
3import "nx_syscalls.nx"
4import "nx_tier.nx"
5import "nx_image.nx"
6import "nx_symmetry_group.nx"
7
8func main() -> nx_int {
9 // === Test 1: uniform image -- all axes trivially maximal ===
10 let img_uni: *Image = nx_image_alloc(16, 16, 1)
11 var y1: nx_int = 0
12 while y1 < 16 {
13 var x1: nx_int = 0
14 while x1 < 16 {
15 nx_image_set(img_uni, x1, y1, 0, 128)
16 x1 = x1 + 1
17 }
18 y1 = y1 + 1
19 }
20 let r_uni: *SymmetryReport = (sys_mmap(12 * NX_SIZEOF_NX_INT)) as *SymmetryReport
21 nx_symmetry_compute(img_uni, r_uni)
22 // Uniform image has all axes trivially symmetric (NX_SYM_Q).
23 if r_uni.h_bilateral_q10 < 900 { return 1 }
24 if r_uni.v_bilateral_q10 < 900 { return 2 }
25 if r_uni.diagonal_q10 < 900 { return 3 }
26 if r_uni.rot_180_q10 < 900 { return 4 }
27 // At least 4 axes should be present at threshold for uniform image.
28 if r_uni.n_axes_present < 4 { return 5 }
29
30 // === Test 2: vertical-bands -> high V symmetry, low H symmetry ===
31 // Bands change column-to-column but stay constant top-to-bottom.
32 let img_vbands: *Image = nx_image_alloc(16, 16, 1)
33 var y2: nx_int = 0
34 while y2 < 16 {
35 var x2: nx_int = 0
36 while x2 < 16 {
37 // Mirror-symmetric across vertical axis (x = 7.5).
38 var v: nx_int = x2
39 if v >= 8 { v = 15 - v }
40 nx_image_set(img_vbands, x2, y2, 0, v * 16)
41 x2 = x2 + 1
42 }
43 y2 = y2 + 1
44 }
45 let r_vb: *SymmetryReport = (sys_mmap(12 * NX_SIZEOF_NX_INT)) as *SymmetryReport
46 nx_symmetry_compute(img_vbands, r_vb)
47 // Mirror-symmetric in x = high H symmetry; bands don't have V mirror
48 // structure (column 0 has v=0, column 15 has v=0 -> H mirror; but
49 // column 0 row 0 vs row 15 are same v=0 -> V mirror IS high too).
50 // Use the relative ordering: H symmetry should be very high.
51 if r_vb.h_bilateral_q10 < 800 { return 10 }
52
53 // === Test 3: diagonal symmetric pattern ===
54 // img(x, y) = img(y, x) by construction (use x*y).
55 let img_diag: *Image = nx_image_alloc(16, 16, 1)
56 var y3: nx_int = 0
57 while y3 < 16 {
58 var x3: nx_int = 0
59 while x3 < 16 {
60 nx_image_set(img_diag, x3, y3, 0, (x3 * y3))
61 x3 = x3 + 1
62 }
63 y3 = y3 + 1
64 }
65 let r_diag: *SymmetryReport = (sys_mmap(12 * NX_SIZEOF_NX_INT)) as *SymmetryReport
66 nx_symmetry_compute(img_diag, r_diag)
67 // x*y is symmetric across the diagonal -> high diagonal_q10.
68 if r_diag.diagonal_q10 < 900 { return 20 }
69
70 // === Test 4: 180-rotation pattern -- img(x,y) = img(W-1-x, H-1-y) ===
71 // Use a checkerboard which has 180 rotation under (x+y) parity.
72 let img_check: *Image = nx_image_alloc(16, 16, 1)
73 var y4: nx_int = 0
74 while y4 < 16 {
75 var x4: nx_int = 0
76 while x4 < 16 {
77 var v: nx_int = 0
78 if (x4 + y4) - ((x4 + y4) / 2) * 2 == 1 { v = 255 }
79 nx_image_set(img_check, x4, y4, 0, v)
80 x4 = x4 + 1
81 }
82 y4 = y4 + 1
83 }
84 let r_chk: *SymmetryReport = (sys_mmap(12 * NX_SIZEOF_NX_INT)) as *SymmetryReport
85 nx_symmetry_compute(img_check, r_chk)
86 // Even-sized checkerboard at corner (0,0)=0 vs corner (15,15) where
87 // (15+15)=30 even -> 0. So both corners are 0 -> 180-rot symmetric.
88 if r_chk.rot_180_q10 < 800 { return 30 }
89 // Also has translational symmetry at period 2.
90 if r_chk.translational_q10 < 800 { return 31 }
91 if r_chk.translation_period != 2 { return 32 }
92
93 // === Test 5: report field ranges ===
94 if r_chk.composite_q10 < 0 { return 40 }
95 if r_chk.composite_q10 > 1024 { return 41 }
96 if r_chk.n_axes_present < 0 { return 42 }
97 if r_chk.n_axes_present > 7 { return 43 }
98
99 // === Test 6: dominant_kind sealed-enum is in valid range ===
100 if nx_sym_kind_is_valid(r_chk.dominant_kind) != 1 { return 50 }
101 if nx_sym_kind_is_valid(NX_SYM_NONE) != 1 { return 51 }
102 if nx_sym_kind_is_valid(NX_SYM_TRANSLATIONAL) != 1 { return 52 }
103 if nx_sym_kind_is_valid(99) != 0 { return 53 }
104 if nx_sym_kind_is_valid(0 - 1) != 0 { return 54 }
105
106 return 0
107}