SL36FMAKKVKYSG4P2SQBPUKU5WRNUFYDLAPCZHO7DNFQZEKG4AIQC
const min = @import("std").math.min;
const max = @import("std").math.max;
pub const HELPER = [_]u9{ 0o000, 0o000, 0o200, 0o201, 0o300, 0o301, 0o401, 0o400, 0o600, 0o601, 0o700, 0o701 };
pub const INDEX = @import("utils").make_array([4]u9, index);
fn index(pos: u6) [4]u9 {
const a = @truncate(u3, pos);
const b = @truncate(u3, pos >> 3);
const c = switch (a) {
3, 4 => a + @as(u4, if (b > 3) 7 else 5),
else => switch (pos) {
2, 10, 53, 61 => a,
else => min(b, max(a, 7 - a)),
},
};
return .{
HELPER[a],
HELPER[b],
HELPER[c],
0,
};
}
const pdep = @import("intrinsic.zig").pdep;
const pext = @import("intrinsic.zig").pext;
const MASK = @import("mask.zig").MASK;
const INDEX = @import("index.zig").INDEX;
const RESULT = @import("result.zig").RESULT;
pub fn flip(board: [2]u64, place: u6) u64 {
var ret: u64 = 0;
comptime var i = 0;
inline while (i < 3) : (i += 1)
ret |= pdep(RESULT[
@as(u64, INDEX[place][i]) * 32 + pext(board[0], MASK[place][i][0]) * 64 + pext(board[1], MASK[place][i][1])
], MASK[place][i][1]);
// plan to further shave 2KiB
// ret |= pdep(RESULT[
// @as(u64, INDEX[place][2]) * 32 | pext(board[0], MASK[place][i][0]) * 64 + pext(board[1], MASK[place][i][1])
// ], MASK[place][i][1]);
ret |= (((board[0] & MASK[place][3][0]) *% 0x05_0005) >> 9) & board[1] & MASK[place][3][1];
return ret;
}
pub fn prefetch() void {
@prefetch(&MASK, .{});
@prefetch(&INDEX, .{});
@prefetch(&RESULT, .{});
}
test {
_ = @import("test.zig");
_ = @import("index.zig");
}
pub const MASK = @import("utils").make_array([4][2]u64, _mask);
const mul = @import("utils").mul;
const fill = @import("utils").fill;
fn mask(pos: u6, comptime dir: u6) @Vector(2, u64) {
const t = @as(u64, 1) << pos;
const a = t | t << 8 | t >> 8;
const b = a | (a << 1 & mul(0xFF, 0xFE)) | (a >> 1 & mul(0xFF, 0x7F));
const r = fill(t, dir);
return .{ r & ~b, r & ~t & switch (dir) {
1 => mul(0xFF, 0x7E),
8 => mul(0x7E, 0xFF),
7, 9 => mul(0x7E, 0x7E),
else => unreachable,
} };
}
fn _mask(pos: u6) [4][2]u64 {
// we don't support this case
// zero for faster error finding
if (@truncate(u1, mul(0x18, 0x18) >> pos) != 0) {
return [_][2]u64{.{ 0, 0 }} ** 4;
}
const t: u64 = switch (pos) {
2, 10, 53, 61 => 0,
19, 20 => mul(0x07, 0xFF),
43, 44 => mul(0xE0, 0xFF),
else => switch (pos & 7) {
2 => mul(0xFF, 0x07),
5 => mul(0xFF, 0xE0),
else => 0,
},
};
const temp = mask(pos, 7) | mask(pos, 9);
const tt = @splat(2, t);
return .{ mask(pos, 1), mask(pos, 8), temp & ~tt, temp & tt };
}
fn res(i: u3, p: u7, n: u7) u7 {
const m = (@as(u7, 1) << i) - 1;
return m & -%(@as(u7, 1) << (7 - @clz(~n & m)) & p) |
((n >> i) + 1 & p >> i << 1) -| 1 << i;
}
const gen_mask = [4]u5{ 0b00101, 0b01010, 0b01010, 0b10100 };
fn special(i: u2, p: u5, n: u5) u5 {
const mask = gen_mask[i];
if (i < 2) {
return gen(mask, p, n, true) | gen(~mask, p, n, true);
} else {
return gen(mask, p, n, false) | gen(~mask, p, n, false);
}
}
fn gen(mask: u5, p: u5, n: u5, is_up: bool) u5 {
const nn = ~n & mask;
const nnn = mask & ~if (is_up)
-%(nn & -%nn)
else
@as(u6, 31) >> @clz(nn);
if (nnn & p == 0) return 0;
return @intCast(u5, nnn);
}
pub const RESULT = init: {
@setEvalBranchQuota(1000000);
var ret: [0x4000]u6 = undefined;
for (@import("index.zig").HELPER) |i, index| {
const range: u7 = switch (index) {
0, 7 => 64,
else => 32,
};
var j: u7 = 0;
while (j < range) : (j += 1) {
var k: u7 = 0;
while (k < range) : (k += 1) {
const ii = @as(u64, i + 2 * j) * 32 + k;
if (index < 8) {
const ind: u3 = @intCast(u3, index -| 1);
ret[ii] = @intCast(u6, res(ind, j, k));
} else {
ret[ii] = special(@intCast(u2, index - 8), j, k);
}
}
}
}
break :init ret;
};