-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patht1.zig
322 lines (295 loc) · 10.4 KB
/
t1.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
// Typing the technical interview
// https://aphyr.com/posts/342-typing-the-technical-interview
// https://github.com/insou22/typing-the-technical-interview-rust
const std = @import("std");
const expectEqual = std.testing.expectEqual;
inline fn nth_child(comptime n: comptime_int, comptime T: type) type {
//return std.meta.FieldType(T, .a);
return @typeInfo(T).Struct.fields[n].field_type;
}
// We can't directly pattern-match on generics like Queen(A,B)
// so we use a hack: store the 'constructor'/'generic' function as a decl
// This is only necessary for Apply
// List
const Nil = struct {
const ctor = Cons;
};
fn Cons(comptime X: type, comptime Xs: type) type {
return struct {
x: X,
xs: Xs,
const ctor = Cons;
};
}
// precondition: T is list (Cons)
fn First(comptime T: type) type {
//if (!(@hasDecl(T, "ctor") and T.ctor == Cons)) unreachable;
return if (T == Nil) Nil else nth_child(0, T);
}
fn Second(comptime T: type) type {
//if (!(@hasDecl(T, "ctor") and T.ctor == Cons)) unreachable;
return if (T == Nil) Nil else nth_child(1, T);
}
// precondition: A, B are lists (Cons)
fn ListConcat(comptime A: type, comptime B: type) type {
//if (!(@hasDecl(A, "ctor") and A.ctor == Cons)) unreachable;
return if (A == Nil) B else Cons(First(A), ListConcat(Second(A), B));
}
fn ListConcatAll(comptime A: type) type {
//if (!(@hasDecl(A, "ctor") and A.ctor == Cons)) unreachable;
return if (A == Nil) Nil else ListConcat(First(A), ListConcatAll(Second(A)));
}
//test
//const L1 = Cons(N1, Nil);
//const L21 = Cons(N2, L1);
//const LL1L21 = Cons(L1, Cons(L21, Nil));
//const L121 = ListConcatAll(LL1L21);
//const L121b = Cons(N1, L21);
fn AnyTrue(comptime L: type) type {
return if (L == Nil) False else if (First(L) == True) True else AnyTrue(Second(L));
}
const True = struct {};
const False = struct {};
fn Not(comptime A: type) type {
//return if (A==True) False else if (A==False) True else unreachable;
return switch (A) {
True => False,
False => True,
else => unreachable,
};
}
fn Or(comptime A: type, comptime B: type) type {
return if (A == False and B == False) False else True;
}
const Z = struct {};
fn S(comptime T: type) type {
return struct { x: T };
}
const N1 = S(Z);
const N2 = S(N1);
const N3 = S(N2);
const N4 = S(N3);
const N5 = S(N4);
const N6 = S(N5);
const N7 = S(N6);
const N8 = S(N7);
fn P(comptime T: type) type {
return @typeInfo(T).Struct.fields[0].field_type;
}
fn PeanoEqual(comptime A: type, comptime B: type) type {
return if (A == B) True else False;
}
fn PeanoLT(comptime A: type, comptime B: type) type {
return if (B == Z) False else if (A == Z) True else PeanoLT(P(A), P(B));
}
fn PeanoAbsDiff(comptime A: type, comptime B: type) type {
return if (B == Z) A else if (A == Z) B else PeanoAbsDiff(P(A), P(B));
}
test {
comptime try expectEqual(PeanoLT(Z, Z), False);
comptime try expectEqual(PeanoLT(S(Z), Z), False);
comptime try expectEqual(PeanoLT(Z, S(Z)), True);
comptime try expectEqual(PeanoLT(S(Z), S(Z)), False);
comptime try expectEqual(PeanoLT(S(S(Z)), S(S(S(Z)))), True);
comptime try expectEqual(PeanoLT(S(S(S(Z))), S(S(Z))), False);
//comptime expectEqual(PeanoLT(Z,Z),False);
}
fn Range(comptime N: type) type {
return if (N == Z) Nil else Cons(P(N), Range(P(N)));
}
test {
comptime try expectEqual(Range(N6), Cons(N5, Cons(N4, Cons(N3, Cons(N2, Cons(N1, Cons(Z, Nil)))))));
}
fn Queen(comptime X: type, comptime Y: type) type {
return struct {
x: X,
y: Y,
};
}
fn AppendIf(comptime Pred: type, comptime x: type, comptime ys: type) type {
return if (Pred == True) Cons(x, ys) else if (Pred == False) ys else unreachable;
}
// Queen threatens other queen
fn Threatens(comptime A: type, comptime B: type) type {
//if (A.ctor != B.ctor) unreachable;
const ax = nth_child(0, A);
const ay = nth_child(1, A);
const bx = nth_child(0, B);
const by = nth_child(1, B);
return Or(Or(PeanoEqual(ax, bx), PeanoEqual(ay, by)), PeanoEqual(PeanoAbsDiff(ax, bx), PeanoAbsDiff(ay, by)));
}
/// To be able to match Apply() for the correct partial application
/// have each partial store a reference to its constructor
/// Zig doesn't seem to have a way to directly pattern-match Type1(...types) like C++ templates do
const ctor_based = struct {
fn Conj1(comptime L: type) type {
return struct {
l: L,
const ctor = Conj1;
};
}
fn Apply(comptime F: type, comptime N: type) type {
//if (F.ctor != Conj1 and N == Nil) unreachable;
return if (@typeInfo(F).Struct.fields.len == 1) switch (F.ctor) {
Conj1 => Cons(N, nth_child(0, F)), //Note reversed
Queen1 => Queen(nth_child(0, F), N),
Threatens1 => Threatens(nth_child(0, F), N),
Safe1 => Safe(nth_child(0, F), N),
else => unreachable,
} else if (@typeInfo(F).Struct.fields.len == 2) switch (F.ctor) {
AddQueen2 => AddQueen(nth_child(0, F), nth_child(1, F), N),
else => unreachable,
} else unreachable;
}
fn Map(comptime F: type, comptime Xs: type) type {
if (Xs == Nil) return Nil;
//@compileLog(struct {}, F, Xs);
const first = Apply(F, First(Xs));
const rest = Map(F, Second(Xs));
return Cons(first, rest);
//return if (Xs == Nil) Nil else Cons(Apply(F, First(Xs)), Map(F, Second(Xs)));
}
fn MapCat(comptime F: type, comptime Xs: type) type {
return if (Xs == Nil) Nil else ListConcatAll(Map(F, Xs));
}
fn Filter(comptime F: type, comptime Xs: type) type {
return if (Xs == Nil) Nil else AppendIf(Apply(F, First(Xs)), First(Xs), Filter(F, Second(Xs)));
}
fn Queen1(comptime X: type) type {
return struct {
x: X,
const ctor = Queen1;
};
}
fn Threatens1(comptime A: type) type {
return struct {
a: A,
const ctor = Threatens1;
};
}
fn Safe1(comptime config: type) type {
return struct {
c: config,
const ctor = Safe1;
};
}
fn AddQueen2(comptime n: type, comptime x: type) type {
return struct {
tn: n,
tx: x,
const ctor = AddQueen2;
};
}
fn QueensInRow(comptime N: type, comptime x: type) type {
// A list of queens in row x with y from 0 to n.
return Map(Queen1(x), Range(N));
}
fn Safe(comptime config: type, comptime queen: type) type {
return Not(AnyTrue(Map(Threatens1(queen), config)));
}
fn AddQueen(comptime n: type, comptime x: type, comptime c: type) type {
return Map(Conj1(c), Filter(Safe1(c), QueensInRow(n, x)));
}
fn AddQueenToAll(comptime n: type, comptime x: type, comptime cs: type) type {
return MapCat(AddQueen2(n, x), cs);
}
fn AddQueensIf(comptime pred: type, comptime n: type, comptime x: type, comptime cs: type) type {
return if (pred == False) cs else if (pred == True) AddQueens(n, S(x), AddQueenToAll(n, x, cs)) else unreachable;
}
fn AddQueens(comptime n: type, comptime x: type, comptime cs: type) type {
return AddQueensIf(PeanoLT(x, n), n, x, cs);
}
fn Solution(comptime n: type) type {
return First(AddQueens(n, Z, Cons(Nil, Nil)));
}
};
/// Alternatively, the partial applications can purely store a closure
/// returning the full application
const closure_based = struct {
fn Conj1(comptime L: type) type {
return struct {
fn Apply(comptime N: type) type {
return Cons(N, L);
}
};
}
fn Queen1(comptime X: type) type {
return struct {
fn Apply(comptime Y: type) type {
return Queen(X, Y);
}
};
}
fn Threatens1(comptime A: type) type {
return struct {
fn Apply(comptime B: type) type {
return Threatens(A, B);
}
};
}
fn Safe1(comptime config: type) type {
return struct {
fn Apply(comptime B: type) type {
return Safe(config, B);
}
};
}
fn AddQueen2(comptime n: type, comptime x: type) type {
return struct {
fn Apply(comptime B: type) type {
return AddQueen(n, x, B);
}
};
}
// delegate to the closure
fn Apply(comptime F: type, comptime N: type) type {
return F.Apply(N);
}
// All other functions are the same
fn Map(comptime F: type, comptime Xs: type) type {
if (Xs == Nil) return Nil;
const first = Apply(F, First(Xs));
const rest = Map(F, Second(Xs));
return Cons(first, rest);
}
fn MapCat(comptime F: type, comptime Xs: type) type {
return if (Xs == Nil) Nil else ListConcatAll(Map(F, Xs));
}
fn Filter(comptime F: type, comptime Xs: type) type {
return if (Xs == Nil) Nil else AppendIf(Apply(F, First(Xs)), First(Xs), Filter(F, Second(Xs)));
}
fn QueensInRow(comptime N: type, comptime x: type) type {
// A list of queens in row x with y from 0 to n.
return Map(Queen1(x), Range(N));
}
fn Safe(comptime config: type, comptime queen: type) type {
return Not(AnyTrue(Map(Threatens1(queen), config)));
}
fn AddQueen(comptime n: type, comptime x: type, comptime c: type) type {
return Map(Conj1(c), Filter(Safe1(c), QueensInRow(n, x)));
}
fn AddQueenToAll(comptime n: type, comptime x: type, comptime cs: type) type {
return MapCat(AddQueen2(n, x), cs);
}
fn AddQueensIf(comptime pred: type, comptime n: type, comptime x: type, comptime cs: type) type {
return if (pred == False) cs else if (pred == True) AddQueens(n, S(x), AddQueenToAll(n, x, cs)) else unreachable;
}
fn AddQueens(comptime n: type, comptime x: type, comptime cs: type) type {
return AddQueensIf(PeanoLT(x, n), n, x, cs);
}
fn Solution(comptime n: type) type {
return First(AddQueens(n, Z, Cons(Nil, Nil)));
}
};
test {
const sol1 = comptime blk: {
@setEvalBranchQuota(500_000);
break :blk ctor_based.Solution(N8);
};
const sol2 = comptime blk: {
@setEvalBranchQuota(1000_000);
break :blk closure_based.Solution(N8);
};
try expectEqual(sol1, sol2);
std.debug.print("{s}\n", .{@typeName(sol1)});
}