nx_phred_test.nx source
↩ module page · 123 lines · 4725 B
1// nx_phred_test.nx -- KAT for Phred+33 and Phred+64 encode/decode.
2//
3// expect_exit: 0
4//
5// license_tier: ORIGINAL
6
7import "nx_syscalls.nx"
8import "nx_const.nx"
9import "nx_phred.nx"
10
11func main() -> i64 {
12
13 // ============================================================
14 // Section A -- Phred+33 single-value boundary + canonical values.
15 // Q 0 -> '!' 0x21
16 // Q 20 -> '5' 0x35 (variant-calling threshold)
17 // Q 30 -> '?' 0x3F (Illumina target)
18 // Q 40 -> 'I' 0x49 (PacBio HiFi typical)
19 // Q 60 -> ']' 0x5D
20 // Q 93 -> '~' 0x7E (upper cap)
21 // ============================================================
22
23 if nx_phred33_encode(0) != 0x21 { return 1 }
24 if nx_phred33_encode(20) != 0x35 { return 2 }
25 if nx_phred33_encode(30) != 0x3F { return 3 }
26 if nx_phred33_encode(40) != 0x49 { return 4 }
27 if nx_phred33_encode(60) != 0x5D { return 5 }
28 if nx_phred33_encode(93) != 0x7E { return 6 }
29
30 // Out-of-range refusal.
31 if nx_phred33_encode(-1) != -1 { return 7 }
32 if nx_phred33_encode(94) != -1 { return 8 }
33 if nx_phred33_encode(1000) != -1 { return 9 }
34
35 // ============================================================
36 // Section B -- Phred+33 decode boundary + round-trip.
37 // ============================================================
38
39 if nx_phred33_decode(0x21) != 0 { return 10 }
40 if nx_phred33_decode(0x35) != 20 { return 11 }
41 if nx_phred33_decode(0x49) != 40 { return 12 }
42 if nx_phred33_decode(0x7E) != 93 { return 13 }
43
44 // Out-of-range refusal.
45 if nx_phred33_decode(0x20) != -1 { return 14 } // ' ' below 33
46 if nx_phred33_decode(0x7F) != -1 { return 15 } // DEL above 126
47 if nx_phred33_decode(0x00) != -1 { return 16 }
48
49 // Round-trip Q=0..93.
50 var q: i64 = 0
51 while q <= 93 {
52 let a: i64 = nx_phred33_encode(q)
53 let qr: i64 = nx_phred33_decode(a)
54 if qr != q { return 20 + q }
55 q = q + 1
56 }
57
58 // ============================================================
59 // Section C -- Phred+64 single-value boundary + canonical.
60 // Q 0 -> '@' 0x40
61 // Q 20 -> 'T' 0x54
62 // Q 40 -> 'h' 0x68
63 // Q 62 -> '~' 0x7E (legacy upper cap)
64 // ============================================================
65
66 if nx_phred64_encode(0) != 0x40 { return 200 }
67 if nx_phred64_encode(20) != 0x54 { return 201 }
68 if nx_phred64_encode(40) != 0x68 { return 202 }
69 if nx_phred64_encode(62) != 0x7E { return 203 }
70
71 if nx_phred64_encode(-1) != -1 { return 204 }
72 if nx_phred64_encode(63) != -1 { return 205 } // cap at 62
73
74 if nx_phred64_decode(0x40) != 0 { return 210 }
75 if nx_phred64_decode(0x7E) != 62 { return 211 }
76 if nx_phred64_decode(0x3F) != -1 { return 212 } // below 64
77 if nx_phred64_decode(0x7F) != -1 { return 213 } // above 126
78
79 // ============================================================
80 // Section D -- bulk encode / decode on a realistic FASTQ snippet.
81 // 8-base read with descending quality (typical of Illumina cycle drop):
82 // Q values: 40 38 35 33 30 25 20 15
83 // ASCII: I G D B ? : 5 0
84 // ============================================================
85
86 let qs: *i64 = sys_mmap(128) as *i64
87 let ascii: *u8 = sys_mmap(64)
88 qs[0]=40; qs[1]=38; qs[2]=35; qs[3]=33; qs[4]=30; qs[5]=25; qs[6]=20; qs[7]=15
89
90 if nx_phred33_encode_string(qs, 8, ascii) != 0 { return 220 }
91 if (ascii[0] & 0xff) != 0x49 { return 221 } // I
92 if (ascii[1] & 0xff) != 0x47 { return 222 } // G
93 if (ascii[2] & 0xff) != 0x44 { return 223 } // D
94 if (ascii[3] & 0xff) != 0x42 { return 224 } // B
95 if (ascii[4] & 0xff) != 0x3F { return 225 } // ?
96 if (ascii[5] & 0xff) != 0x3A { return 226 } // :
97 if (ascii[6] & 0xff) != 0x35 { return 227 } // 5
98 if (ascii[7] & 0xff) != 0x30 { return 228 } // 0
99
100 // Round-trip via decode.
101 let qs_rt: *i64 = sys_mmap(128) as *i64
102 if nx_phred33_decode_string(ascii, 8, qs_rt) != 0 { return 230 }
103 var i: i64 = 0
104 while i < 8 {
105 if qs_rt[i] != qs[i] { return 240 + i }
106 i = i + 1
107 }
108
109 // ============================================================
110 // Section E -- bulk operations refuse bad input.
111 // ============================================================
112
113 qs[0] = 100 // out of range for Phred+33
114 if nx_phred33_encode_string(qs, 1, ascii) != -1 { return 250 }
115
116 ascii[0] = 0x20 // below Phred+33 range
117 if nx_phred33_decode_string(ascii, 1, qs_rt) != -1 { return 251 }
118
119 if nx_phred33_encode_string(qs, -1, ascii) != -1 { return 252 }
120 if nx_phred33_decode_string(ascii, -1, qs_rt) != -1 { return 253 }
121
122 return 0
123}