| 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); |
| 3 | const extras = @import("extras"); |
| 4 | |
| 5 | /// GF(256)[0...255] |
| 6 | const GF = blk: { |
| 7 | var gf: [256]u8 = @splat(2); |
| 8 | for (0..256) |n| { |
| 9 | if (n == 0) { |
| 10 | gf[n] = 1; |
| 11 | continue; |
| 12 | } |
| 13 | if (n < 8) { |
| 14 | gf[n] <<= @intCast(n - 1); |
| 15 | continue; |
| 16 | } |
| 17 | gf[n] = gf[n - 1] *% 2; |
| 18 | if (gf[n - 1] > 127) gf[n] ^= 0b00011101; |
| 19 | } |
| 20 | const out = gf; |
| 21 | break :blk out; |
| 22 | }; |
| 23 | |
| 24 | pub fn encode(comptime mode: Mode, comptime level: Level, comptime version: u16, input: []const u8) BitGrid(4 * version + 17) { |
| 25 | if (mode == .numeric) std.debug.assert(extras.matchesAll(u8, input, isNumeric)); |
| 26 | if (mode == .alphanumeric) std.debug.assert(extras.matchesAll(u8, input, isAlphanumeric)); |
| 27 | // latin1 is a full 1-byte format, some readers can detect utf-8 |
| 28 | // shift jis is a full 2-byte format |
| 29 | |
| 30 | const max_lengths = comptime maxLenPerModeLevelVersion(mode, level); |
| 31 | const max_length = max_lengths[version]; |
| 32 | std.debug.assert(input.len <= max_length); |
| 33 | |
| 34 | const dataCodewordsCount, const ecCodewordsPerBlock, const groupOneBlockCount, const groupOneDataCodewordsCountPerBlock, const groupTwoBlockCount, const groupTwoDataCodewordsCountPerBlock = comptime errorCorrection(level, version); |
| 35 | |
| 36 | const mode_indicator: u4 = switch (mode) { |
| 37 | .numeric => 0b0001, |
| 38 | .alphanumeric => 0b0010, |
| 39 | .latin1 => 0b0100, |
| 40 | .shiftjis => 0b1000, |
| 41 | }; |
| 42 | |
| 43 | const LenIndicator = switch (mode) { |
| 44 | .numeric => switch (version) { |
| 45 | 1...9 => u10, |
| 46 | 10...26 => u12, |
| 47 | 27...40 => u14, |
| 48 | else => unreachable, |
| 49 | }, |
| 50 | .alphanumeric => switch (version) { |
| 51 | 1...9 => u9, |
| 52 | 10...26 => u11, |
| 53 | 27...40 => u13, |
| 54 | else => unreachable, |
| 55 | }, |
| 56 | .latin1 => switch (version) { |
| 57 | 1...9 => u8, |
| 58 | 10...26 => u16, |
| 59 | 27...40 => u16, |
| 60 | else => unreachable, |
| 61 | }, |
| 62 | .shiftjis => switch (version) { |
| 63 | 1...9 => u8, |
| 64 | 10...26 => u10, |
| 65 | 27...40 => u12, |
| 66 | else => unreachable, |
| 67 | }, |
| 68 | }; |
| 69 | const len_indicator: LenIndicator = @intCast(input.len); |
| 70 | |
| 71 | const total_bits = dataCodewordsCount * 8; |
| 72 | |
| 73 | var scratch: std.bit_set.ArrayBitSet(u8, total_bits) = .initEmpty(); |
| 74 | var scratchw: BitsetWriter(u8, total_bits) = .{ .set = &scratch }; |
| 75 | |
| 76 | scratchw.writeInt(mode_indicator); |
| 77 | scratchw.writeInt(len_indicator); |
| 78 | for (input) |x| scratchw.writeInt(x); |
| 79 | |
| 80 | const data_bits = input.len * 8 + @bitSizeOf(@TypeOf(mode_indicator)) + @bitSizeOf(@TypeOf(len_indicator)); |
| 81 | std.debug.assert(scratchw.idx == data_bits); |
| 82 | |
| 83 | // write @max(4, padding_bits) 0s |
| 84 | for (0..@min(4, total_bits - scratchw.idx)) |_| scratchw.writeInt(@as(u1, 0)); |
| 85 | |
| 86 | // add zero bits to make data_bits%8 == 0 |
| 87 | for (0..-%scratchw.idx & 7) |_| scratchw.writeInt(@as(u1, 0)); |
| 88 | |
| 89 | // add 236(11101100) 17(00010001) until we reach total_bits |
| 90 | while (scratchw.idx < total_bits) { |
| 91 | scratchw.writeInt(@as(u8, 0xec)); |
| 92 | scratchw.writeInt(@as(u8, 0x11)); |
| 93 | } |
| 94 | |
| 95 | const generator_polynomial = reverseArray(generatorPolynomial(ecCodewordsPerBlock)); |
| 96 | const generator_polynomial_alpha = to_alpha(generator_polynomial); |
| 97 | |
| 98 | // Now that the message and generator polynomials have been created, divide them to get the error codewords. |
| 99 | { |
| 100 | // 1. Multiply the message polynomial by x^n where n is the number of error correction codewords to be generated |
| 101 | // and multiply the generator polynomial by x^m where m is the leading exponent of the message polynomial. |
| 102 | // 2. Multiply the generator polynomial by the lead term of the message polynomial. |
| 103 | // 3. XOR the result of Step 2 with the message polynomial and discard the lead 0 term. |
| 104 | // 4. Repeat Steps 2-3 n-1 more times where n is the number of data codewords. |
| 105 | } |
| 106 | |
| 107 | const group2_block_count = groupTwoBlockCount orelse 0; |
| 108 | const group2_datacodewords_per_block = groupTwoDataCodewordsCountPerBlock orelse 0; |
| 109 | |
| 110 | var group1_ec: [groupOneBlockCount][ecCodewordsPerBlock]u8 = @splat(@splat(0)); |
| 111 | var group2_ec: [group2_block_count][ecCodewordsPerBlock]u8 = @splat(@splat(0)); |
| 112 | |
| 113 | for (&group1_ec, 0..) |*g, gn| { |
| 114 | const message_polynomial = scratch.masks[groupOneDataCodewordsCountPerBlock * gn ..][0..groupOneDataCodewordsCountPerBlock]; |
| 115 | |
| 116 | var mp: [groupOneDataCodewordsCountPerBlock + ecCodewordsPerBlock]?u8 = @splat(null); |
| 117 | for (message_polynomial, 0..) |x, i| mp[i] = to_alpha(.{@bitReverse(x)})[0]; |
| 118 | |
| 119 | for (0..groupOneDataCodewordsCountPerBlock) |i| { |
| 120 | const lead = mp[i] orelse 0; |
| 121 | for (&generator_polynomial_alpha, 0..) |x, j| { |
| 122 | var y = GF[(@as(usize, x) + lead) % 255]; |
| 123 | if (mp[i + j]) |z| y ^= GF[z]; |
| 124 | mp[i + j] = if (y == 0) null else to_alpha(.{y})[0]; |
| 125 | } |
| 126 | } |
| 127 | for (g, 0..) |_, i| { |
| 128 | g[i] = if (mp[message_polynomial.len..][i]) |x| GF[x] else 0; |
| 129 | } |
| 130 | } |
| 131 | for (&group2_ec, 0..) |*g, gn| { |
| 132 | const message_polynomial = scratch.masks[groupOneBlockCount * groupOneDataCodewordsCountPerBlock ..][group2_datacodewords_per_block * gn ..][0..group2_datacodewords_per_block]; |
| 133 | |
| 134 | var mp: [group2_datacodewords_per_block + ecCodewordsPerBlock]?u8 = @splat(null); |
| 135 | for (message_polynomial, 0..) |x, i| mp[i] = to_alpha(.{@bitReverse(x)})[0]; |
| 136 | |
| 137 | for (0..group2_datacodewords_per_block) |i| { |
| 138 | const lead = mp[i] orelse 0; |
| 139 | for (&generator_polynomial_alpha, 0..) |x, j| { |
| 140 | var y = GF[(@as(usize, x) + lead) % 255]; |
| 141 | if (mp[i + j]) |z| y ^= GF[z]; |
| 142 | mp[i + j] = if (y == 0) null else to_alpha(.{y})[0]; |
| 143 | } |
| 144 | } |
| 145 | for (g, 0..) |_, i| { |
| 146 | g[i] = if (mp[message_polynomial.len..][i]) |x| GF[x] else 0; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | comptime std.debug.assert(groupOneDataCodewordsCountPerBlock != group2_datacodewords_per_block); |
| 151 | const group1_allcodewords_count = groupOneBlockCount * groupOneDataCodewordsCountPerBlock + groupOneBlockCount * ecCodewordsPerBlock; |
| 152 | const group2_allcodewords_count = group2_block_count * group2_datacodewords_per_block + group2_block_count * ecCodewordsPerBlock; |
| 153 | var interleaved_message: [group1_allcodewords_count + group2_allcodewords_count]u8 = @splat(0); |
| 154 | var imi: usize = 0; |
| 155 | for (0..@min(groupOneDataCodewordsCountPerBlock, group2_datacodewords_per_block)) |j| { |
| 156 | for (0..groupOneBlockCount) |i| { |
| 157 | interleaved_message[imi] = scratch.masks[(i * groupOneDataCodewordsCountPerBlock) + j]; |
| 158 | imi += 1; |
| 159 | } |
| 160 | for (0..group2_block_count) |i| { |
| 161 | interleaved_message[imi] = scratch.masks[(groupOneBlockCount * groupOneDataCodewordsCountPerBlock) + (i * group2_datacodewords_per_block) + j]; |
| 162 | imi += 1; |
| 163 | } |
| 164 | } |
| 165 | for (0..groupOneDataCodewordsCountPerBlock - @min(groupOneDataCodewordsCountPerBlock, group2_datacodewords_per_block)) |j| { |
| 166 | for (0..groupOneBlockCount) |i| { |
| 167 | interleaved_message[imi] = scratch.masks[(i * groupOneDataCodewordsCountPerBlock) + j]; |
| 168 | imi += 1; |
| 169 | } |
| 170 | } |
| 171 | for (0..group2_datacodewords_per_block - @min(groupOneDataCodewordsCountPerBlock, group2_datacodewords_per_block)) |j| { |
| 172 | for (0..group2_block_count) |i| { |
| 173 | interleaved_message[imi] = scratch.masks[(groupOneBlockCount * groupOneDataCodewordsCountPerBlock) + (i * group2_datacodewords_per_block) + j]; |
| 174 | imi += 1; |
| 175 | } |
| 176 | } |
| 177 | for (0..ecCodewordsPerBlock) |j| { |
| 178 | for (0..groupOneBlockCount) |i| { |
| 179 | interleaved_message[imi] = @bitReverse(group1_ec[i][j]); |
| 180 | imi += 1; |
| 181 | } |
| 182 | for (0..group2_block_count) |i| { |
| 183 | interleaved_message[imi] = @bitReverse(group2_ec[i][j]); |
| 184 | imi += 1; |
| 185 | } |
| 186 | } |
| 187 | std.debug.assert(imi == interleaved_message.len); |
| 188 | |
| 189 | // remainder bits |
| 190 | const remainder_bits_count = comptime remainderBitsCount(version); |
| 191 | _ = remainder_bits_count; |
| 192 | |
| 193 | // |
| 194 | |
| 195 | const size = 4 * version + 17; |
| 196 | var grid: BitGrid(size) = .initEmpty(); |
| 197 | |
| 198 | // top left finder pattern |
| 199 | for (0..7) |i| for (0..7) |j| grid.setT(0 + i, 0 + j); |
| 200 | for (0..5) |i| for (0..5) |j| grid.setF(1 + i, 1 + j); |
| 201 | for (0..3) |i| for (0..3) |j| grid.setT(2 + i, 2 + j); |
| 202 | |
| 203 | // top right finder pattern |
| 204 | for (0..7) |i| for (0..7) |j| grid.setT(size - 7 + 0 + i, 0 + j); |
| 205 | for (0..5) |i| for (0..5) |j| grid.setF(size - 7 + 1 + i, 1 + j); |
| 206 | for (0..3) |i| for (0..3) |j| grid.setT(size - 7 + 2 + i, 2 + j); |
| 207 | |
| 208 | // bottom left finder pattern |
| 209 | for (0..7) |i| for (0..7) |j| grid.setT(0 + i, size - 7 + 0 + j); |
| 210 | for (0..5) |i| for (0..5) |j| grid.setF(1 + i, size - 7 + 1 + j); |
| 211 | for (0..3) |i| for (0..3) |j| grid.setT(2 + i, size - 7 + 2 + j); |
| 212 | |
| 213 | // timing pattern |
| 214 | for (8..size) |i| if (i % 2 == 0) grid.set(i, 6); |
| 215 | for (8..size) |i| if (i % 2 == 0) grid.set(6, i); |
| 216 | |
| 217 | // dark module |
| 218 | grid.set(8, 4 * (version - 1) + 13); |
| 219 | |
| 220 | // data mask (data cannot be placed where this is set) |
| 221 | var data_mask: BitGrid(size) = .initEmpty(); |
| 222 | for (0..9) |y| for (0..9) |x| data_mask.set(x, y); |
| 223 | for (0..9) |y| for (0..8) |x| data_mask.set(size - 8 + x, y); |
| 224 | for (0..8) |y| for (0..9) |x| data_mask.set(x, size - 8 + y); |
| 225 | for (0..size) |i| data_mask.set(i, 6); |
| 226 | for (0..size) |i| data_mask.set(6, i); |
| 227 | |
| 228 | // alignment patterns |
| 229 | const alignment_pattern_centers = comptime alignmentPatternCenters(version); |
| 230 | for (alignment_pattern_centers, 0..) |y, i| { |
| 231 | for (alignment_pattern_centers, 0..) |x, j| blk: { |
| 232 | if (i == 0 and j == 0) break :blk; |
| 233 | if (i == 0 and j == alignment_pattern_centers.len - 1) break :blk; |
| 234 | if (j == 0 and i == alignment_pattern_centers.len - 1) break :blk; |
| 235 | |
| 236 | for (y - 2..y + 2 + 1) |y2| for (x - 2..x + 2 + 1) |x2| { |
| 237 | grid.setT(x2, y2); |
| 238 | data_mask.set(x2, y2); |
| 239 | }; |
| 240 | for (y - 1..y + 1 + 1) |y2| for (x - 1..x + 1 + 1) |x2| { |
| 241 | grid.setF(x2, y2); |
| 242 | data_mask.set(x2, y2); |
| 243 | }; |
| 244 | grid.setT(x, y); |
| 245 | data_mask.set(x, y); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | // version block |
| 250 | if (version >= 7) { |
| 251 | var bititer: std.bit_set.IntegerBitSet(18) = .{ .mask = versionBits(version) }; |
| 252 | for (0..3) |x| { |
| 253 | for (0..6) |y| { |
| 254 | grid.setV(size - 11 + x, y, bititer.isSet((y * 3) + x)); |
| 255 | data_mask.set(size - 11 + x, y); |
| 256 | } |
| 257 | } |
| 258 | for (0..6) |x| { |
| 259 | for (0..3) |y| { |
| 260 | grid.setV(x, size - 11 + y, bititer.isSet((x * 3) + y)); |
| 261 | data_mask.set(x, size - 11 + y); |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | // write data |
| 267 | var data_iter: BufBiterator = .init(&bitReverse(interleaved_message)); |
| 268 | |
| 269 | var griditer: GridZigZag = .init(size, 6); |
| 270 | while (griditer.next()) |idx| { |
| 271 | const x = idx % size; |
| 272 | const y = (idx - x) / size; |
| 273 | if (data_mask.isSet(x, y)) continue; |
| 274 | if (@bitCast(data_iter.next() orelse break)) grid.set(x, y); |
| 275 | } |
| 276 | |
| 277 | // format info |
| 278 | var masks = [_]std.bit_set.IntegerBitSet(15){ |
| 279 | .{ .mask = @bitReverse(formatPattern(level, 0)) }, |
| 280 | .{ .mask = @bitReverse(formatPattern(level, 1)) }, |
| 281 | .{ .mask = @bitReverse(formatPattern(level, 2)) }, |
| 282 | .{ .mask = @bitReverse(formatPattern(level, 3)) }, |
| 283 | .{ .mask = @bitReverse(formatPattern(level, 4)) }, |
| 284 | .{ .mask = @bitReverse(formatPattern(level, 5)) }, |
| 285 | .{ .mask = @bitReverse(formatPattern(level, 6)) }, |
| 286 | .{ .mask = @bitReverse(formatPattern(level, 7)) }, |
| 287 | }; |
| 288 | var grids = [_]BitGrid(size){ |
| 289 | grid, |
| 290 | grid, |
| 291 | grid, |
| 292 | grid, |
| 293 | grid, |
| 294 | grid, |
| 295 | grid, |
| 296 | grid, |
| 297 | }; |
| 298 | for (&grids, &masks) |*g, m| { |
| 299 | g.setV(0, 8, m.isSet(0)); |
| 300 | g.setV(1, 8, m.isSet(1)); |
| 301 | g.setV(2, 8, m.isSet(2)); |
| 302 | g.setV(3, 8, m.isSet(3)); |
| 303 | g.setV(4, 8, m.isSet(4)); |
| 304 | g.setV(5, 8, m.isSet(5)); |
| 305 | g.setV(7, 8, m.isSet(6)); |
| 306 | g.setV(8, 8, m.isSet(7)); |
| 307 | g.setV(8, 7, m.isSet(8)); |
| 308 | g.setV(8, 5, m.isSet(9)); |
| 309 | g.setV(8, 4, m.isSet(10)); |
| 310 | g.setV(8, 3, m.isSet(11)); |
| 311 | g.setV(8, 2, m.isSet(12)); |
| 312 | g.setV(8, 1, m.isSet(13)); |
| 313 | g.setV(8, 0, m.isSet(14)); |
| 314 | |
| 315 | g.setV(8, size - 1 - 0, m.isSet(0)); |
| 316 | g.setV(8, size - 1 - 1, m.isSet(1)); |
| 317 | g.setV(8, size - 1 - 2, m.isSet(2)); |
| 318 | g.setV(8, size - 1 - 3, m.isSet(3)); |
| 319 | g.setV(8, size - 1 - 4, m.isSet(4)); |
| 320 | g.setV(8, size - 1 - 5, m.isSet(5)); |
| 321 | g.setV(8, size - 1 - 6, m.isSet(6)); |
| 322 | g.setV(size - 8 + 0, 8, m.isSet(7)); |
| 323 | g.setV(size - 8 + 1, 8, m.isSet(8)); |
| 324 | g.setV(size - 8 + 2, 8, m.isSet(9)); |
| 325 | g.setV(size - 8 + 3, 8, m.isSet(10)); |
| 326 | g.setV(size - 8 + 4, 8, m.isSet(11)); |
| 327 | g.setV(size - 8 + 5, 8, m.isSet(12)); |
| 328 | g.setV(size - 8 + 6, 8, m.isSet(13)); |
| 329 | g.setV(size - 8 + 7, 8, m.isSet(14)); |
| 330 | } |
| 331 | |
| 332 | // masking patterns |
| 333 | // Pattern 0: (row + column) mod 2 == 0 |
| 334 | for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (row + col) % 2 == 0) grids[0].flip(col, row); |
| 335 | // Pattern 1: row mod 2 == 0 |
| 336 | for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and row % 2 == 0) grids[1].flip(col, row); |
| 337 | // Pattern 2: column mod 3 == 0 |
| 338 | for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and col % 3 == 0) grids[2].flip(col, row); |
| 339 | // Pattern 3: (row + column) mod 3 == 0 |
| 340 | for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (row + col) % 3 == 0) grids[3].flip(col, row); |
| 341 | // Pattern 4: (floor(row/2) + floor(column / 3)) mod 2 == 0 |
| 342 | for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (row / 2 + col / 3) % 2 == 0) grids[4].flip(col, row); |
| 343 | // Pattern 5: ((row * column) mod 2) + ((row * column) mod 3) == 0 |
| 344 | for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (((row * col) % 2) + ((row * col) % 3)) == 0) grids[5].flip(col, row); |
| 345 | // Pattern 6: ( ((row * column) mod 2) + ((row * column) mod 3) ) mod 2 == 0 |
| 346 | for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (((row * col) % 2) + ((row * col) % 3)) % 2 == 0) grids[6].flip(col, row); |
| 347 | // Pattern 7: ( ((row + column) mod 2) + ((row * column) mod 3) ) mod 2 == 0 |
| 348 | for (0..size) |row| for (0..size) |col| if (!data_mask.isSet(col, row) and (((row + col) % 2) + ((row * col) % 3)) % 2 == 0) grids[7].flip(col, row); |
| 349 | |
| 350 | // penalty |
| 351 | var penaltys: [8][4]u32 = @splat(@splat(0)); |
| 352 | for (&grids, 0..) |*g, i| { |
| 353 | var prev = false; |
| 354 | var run: u16 = 0; |
| 355 | for (0..size) |y| { |
| 356 | for (0..size) |x| { |
| 357 | if (x == 0) { |
| 358 | run = 0; |
| 359 | prev = g.isSet(0, y); |
| 360 | } |
| 361 | const this = g.isSet(x, y); |
| 362 | if (this == prev) { |
| 363 | run += 1; |
| 364 | penaltys[i][0] += if (run == 5) 3 else if (run > 5) 1 else 0; |
| 365 | } else { |
| 366 | run = 1; |
| 367 | } |
| 368 | prev = this; |
| 369 | } |
| 370 | } |
| 371 | for (0..size) |x| { |
| 372 | for (0..size) |y| { |
| 373 | if (y == 0) { |
| 374 | run = 0; |
| 375 | prev = g.isSet(x, 0); |
| 376 | } |
| 377 | const this = g.isSet(x, y); |
| 378 | if (this == prev) { |
| 379 | run += 1; |
| 380 | penaltys[i][0] += if (run == 5) 3 else if (run > 5) 1 else 0; |
| 381 | } else { |
| 382 | run = 1; |
| 383 | } |
| 384 | prev = this; |
| 385 | } |
| 386 | } |
| 387 | } |
| 388 | for (&grids, 0..) |*g, i| { |
| 389 | for (0..size - 1) |y| { |
| 390 | for (0..size - 1) |x| { |
| 391 | const p0 = g.isSet(x, y); |
| 392 | const p1 = g.isSet(x + 1, y); |
| 393 | const p2 = g.isSet(x, y + 1); |
| 394 | const p3 = g.isSet(x + 1, y + 1); |
| 395 | penaltys[i][1] += if (p0 == p1 and p1 == p2 and p2 == p3) 3 else 0; |
| 396 | } |
| 397 | } |
| 398 | } |
| 399 | for (&grids, 0..) |*g, i| { |
| 400 | for (0..size) |y| { |
| 401 | for (0..size - 10) |x| { |
| 402 | const sel = g.sliceX(y, x, 11); |
| 403 | penaltys[i][2] += if (sel.mask == 0b00001011101) 40 else 0; |
| 404 | penaltys[i][2] += if (sel.mask == 0b10111010000) 40 else 0; |
| 405 | } |
| 406 | } |
| 407 | for (0..size) |x| { |
| 408 | for (0..size - 10) |y| { |
| 409 | const sel = g.sliceY(x, y, 11); |
| 410 | penaltys[i][2] += if (sel.mask == 0b00001011101) 40 else 0; |
| 411 | penaltys[i][2] += if (sel.mask == 0b10111010000) 40 else 0; |
| 412 | } |
| 413 | } |
| 414 | } |
| 415 | for (&grids, 0..) |*g, i| { |
| 416 | const total_modules: comptime_float = size * size; |
| 417 | var dark_modules: f32 = 0; |
| 418 | for (0..size) |y| { |
| 419 | for (0..size) |x| { |
| 420 | if (g.isSet(x, y)) { |
| 421 | dark_modules += 1; |
| 422 | } |
| 423 | } |
| 424 | } |
| 425 | const percent: u32 = @intFromFloat(@ceil(dark_modules / total_modules * 100)); |
| 426 | const round1 = percent + (-%percent & 4); |
| 427 | const round2 = round1 -| 5; |
| 428 | const sub = [_]u32{ @abs(@as(i32, @intCast(round1)) - 50), @abs(@as(i32, @intCast(round2)) - 50) }; |
| 429 | const div = [_]u32{ @divFloor(sub[0], 5), @divFloor(sub[1], 5) }; |
| 430 | penaltys[i][3] += std.mem.min(u32, &div) * 10; |
| 431 | } |
| 432 | |
| 433 | var penalty: [8]u32 = @splat(0); |
| 434 | for (0..8) |i| penalty[i] = extras.sum(u32, &penaltys[i]); |
| 435 | |
| 436 | const min_penalty = std.mem.min(u32, &penalty); |
| 437 | const min_mask = std.mem.indexOfScalar(u32, &penalty, min_penalty).?; |
| 438 | return grids[min_mask]; |
| 439 | } |
| 440 | |
| 441 | fn BitGrid(comptime size: usize) type { |
| 442 | return struct { |
| 443 | bset: std.bit_set.ArrayBitSet(usize, size * size), |
| 444 | comptime size: usize = size, |
| 445 | |
| 446 | pub fn initEmpty() @This() { |
| 447 | return .{ |
| 448 | .bset = .initEmpty(), |
| 449 | }; |
| 450 | } |
| 451 | |
| 452 | pub fn isSet(self: *const @This(), x: usize, y: usize) bool { |
| 453 | return self.bset.isSet((y * size) + x); |
| 454 | } |
| 455 | |
| 456 | pub fn set(self: *@This(), x: usize, y: usize) void { |
| 457 | self.bset.set((y * size) + x); |
| 458 | } |
| 459 | |
| 460 | pub fn setF(self: *@This(), x: usize, y: usize) void { |
| 461 | self.bset.setValue((y * size) + x, false); |
| 462 | } |
| 463 | |
| 464 | pub fn setT(self: *@This(), x: usize, y: usize) void { |
| 465 | self.bset.setValue((y * size) + x, true); |
| 466 | } |
| 467 | |
| 468 | pub fn setV(self: *@This(), x: usize, y: usize, value: bool) void { |
| 469 | self.bset.setValue((y * size) + x, value); |
| 470 | } |
| 471 | |
| 472 | pub fn flip(self: *@This(), x: usize, y: usize) void { |
| 473 | self.bset.toggle((y * size) + x); |
| 474 | } |
| 475 | |
| 476 | pub fn sliceX(self: *@This(), y: usize, sx: usize, comptime len: usize) std.bit_set.IntegerBitSet(len) { |
| 477 | var result: std.bit_set.IntegerBitSet(len) = .initEmpty(); |
| 478 | for (0..len) |i| result.setValue(i, self.isSet(sx + i, y)); |
| 479 | return result; |
| 480 | } |
| 481 | |
| 482 | pub fn sliceY(self: *@This(), x: usize, sy: usize, comptime len: usize) std.bit_set.IntegerBitSet(len) { |
| 483 | var result: std.bit_set.IntegerBitSet(len) = .initEmpty(); |
| 484 | for (0..len) |i| result.setValue(i, self.isSet(x, sy + i)); |
| 485 | return result; |
| 486 | } |
| 487 | |
| 488 | pub fn asBlockString(grid: *const @This(), allocator: std.mem.Allocator) ![]u8 { |
| 489 | var content: std.Io.Writer.Allocating = .init(allocator); |
| 490 | errdefer content.deinit(); |
| 491 | for (0..grid.size / 2 + 1) |y| { |
| 492 | for (0..grid.size / 2 + 1) |x| { |
| 493 | var idx: std.bit_set.IntegerBitSet(4) = .initEmpty(); |
| 494 | inline for (0..2) |x2| { |
| 495 | inline for (0..2) |y2| blk: { |
| 496 | const real_x = x * 2 + x2; |
| 497 | const real_y = y * 2 + y2; |
| 498 | if (real_x >= grid.size) break :blk; |
| 499 | if (real_y >= grid.size) break :blk; |
| 500 | const is_set = grid.isSet(real_x, real_y); |
| 501 | if (!is_set) break :blk; |
| 502 | idx.set(x2 * 2 + y2); |
| 503 | } |
| 504 | } |
| 505 | const characters = [_][]const u8{ " ", "▘", "▖", "▌", "▝", "▀", "▞", "▛", "▗", "▚", "▄", "▙", "▐", "▜", "▟", "█" }; |
| 506 | try content.writer.writeAll(characters[(idx.mask)]); |
| 507 | } |
| 508 | if (builtin.is_test) try content.writer.writeAll("|"); |
| 509 | try content.writer.writeAll("\n"); |
| 510 | } |
| 511 | return content.toOwnedSlice(); |
| 512 | } |
| 513 | |
| 514 | pub fn asBlockString2(grid: *const @This(), allocator: std.mem.Allocator) ![]u8 { |
| 515 | var content: std.Io.Writer.Allocating = .init(allocator); |
| 516 | errdefer content.deinit(); |
| 517 | for (0..grid.size / 2 + 1) |y| { |
| 518 | for (0..grid.size) |x| { |
| 519 | var idx: std.bit_set.IntegerBitSet(4) = .initEmpty(); |
| 520 | inline for (0..2) |y2| blk: { |
| 521 | const real_x = x; |
| 522 | const real_y = y * 2 + y2; |
| 523 | if (real_x >= grid.size) break :blk; |
| 524 | if (real_y >= grid.size) break :blk; |
| 525 | const is_set = grid.isSet(real_x, real_y); |
| 526 | if (!is_set) break :blk; |
| 527 | idx.set(y2); |
| 528 | } |
| 529 | const characters = [_][]const u8{ " ", "▀", "▄", "█" }; |
| 530 | try content.writer.writeAll(characters[(idx.mask)]); |
| 531 | } |
| 532 | if (builtin.is_test) try content.writer.writeAll("|"); |
| 533 | try content.writer.writeAll("\n"); |
| 534 | } |
| 535 | return content.toOwnedSlice(); |
| 536 | } |
| 537 | |
| 538 | pub fn asBrailleString(grid: *const @This(), allocator: std.mem.Allocator) ![]u8 { |
| 539 | const B = packed struct(u8) { @"0": bool, @"1": bool, @"2": bool, @"3": bool, @"4": bool, @"5": bool, @"6": bool, @"7": bool }; |
| 540 | var content: std.Io.Writer.Allocating = .init(allocator); |
| 541 | errdefer content.deinit(); |
| 542 | for (0..grid.size / 4 + 1) |y| { |
| 543 | for (0..grid.size / 2 + 1) |x| { |
| 544 | var b: B = std.mem.zeroes(B); |
| 545 | inline for (0..2) |x2| { |
| 546 | inline for (0..4) |y2| blk: { |
| 547 | const real_x = x * 2 + x2; |
| 548 | const real_y = y * 4 + y2; |
| 549 | if (real_x >= grid.size) break :blk; |
| 550 | if (real_y >= grid.size) break :blk; |
| 551 | const is_set = grid.isSet(real_x, real_y); |
| 552 | if (!is_set) break :blk; |
| 553 | @field(b, "01263457"[x2 * 4 + y2 ..][0..1]) = true; |
| 554 | } |
| 555 | } |
| 556 | var cp: u21 = 0x2800; |
| 557 | cp += @as(u8, @bitCast(b)); |
| 558 | var buf: [3]u8 = undefined; |
| 559 | _ = std.unicode.utf8Encode(cp, &buf) catch unreachable; |
| 560 | try content.writer.writeAll(&buf); |
| 561 | } |
| 562 | if (builtin.is_test) try content.writer.writeAll("|"); |
| 563 | try content.writer.writeAll("\n"); |
| 564 | } |
| 565 | return content.toOwnedSlice(); |
| 566 | } |
| 567 | |
| 568 | pub fn asSvg(grid: *const @This(), allocator: std.mem.Allocator) ![]u8 { |
| 569 | var content: std.Io.Writer.Allocating = .init(allocator); |
| 570 | errdefer content.deinit(); |
| 571 | try content.writer.print("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{d}0\" height=\"{d}0\" viewBox=\"-1 -1 {d} {d}\" shape-rendering=\"crispEdges\" stroke=\"#000\">\n", .{ size + 2, size + 1, size + 2, size + 1 }); |
| 572 | try content.writer.print("<rect x=\"-1\" y=\"-1\" width=\"{d}\" height=\"{d}\" fill=\"#fff\" stroke=\"none\" />\n", .{ size + 2, size + 1 }); |
| 573 | var prev = false; |
| 574 | var run: u16 = 0; |
| 575 | for (0..size) |y| { |
| 576 | try content.writer.writeAll("<path d=\""); |
| 577 | for (0..size) |x| { |
| 578 | if (x == 0) { |
| 579 | run = 0; |
| 580 | prev = grid.isSet(0, y); |
| 581 | } |
| 582 | const this = grid.isSet(x, y); |
| 583 | if (this == prev) { |
| 584 | run += 1; |
| 585 | } else { |
| 586 | if (prev) { |
| 587 | try content.writer.print("M{d},{d} h{d} ", .{ x - run, y, run }); |
| 588 | } |
| 589 | run = 1; |
| 590 | } |
| 591 | prev = this; |
| 592 | } |
| 593 | if (grid.isSet(size - 1, y)) { |
| 594 | const x = size; |
| 595 | try content.writer.print("M{d},{d} h{d}", .{ x - run, y, run }); |
| 596 | } |
| 597 | try content.writer.writeAll("\" />\n"); |
| 598 | } |
| 599 | try content.writer.writeAll("</svg>\n"); |
| 600 | return content.toOwnedSlice(); |
| 601 | } |
| 602 | }; |
| 603 | } |
| 604 | |
| 605 | fn BitsetWriter(MaskIntType: type, size: usize) type { |
| 606 | return struct { |
| 607 | set: *std.bit_set.ArrayBitSet(MaskIntType, size), |
| 608 | idx: usize = 0, |
| 609 | |
| 610 | pub fn writeInt(self: *@This(), int: anytype) void { |
| 611 | return writeInt2(self, @bitReverse(int)); |
| 612 | } |
| 613 | fn writeInt2(self: *@This(), int: anytype) void { |
| 614 | const T = @TypeOf(int); |
| 615 | const info = @typeInfo(T).int; |
| 616 | comptime std.debug.assert(info.signedness == .unsigned); |
| 617 | if (self.idx == size) { |
| 618 | return; |
| 619 | } |
| 620 | if (T == u1) { |
| 621 | self.set.setValue(self.idx, @bitCast(int)); |
| 622 | self.idx += 1; |
| 623 | return; |
| 624 | } |
| 625 | self.set.setValue(self.idx, (int & 1) == 1); |
| 626 | self.idx += 1; |
| 627 | return writeInt2(self, @as(extras.OneSmallerInt(T), @intCast(int >> 1))); |
| 628 | } |
| 629 | }; |
| 630 | } |
| 631 | |
| 632 | fn bitReverse(array: anytype) [array.len]std.meta.Child(@TypeOf(array)) { |
| 633 | var result: [array.len]std.meta.Child(@TypeOf(array)) = @splat(0); |
| 634 | for (&array, &result) |x, *y| y.* = @bitReverse(x); |
| 635 | return result; |
| 636 | } |
| 637 | |
| 638 | fn reverseArray(array: anytype) @TypeOf(array) { |
| 639 | var result: @TypeOf(array) = array; |
| 640 | for (0..result.len / 2) |i| { |
| 641 | std.mem.swap(std.meta.Child(@TypeOf(result)), &result[i], &result[result.len - 1 - i]); |
| 642 | } |
| 643 | return result; |
| 644 | } |
| 645 | |
| 646 | // |
| 647 | |
| 648 | pub const Mode = enum { |
| 649 | numeric, |
| 650 | alphanumeric, |
| 651 | latin1, |
| 652 | shiftjis, |
| 653 | }; |
| 654 | |
| 655 | pub const Level = enum { |
| 656 | L, |
| 657 | M, |
| 658 | Q, |
| 659 | H, |
| 660 | }; |
| 661 | |
| 662 | // Sourced from https://gist.github.com/ZavierHenry/a4b3761212109d5598bb2db79c334782 |
| 663 | pub fn maxLenPerModeLevelVersion(mode: Mode, level: Level) *const [41]u16 { |
| 664 | return switch (mode) { |
| 665 | .numeric => switch (level) { |
| 666 | .L => &.{ 0, 41, 77, 127, 187, 255, 322, 370, 461, 552, 652, 772, 883, 1022, 1101, 1250, 1408, 1548, 1725, 1903, 2061, 2232, 2409, 2620, 2812, 3057, 2383, 3517, 3669, 3909, 4158, 4417, 4686, 4965, 5253, 5529, 5836, 6153, 6479, 6743, 7089 }, |
| 667 | .M => &.{ 0, 34, 63, 101, 149, 202, 255, 293, 365, 432, 513, 694, 691, 796, 871, 991, 1082, 1212, 1346, 1500, 1600, 1708, 1872, 2059, 2188, 2395, 2544, 2701, 2857, 3035, 3289, 3486, 3693, 3909, 4134, 4343, 4588, 4775, 5039, 5313, 5596 }, |
| 668 | .Q => &.{ 0, 27, 48, 77, 111, 144, 178, 207, 259, 312, 364, 427, 489, 580, 621, 703, 775, 876, 948, 1063, 1159, 1224, 1358, 1468, 1588, 1718, 1804, 1933, 2085, 2181, 2358, 2473, 2670, 2805, 2949, 3081, 3244, 3417, 3599, 3791, 3993 }, |
| 669 | .H => &.{ 0, 17, 34, 58, 82, 106, 139, 154, 202, 235, 288, 331, 374, 427, 468, 530, 602, 674, 746, 813, 919, 969, 1056, 1108, 1228, 1286, 1425, 1501, 1581, 1677, 1782, 1897, 2022, 2157, 2301, 2361, 2524, 2625, 2735, 2927, 3057 }, |
| 670 | }, |
| 671 | .alphanumeric => switch (level) { |
| 672 | .L => &.{ 0, 25, 47, 77, 114, 154, 195, 224, 279, 335, 395, 468, 535, 619, 667, 758, 854, 938, 1046, 1153, 1249, 1352, 1460, 1588, 1704, 1853, 1990, 2132, 2223, 2369, 2520, 2677, 2840, 3009, 3183, 3351, 3537, 3729, 3927, 4087, 4296 }, |
| 673 | .M => &.{ 0, 20, 38, 61, 90, 122, 154, 178, 221, 262, 311, 366, 419, 483, 528, 600, 656, 734, 816, 909, 970, 1035, 1134, 1248, 1326, 1451, 1542, 1637, 1732, 1839, 1994, 2113, 2238, 2369, 2506, 2632, 2780, 2894, 3054, 3220, 3391 }, |
| 674 | .Q => &.{ 0, 16, 29, 47, 67, 87, 108, 125, 157, 189, 221, 259, 296, 352, 376, 426, 470, 531, 574, 644, 702, 742, 823, 890, 963, 1041, 1094, 1172, 1263, 1322, 1429, 1499, 1618, 1700, 1787, 1867, 1966, 2071, 2181, 2298, 2420 }, |
| 675 | .H => &.{ 0, 10, 20, 35, 50, 64, 84, 93, 122, 143, 174, 200, 227, 259, 283, 321, 365, 408, 452, 493, 557, 587, 640, 672, 744, 779, 864, 910, 958, 1016, 1080, 1150, 1226, 1307, 1394, 1431, 1530, 1591, 1658, 1774, 1852 }, |
| 676 | }, |
| 677 | .latin1 => switch (level) { |
| 678 | .L => &.{ 0, 17, 32, 53, 78, 106, 134, 154, 192, 230, 271, 321, 367, 425, 458, 520, 586, 644, 718, 792, 858, 929, 1003, 1091, 1171, 1273, 1367, 1465, 1528, 1628, 1732, 1840, 1952, 2068, 2188, 2303, 2431, 2563, 2699, 2809, 2953 }, |
| 679 | .M => &.{ 0, 14, 26, 42, 62, 84, 106, 122, 152, 180, 213, 251, 287, 331, 362, 412, 450, 504, 560, 624, 666, 711, 779, 857, 911, 997, 1059, 1125, 1190, 1264, 1370, 1452, 1538, 1628, 1722, 1809, 1911, 1989, 2099, 2213, 2331 }, |
| 680 | .Q => &.{ 0, 11, 20, 32, 46, 60, 74, 86, 108, 130, 151, 177, 203, 241, 258, 292, 322, 364, 394, 442, 482, 509, 565, 611, 661, 715, 751, 805, 868, 908, 982, 1030, 1112, 1168, 1228, 1283, 1351, 1423, 1499, 1579, 1663 }, |
| 681 | .H => &.{ 0, 7, 14, 24, 34, 44, 58, 64, 84, 98, 119, 137, 155, 177, 194, 220, 250, 280, 310, 338, 382, 403, 439, 461, 511, 535, 593, 625, 658, 698, 742, 790, 842, 898, 958, 983, 1051, 1093, 1139, 1219, 1273 }, |
| 682 | }, |
| 683 | .shiftjis => switch (level) { |
| 684 | .L => &.{ 0, 10, 20, 32, 48, 65, 82, 95, 118, 141, 167, 198, 226, 262, 282, 320, 361, 397, 442, 488, 528, 572, 618, 672, 721, 784, 842, 902, 940, 1002, 1066, 1132, 1201, 1273, 1347, 1417, 1496, 1577, 1661, 1729, 1817 }, |
| 685 | .M => &.{ 0, 8, 16, 26, 38, 52, 65, 75, 93, 111, 131, 155, 177, 204, 223, 254, 277, 310, 345, 384, 410, 438, 480, 528, 561, 614, 652, 692, 732, 778, 843, 894, 947, 1002, 1060, 1113, 1176, 1224, 1292, 1362, 1435 }, |
| 686 | .Q => &.{ 0, 7, 12, 20, 28, 37, 45, 53, 66, 80, 93, 109, 125, 149, 159, 180, 198, 224, 243, 272, 297, 314, 348, 376, 407, 440, 462, 496, 534, 559, 604, 634, 684, 719, 756, 790, 832, 876, 923, 972, 1024 }, |
| 687 | .H => &.{ 0, 4, 8, 15, 21, 27, 36, 39, 52, 60, 74, 85, 96, 109, 120, 136, 154, 173, 191, 208, 235, 248, 270, 284, 315, 330, 365, 385, 405, 430, 457, 486, 518, 553, 590, 605, 647, 673, 701, 750, 784 }, |
| 688 | }, |
| 689 | }; |
| 690 | } |
| 691 | |
| 692 | fn isNumeric(c: u8) bool { |
| 693 | return switch (c) { |
| 694 | '0'...'9' => true, |
| 695 | else => false, |
| 696 | }; |
| 697 | } |
| 698 | |
| 699 | fn isAlphanumeric(c: u8) bool { |
| 700 | return switch (c) { |
| 701 | '0'...'9' => true, |
| 702 | 'A'...'Z' => true, |
| 703 | ' ', '$', '%', '*', '+', '-', '.', '/', ':' => true, |
| 704 | else => false, |
| 705 | }; |
| 706 | } |
| 707 | |
| 708 | fn generatorPolynomial(comptime n: usize) [n + 1]u8 { |
| 709 | if (n == 1) return .{ 1, 1 }; |
| 710 | |
| 711 | const prev = generatorPolynomial(n - 1); |
| 712 | const prev_alpha = to_alpha(prev); |
| 713 | const term_alpha: [2]u8 = .{ n - 1, 0 }; |
| 714 | |
| 715 | var multiply_alpha: [n * 2][2]u8 = @splat(@splat(0)); |
| 716 | for (&prev_alpha, 0..) |a, i| { |
| 717 | for (&term_alpha, 0..) |b, j| { |
| 718 | multiply_alpha[(term_alpha.len * i) + j] = .{ @intCast((@as(u16, a) + b) % 255), @intCast(i + j) }; |
| 719 | } |
| 720 | } |
| 721 | var combine_alpha: [n + 1]u8 = @splat(0); |
| 722 | for (0..n + 1) |i| { |
| 723 | for (&multiply_alpha) |el| { |
| 724 | if (el[1] == i) combine_alpha[i] ^= GF[el[0]]; |
| 725 | } |
| 726 | } |
| 727 | return combine_alpha; |
| 728 | } |
| 729 | |
| 730 | fn to_alpha(array: anytype) [array.len]u8 { |
| 731 | var result: [array.len]u8 = array; |
| 732 | for (&result) |*x| x.* = @intCast(std.mem.indexOfScalar(u8, &GF, x.*) orelse 0); |
| 733 | return result; |
| 734 | } |
| 735 | |
| 736 | fn formatPattern(level: Level, mask: u3) u15 { |
| 737 | return switch (level) { |
| 738 | .L => switch (mask) { |
| 739 | 0 => 0b111011111000100, |
| 740 | 1 => 0b111001011110011, |
| 741 | 2 => 0b111110110101010, |
| 742 | 3 => 0b111100010011101, |
| 743 | 4 => 0b110011000101111, |
| 744 | 5 => 0b110001100011000, |
| 745 | 6 => 0b110110001000001, |
| 746 | 7 => 0b110100101110110, |
| 747 | }, |
| 748 | .M => switch (mask) { |
| 749 | 0 => 0b101010000010010, |
| 750 | 1 => 0b101000100100101, |
| 751 | 2 => 0b101111001111100, |
| 752 | 3 => 0b101101101001011, |
| 753 | 4 => 0b100010111111001, |
| 754 | 5 => 0b100000011001110, |
| 755 | 6 => 0b100111110010111, |
| 756 | 7 => 0b100101010100000, |
| 757 | }, |
| 758 | .Q => switch (mask) { |
| 759 | 0 => 0b011010101011111, |
| 760 | 1 => 0b011000001101000, |
| 761 | 2 => 0b011111100110001, |
| 762 | 3 => 0b011101000000110, |
| 763 | 4 => 0b010010010110100, |
| 764 | 5 => 0b010000110000011, |
| 765 | 6 => 0b010111011011010, |
| 766 | 7 => 0b010101111101101, |
| 767 | }, |
| 768 | .H => switch (mask) { |
| 769 | 0 => 0b001011010001001, |
| 770 | 1 => 0b001001110111110, |
| 771 | 2 => 0b001110011100111, |
| 772 | 3 => 0b001100111010000, |
| 773 | 4 => 0b000011101100010, |
| 774 | 5 => 0b000001001010101, |
| 775 | 6 => 0b000110100001100, |
| 776 | 7 => 0b000100000111011, |
| 777 | }, |
| 778 | }; |
| 779 | } |
| 780 | |
| 781 | fn errorCorrection(level: Level, version: u16) ErrorCorrectionEntry { |
| 782 | return switch (level) { |
| 783 | .L => &.{ |
| 784 | std.mem.zeroes(ErrorCorrectionEntry), |
| 785 | .{ 19, 7, 1, 19, null, null }, |
| 786 | .{ 34, 10, 1, 34, null, null }, |
| 787 | .{ 55, 15, 1, 55, null, null }, |
| 788 | .{ 80, 20, 1, 80, null, null }, |
| 789 | .{ 108, 26, 1, 108, null, null }, |
| 790 | .{ 136, 18, 2, 68, null, null }, |
| 791 | .{ 156, 20, 2, 78, null, null }, |
| 792 | .{ 194, 24, 2, 97, null, null }, |
| 793 | .{ 232, 30, 2, 116, null, null }, |
| 794 | .{ 274, 18, 2, 68, 2, 69 }, |
| 795 | .{ 324, 20, 4, 81, null, null }, |
| 796 | .{ 370, 24, 2, 92, 2, 93 }, |
| 797 | .{ 428, 26, 4, 107, null, null }, |
| 798 | .{ 461, 30, 3, 115, 1, 116 }, |
| 799 | .{ 523, 22, 5, 87, 1, 88 }, |
| 800 | .{ 589, 24, 5, 98, 1, 99 }, |
| 801 | .{ 647, 28, 1, 107, 5, 108 }, |
| 802 | .{ 721, 30, 5, 120, 1, 121 }, |
| 803 | .{ 795, 28, 3, 113, 4, 114 }, |
| 804 | .{ 861, 28, 3, 107, 5, 108 }, |
| 805 | .{ 932, 28, 4, 116, 4, 117 }, |
| 806 | .{ 1006, 28, 2, 111, 7, 112 }, |
| 807 | .{ 1094, 30, 4, 121, 5, 122 }, |
| 808 | .{ 1174, 30, 6, 117, 4, 118 }, |
| 809 | .{ 1276, 26, 8, 106, 4, 107 }, |
| 810 | .{ 1370, 28, 10, 114, 2, 115 }, |
| 811 | .{ 1468, 30, 8, 122, 4, 123 }, |
| 812 | .{ 1531, 30, 3, 117, 10, 118 }, |
| 813 | .{ 1631, 30, 7, 116, 7, 117 }, |
| 814 | .{ 1735, 30, 5, 115, 10, 116 }, |
| 815 | .{ 1843, 30, 13, 115, 3, 116 }, |
| 816 | .{ 1955, 30, 17, 115, null, null }, |
| 817 | .{ 2071, 30, 17, 115, 1, 116 }, |
| 818 | .{ 2191, 30, 13, 115, 6, 116 }, |
| 819 | .{ 2306, 30, 12, 121, 7, 122 }, |
| 820 | .{ 2434, 30, 6, 121, 14, 122 }, |
| 821 | .{ 2566, 30, 17, 122, 4, 123 }, |
| 822 | .{ 2702, 30, 4, 122, 18, 123 }, |
| 823 | .{ 2812, 30, 20, 117, 4, 118 }, |
| 824 | .{ 2956, 30, 19, 118, 6, 119 }, |
| 825 | }, |
| 826 | .M => &.{ |
| 827 | std.mem.zeroes(ErrorCorrectionEntry), |
| 828 | .{ 16, 10, 1, 16, null, null }, |
| 829 | .{ 28, 16, 1, 28, null, null }, |
| 830 | .{ 44, 26, 1, 44, null, null }, |
| 831 | .{ 64, 18, 2, 32, null, null }, |
| 832 | .{ 86, 24, 2, 43, null, null }, |
| 833 | .{ 108, 16, 4, 27, null, null }, |
| 834 | .{ 124, 18, 4, 31, null, null }, |
| 835 | .{ 154, 22, 2, 38, 2, 39 }, |
| 836 | .{ 182, 22, 3, 36, 2, 37 }, |
| 837 | .{ 216, 26, 4, 43, 1, 44 }, |
| 838 | .{ 254, 30, 1, 50, 4, 51 }, |
| 839 | .{ 290, 22, 6, 36, 2, 37 }, |
| 840 | .{ 334, 22, 8, 37, 1, 38 }, |
| 841 | .{ 365, 24, 4, 40, 5, 41 }, |
| 842 | .{ 415, 24, 5, 41, 5, 42 }, |
| 843 | .{ 453, 28, 7, 45, 3, 46 }, |
| 844 | .{ 507, 28, 10, 46, 1, 47 }, |
| 845 | .{ 563, 26, 9, 43, 4, 44 }, |
| 846 | .{ 627, 26, 3, 44, 11, 45 }, |
| 847 | .{ 669, 26, 3, 41, 13, 42 }, |
| 848 | .{ 714, 26, 17, 42, null, null }, |
| 849 | .{ 782, 28, 17, 46, null, null }, |
| 850 | .{ 860, 28, 4, 47, 14, 48 }, |
| 851 | .{ 914, 28, 6, 45, 14, 46 }, |
| 852 | .{ 1000, 28, 8, 47, 13, 48 }, |
| 853 | .{ 1062, 28, 19, 46, 4, 47 }, |
| 854 | .{ 1128, 28, 22, 45, 3, 46 }, |
| 855 | .{ 1193, 28, 3, 45, 23, 46 }, |
| 856 | .{ 1267, 28, 21, 45, 7, 46 }, |
| 857 | .{ 1373, 28, 19, 47, 10, 48 }, |
| 858 | .{ 1455, 28, 2, 46, 29, 47 }, |
| 859 | .{ 1541, 28, 10, 46, 23, 47 }, |
| 860 | .{ 1631, 28, 14, 46, 21, 47 }, |
| 861 | .{ 1725, 28, 14, 46, 23, 47 }, |
| 862 | .{ 1812, 28, 12, 47, 26, 48 }, |
| 863 | .{ 1914, 28, 6, 47, 34, 48 }, |
| 864 | .{ 1992, 28, 29, 46, 14, 47 }, |
| 865 | .{ 2102, 28, 13, 46, 32, 47 }, |
| 866 | .{ 2216, 28, 40, 47, 7, 48 }, |
| 867 | .{ 2334, 28, 18, 47, 31, 48 }, |
| 868 | }, |
| 869 | .Q => &.{ |
| 870 | std.mem.zeroes(ErrorCorrectionEntry), |
| 871 | .{ 13, 13, 1, 13, null, null }, |
| 872 | .{ 22, 22, 1, 22, null, null }, |
| 873 | .{ 34, 18, 2, 17, null, null }, |
| 874 | .{ 48, 26, 2, 24, null, null }, |
| 875 | .{ 62, 18, 2, 15, 2, 16 }, |
| 876 | .{ 76, 24, 4, 19, null, null }, |
| 877 | .{ 88, 18, 2, 14, 4, 15 }, |
| 878 | .{ 110, 22, 4, 18, 2, 19 }, |
| 879 | .{ 132, 20, 4, 16, 4, 17 }, |
| 880 | .{ 154, 24, 6, 19, 2, 20 }, |
| 881 | .{ 180, 28, 4, 22, 4, 23 }, |
| 882 | .{ 206, 26, 4, 20, 6, 21 }, |
| 883 | .{ 244, 24, 8, 20, 4, 21 }, |
| 884 | .{ 261, 20, 11, 16, 5, 17 }, |
| 885 | .{ 295, 30, 5, 24, 7, 25 }, |
| 886 | .{ 325, 24, 15, 19, 2, 20 }, |
| 887 | .{ 367, 28, 1, 22, 15, 23 }, |
| 888 | .{ 397, 28, 17, 22, 1, 23 }, |
| 889 | .{ 445, 26, 17, 21, 4, 22 }, |
| 890 | .{ 485, 30, 15, 24, 5, 25 }, |
| 891 | .{ 512, 28, 17, 22, 6, 23 }, |
| 892 | .{ 568, 30, 7, 24, 16, 25 }, |
| 893 | .{ 614, 30, 11, 24, 14, 25 }, |
| 894 | .{ 664, 30, 11, 24, 16, 25 }, |
| 895 | .{ 718, 30, 7, 24, 22, 25 }, |
| 896 | .{ 754, 28, 28, 22, 6, 23 }, |
| 897 | .{ 808, 30, 8, 23, 26, 24 }, |
| 898 | .{ 871, 30, 4, 24, 31, 25 }, |
| 899 | .{ 911, 30, 1, 23, 37, 24 }, |
| 900 | .{ 985, 30, 15, 24, 25, 25 }, |
| 901 | .{ 1033, 30, 42, 24, 1, 25 }, |
| 902 | .{ 1115, 30, 10, 24, 35, 25 }, |
| 903 | .{ 1171, 30, 29, 24, 19, 25 }, |
| 904 | .{ 1231, 30, 44, 24, 7, 25 }, |
| 905 | .{ 1286, 30, 39, 24, 14, 25 }, |
| 906 | .{ 1354, 30, 46, 24, 10, 25 }, |
| 907 | .{ 1426, 30, 49, 24, 10, 25 }, |
| 908 | .{ 1502, 30, 48, 24, 14, 25 }, |
| 909 | .{ 1582, 30, 43, 24, 22, 25 }, |
| 910 | .{ 1666, 30, 34, 24, 34, 25 }, |
| 911 | }, |
| 912 | .H => &.{ |
| 913 | std.mem.zeroes(ErrorCorrectionEntry), |
| 914 | .{ 9, 17, 1, 9, null, null }, |
| 915 | .{ 16, 28, 1, 16, null, null }, |
| 916 | .{ 26, 22, 2, 13, null, null }, |
| 917 | .{ 36, 16, 4, 9, null, null }, |
| 918 | .{ 46, 22, 2, 11, 2, 12 }, |
| 919 | .{ 60, 28, 4, 15, null, null }, |
| 920 | .{ 66, 26, 4, 13, 1, 14 }, |
| 921 | .{ 86, 26, 4, 14, 2, 15 }, |
| 922 | .{ 100, 24, 4, 12, 4, 13 }, |
| 923 | .{ 122, 28, 6, 15, 2, 16 }, |
| 924 | .{ 140, 24, 3, 12, 8, 13 }, |
| 925 | .{ 158, 28, 7, 14, 4, 15 }, |
| 926 | .{ 180, 22, 12, 11, 4, 12 }, |
| 927 | .{ 197, 24, 11, 12, 5, 13 }, |
| 928 | .{ 223, 24, 11, 12, 7, 13 }, |
| 929 | .{ 253, 30, 3, 15, 13, 16 }, |
| 930 | .{ 283, 28, 2, 14, 17, 15 }, |
| 931 | .{ 313, 28, 2, 14, 19, 15 }, |
| 932 | .{ 341, 26, 9, 13, 16, 14 }, |
| 933 | .{ 385, 28, 15, 15, 10, 16 }, |
| 934 | .{ 406, 30, 19, 16, 6, 17 }, |
| 935 | .{ 442, 24, 34, 13, null, null }, |
| 936 | .{ 464, 30, 16, 15, 14, 16 }, |
| 937 | .{ 514, 30, 30, 16, 2, 17 }, |
| 938 | .{ 538, 30, 22, 15, 13, 16 }, |
| 939 | .{ 596, 30, 33, 16, 4, 17 }, |
| 940 | .{ 628, 30, 12, 15, 28, 16 }, |
| 941 | .{ 661, 30, 11, 15, 31, 16 }, |
| 942 | .{ 701, 30, 19, 15, 26, 16 }, |
| 943 | .{ 745, 30, 23, 15, 25, 16 }, |
| 944 | .{ 793, 30, 23, 15, 28, 16 }, |
| 945 | .{ 845, 30, 19, 115, 35, 16 }, |
| 946 | .{ 901, 30, 11, 15, 46, 16 }, |
| 947 | .{ 961, 30, 59, 16, 1, 17 }, |
| 948 | .{ 986, 30, 22, 15, 41, 16 }, |
| 949 | .{ 1054, 30, 2, 15, 64, 16 }, |
| 950 | .{ 1096, 30, 24, 15, 46, 16 }, |
| 951 | .{ 1142, 30, 42, 15, 32, 16 }, |
| 952 | .{ 1222, 30, 10, 15, 67, 16 }, |
| 953 | .{ 1276, 30, 20, 15, 61, 16 }, |
| 954 | }, |
| 955 | }[version]; |
| 956 | } |
| 957 | |
| 958 | const ErrorCorrectionEntry = struct { |
| 959 | /// dataCodewordsCount |
| 960 | u32, |
| 961 | /// ecCodewordsPerBlock |
| 962 | u32, |
| 963 | /// groupOneBlockCount |
| 964 | u32, |
| 965 | /// groupOneDataCodewordsCountPerBlock |
| 966 | u32, |
| 967 | /// groupTwoBlockCount |
| 968 | ?u32, |
| 969 | /// groupTwoDataCodewordsCountPerBlock |
| 970 | ?u32, |
| 971 | }; |
| 972 | |
| 973 | fn remainderBitsCount(version: u16) u8 { |
| 974 | return .{ 0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 }[version]; |
| 975 | } |
| 976 | |
| 977 | fn alignmentPatternCenters(version: u16) []const u8 { |
| 978 | return .{ |
| 979 | &.{}, |
| 980 | &.{}, |
| 981 | &.{ 6, 18 }, |
| 982 | &.{ 6, 22 }, |
| 983 | &.{ 6, 26 }, |
| 984 | &.{ 6, 30 }, |
| 985 | &.{ 6, 34 }, |
| 986 | &.{ 6, 22, 38 }, |
| 987 | &.{ 6, 24, 42 }, |
| 988 | &.{ 6, 26, 46 }, |
| 989 | &.{ 6, 28, 50 }, |
| 990 | &.{ 6, 30, 54 }, |
| 991 | &.{ 6, 32, 58 }, |
| 992 | &.{ 6, 34, 62 }, |
| 993 | &.{ 6, 26, 46, 66 }, |
| 994 | &.{ 6, 26, 48, 70 }, |
| 995 | &.{ 6, 26, 50, 74 }, |
| 996 | &.{ 6, 30, 54, 78 }, |
| 997 | &.{ 6, 30, 56, 82 }, |
| 998 | &.{ 6, 30, 58, 86 }, |
| 999 | &.{ 6, 34, 62, 90 }, |
| 1000 | &.{ 6, 28, 50, 72, 94 }, |
| 1001 | &.{ 6, 26, 50, 74, 98 }, |
| 1002 | &.{ 6, 30, 54, 78, 102 }, |
| 1003 | &.{ 6, 28, 54, 80, 106 }, |
| 1004 | &.{ 6, 32, 58, 84, 110 }, |
| 1005 | &.{ 6, 30, 58, 86, 114 }, |
| 1006 | &.{ 6, 34, 62, 90, 118 }, |
| 1007 | &.{ 6, 26, 50, 74, 98, 122 }, |
| 1008 | &.{ 6, 30, 54, 78, 102, 126 }, |
| 1009 | &.{ 6, 26, 52, 78, 104, 130 }, |
| 1010 | &.{ 6, 30, 56, 82, 108, 134 }, |
| 1011 | &.{ 6, 34, 60, 86, 112, 138 }, |
| 1012 | &.{ 6, 30, 58, 86, 114, 142 }, |
| 1013 | &.{ 6, 32, 62, 90, 118, 146 }, |
| 1014 | &.{ 6, 30, 54, 78, 102, 126, 150 }, |
| 1015 | &.{ 6, 24, 50, 76, 102, 128, 154 }, |
| 1016 | &.{ 6, 28, 54, 80, 106, 132, 158 }, |
| 1017 | &.{ 6, 32, 58, 84, 110, 136, 162 }, |
| 1018 | &.{ 6, 26, 54, 82, 110, 138, 166 }, |
| 1019 | &.{ 6, 30, 58, 86, 114, 142, 170 }, |
| 1020 | }[version]; |
| 1021 | } |
| 1022 | |
| 1023 | fn versionBits(version: u16) u18 { |
| 1024 | return ([_]u18{ |
| 1025 | 0, |
| 1026 | 0, |
| 1027 | 0, |
| 1028 | 0, |
| 1029 | 0, |
| 1030 | 0, |
| 1031 | 0, |
| 1032 | 0b000111110010010100, |
| 1033 | 0b001000010110111100, |
| 1034 | 0b001001101010011001, |
| 1035 | 0b001010010011010011, |
| 1036 | 0b001011101111110110, |
| 1037 | 0b001100011101100010, |
| 1038 | 0b001101100001000111, |
| 1039 | 0b001110011000001101, |
| 1040 | 0b001111100100101000, |
| 1041 | 0b010000101101111000, |
| 1042 | 0b010001010001011101, |
| 1043 | 0b010010101000010111, |
| 1044 | 0b010011010100110010, |
| 1045 | 0b010100100110100110, |
| 1046 | 0b010101011010000011, |
| 1047 | 0b010110100011001001, |
| 1048 | 0b010111011111101100, |
| 1049 | 0b011000111011000100, |
| 1050 | 0b011001000111100001, |
| 1051 | 0b011010111110101011, |
| 1052 | 0b011011000010001110, |
| 1053 | 0b011100110000011010, |
| 1054 | 0b011101001100111111, |
| 1055 | 0b011110110101110101, |
| 1056 | 0b011111001001010000, |
| 1057 | 0b100000100111010101, |
| 1058 | 0b100001011011110000, |
| 1059 | 0b100010100010111010, |
| 1060 | 0b100011011110011111, |
| 1061 | 0b100100101100001011, |
| 1062 | 0b100101010000101110, |
| 1063 | 0b100110101001100100, |
| 1064 | 0b100111010101000001, |
| 1065 | 0b101000110001101001, |
| 1066 | })[version]; |
| 1067 | } |
| 1068 | |
| 1069 | pub fn getMinVersion(comptime level: Level, comptime mode: Mode, length: usize) ?u16 { |
| 1070 | const max_lengths = maxLenPerModeLevelVersion(mode, level); |
| 1071 | for (max_lengths, 0..) |l, i| { |
| 1072 | if (l > length) return @intCast(i); |
| 1073 | } |
| 1074 | return null; |
| 1075 | } |
| 1076 | |
| 1077 | // |
| 1078 | // |
| 1079 | // |
| 1080 | // |
| 1081 | |
| 1082 | const BufBiterator = struct { |
| 1083 | buf: []const u8, |
| 1084 | bits: std.bit_set.IntegerBitSet(8), |
| 1085 | idx: u32, // buf index |
| 1086 | jdx: u8, // bits index |
| 1087 | |
| 1088 | pub fn init(buf: []const u8) BufBiterator { |
| 1089 | std.debug.assert(buf.len > 0); |
| 1090 | return .{ |
| 1091 | .buf = buf, |
| 1092 | .bits = .{ .mask = buf[0] }, |
| 1093 | .idx = 0, |
| 1094 | .jdx = 0, |
| 1095 | }; |
| 1096 | } |
| 1097 | |
| 1098 | pub fn next(self: *BufBiterator) ?u1 { |
| 1099 | if (self.jdx == 8) { |
| 1100 | self.jdx = 0; |
| 1101 | self.idx += 1; |
| 1102 | if (self.idx == self.buf.len) return null; |
| 1103 | self.bits.mask = self.buf[self.idx]; |
| 1104 | return self.next(); |
| 1105 | } |
| 1106 | const result = self.bits.isSet(7 - self.jdx); |
| 1107 | self.jdx += 1; |
| 1108 | return @intFromBool(result); |
| 1109 | } |
| 1110 | }; |
| 1111 | |
| 1112 | // |
| 1113 | // |
| 1114 | // |
| 1115 | // |
| 1116 | // |
| 1117 | |
| 1118 | const GridZigZag = struct { |
| 1119 | grid_size: usize, |
| 1120 | col_skip: usize, |
| 1121 | dir: u1, |
| 1122 | phase: u1, |
| 1123 | y: usize, |
| 1124 | x: usize, |
| 1125 | |
| 1126 | pub fn init(grid_size: usize, col_skip: usize) GridZigZag { |
| 1127 | return .{ |
| 1128 | .grid_size = grid_size, |
| 1129 | .col_skip = col_skip, |
| 1130 | .dir = 0, |
| 1131 | .phase = 0, |
| 1132 | .y = grid_size, |
| 1133 | .x = grid_size - 2, |
| 1134 | }; |
| 1135 | } |
| 1136 | |
| 1137 | pub fn next(self: *@This()) ?usize { |
| 1138 | blk: switch (self.dir) { |
| 1139 | 0 => switch (self.phase) { //up |
| 1140 | 0 => { //prev left |
| 1141 | if (self.y == 0) { |
| 1142 | self.x -= 1; |
| 1143 | self.dir = 1; |
| 1144 | self.phase = 1; |
| 1145 | if (self.x == self.col_skip and self.col_skip == 0) return null; |
| 1146 | if (self.x == self.col_skip) self.x -= 1; |
| 1147 | break :blk; |
| 1148 | } |
| 1149 | self.y -= 1; |
| 1150 | self.x += 1; |
| 1151 | self.phase +%= 1; |
| 1152 | }, |
| 1153 | 1 => { //prev right |
| 1154 | self.x -= 1; |
| 1155 | self.phase +%= 1; |
| 1156 | }, |
| 1157 | }, |
| 1158 | 1 => switch (self.phase) { //down |
| 1159 | 0 => { //prev left |
| 1160 | if (self.y == self.grid_size - 1) { |
| 1161 | if (self.x == 0) return null; |
| 1162 | self.x -= 1; |
| 1163 | if (self.x == self.col_skip and self.col_skip == 0) return null; |
| 1164 | if (self.x == self.col_skip) self.x -= 1; |
| 1165 | self.dir = 0; |
| 1166 | self.phase = 1; |
| 1167 | break :blk; |
| 1168 | } |
| 1169 | self.x += 1; |
| 1170 | self.y += 1; |
| 1171 | self.phase +%= 1; |
| 1172 | }, |
| 1173 | 1 => { //prev right |
| 1174 | self.x -= 1; |
| 1175 | self.phase +%= 1; |
| 1176 | }, |
| 1177 | }, |
| 1178 | } |
| 1179 | return (self.y * self.grid_size) + self.x; |
| 1180 | } |
| 1181 | }; |