Put reference to arXiv preprint in README.
[fgc-section-5.git] / mkQ.myr
blobac76e5e38ee276ed9272e009f28980af68607a10
1 use std
2 use iter
4 use t
5 use quiver
6 use yakmo
8 use "types"
9 use "kc"
10 use "latex"
11 use "mutations"
12 use "roots"
13 use "util"
14 use "zzz"
16 pkg =
17         const mk_Q : (kc : KC_type -> std.result(quiver.quiver#, byte[:]))
18         const mk_Qdv : (kc : KC_type -> std.result(quiver.quiver#, byte[:]))
19         const mk_Qdh : (kc : KC_type -> std.result(quiver.quiver#, byte[:]))
22 /* Cosmetic; can't declare these as constants because they're tuples or something. */
23 const frozen_color = { ; -> (0x4c, 0xe7, 0xff) }
24 const mutable_color = { ; -> (0xad, 0x7f, 0xa8) }
26 const mk_Q = { kc
27         var c : weyl_reflection[:] = [][:]
28         var rank : int = get_n(kc)
29         var l = 0
30         var h = 0
31         var q1 : quiver.quiver# = auto quiver.mk_with_opts([.use_a_coords = false])
32         var q : quiver.quiver#
33         var dynkin_edges : (weyl_reflection, weyl_reflection)[:] = get_edges(kc)
34         var T : weyl_reflection[:][:] = get_tree_partition(kc)
35         var zero : yakmo.Q# = auto yakmo.QfromZ(0)
36         var one : yakmo.Q# = auto yakmo.QfromZ(1)
37         var half : yakmo.Q# = auto std.try(yakmo.Qfrom(1, 2))
38         match kc
39         | `A n:
40                 if n % 2 == 0
41                         -> `std.Err std.fmt("A_{{2n}} is unimplemented at the moment")
42                 ;;
43         | _:
44         ;;
46         /* Cosmetic */
47         var node_xshifts = get_node_xshifts(kc)
49         /* Something like [ 1, 2, 3, 4 ] */
50         match get_coxeter_elt(kc)
51         | `std.Err e: -> `std.Err std.fmt("get_coxeter_elt({}): {}", kc, e)
52         | `std.Ok cc: c = cc
53         ;;
54         h = coxeter_num(kc)
55         if h % 2 != 0
56                 /* Should be impossible */
57                 -> `std.Err std.fmt("Coxeter number {} must be even", h)
58         ;;
59         l = h / 2 - 1
61         /*
62            Construct the mutable portion. We'll go straight into Q_1,
63            since it makes adding the edges more readable.
64          */
65         for var i = 0; i < c.len; ++i
66                 for var j = 1; j <= l; ++j
67                         var name : byte[:] = vname(c[i],j)
68                         match quiver.add_vertex(q1, name, (get_weight(kc, c[i]).0 : uint32))
69                         | `std.Err e: -> `std.Err std.fmt("add_vertex: {}", e)
70                         | `std.Ok idx:
71                                 q1.v[idx].x_pos = (l - j + 1 : int64) * 100 + std.htgetv(node_xshifts, c[i], 0)
72                                 q1.v[idx].y_pos = (i + 1 : int64) * (-75)
73                                 q1.v[idx].color = mutable_color()
74                         ;;
75                         std.slfree(name)
76                 ;;
77         ;;
79         /*
80            Construct B and C vertices, as well as dummy A vertices.
82            These are added in a verbose order so that reading the output
83            file is possible.
84          */
85         for var i = 0; i < c.len; ++i
86                 var name : byte[:] = std.fmt("B{}", c[i])
87                 match quiver.add_vertex(q1, name, (get_weight(kc, c[i]).0 : uint32))
88                 | `std.Err e: -> `std.Err std.fmt("add_vertex: {}", e)
89                 | `std.Ok idx:
90                         q1.v[idx].x_pos = 0 - (c.len - i : int64) * 43 + std.htgetv(node_xshifts, c[i], 0)
91                         q1.v[idx].y_pos = (i + 1 : int64) * (-75)
92                         q1.v[idx].color = frozen_color()
93                 ;;
94                 std.slfree(name)
95         ;;
97         var min_mutable_x_pos : int64 = 100
98         var max_mutable_x_pos : int64 = (l * 100 : int64)
99         for var i = 0; i < c.len; ++i
100                 var name : byte[:] = std.fmt("C{}", c[i])
101                 match quiver.add_vertex(q1, name, (get_weight(kc, c[i]).0 : uint32))
102                 | `std.Err e: -> `std.Err std.fmt("add_vertex: {}", e)
103                 | `std.Ok idx:
104                         q1.v[idx].x_pos = max_mutable_x_pos + 100 + (c.len - i : int64) * 43 + std.htgetv(node_xshifts, c[i], 0)
105                         q1.v[idx].y_pos = (i + 1 : int64) * (-75)
106                         q1.v[idx].color = frozen_color()
107                 ;;
108                 std.slfree(name)
109         ;;
111         var c_min_x : int64 = 50
112         var c_max_x : int64 = (l * 100 : int64) + 50
113         if c.len <= 1
114                 for var i = 0; i < c.len; ++i
115                         var name : byte[:] = std.fmt("A{}", c[i])
116                         match quiver.add_vertex(q1, name, (get_weight(kc, c[i]).0 : uint32))
117                         | `std.Err e: -> `std.Err std.fmt("cannot add {}: {}", name, e)
118                         | `std.Ok idx:
119                                 q1.v[idx].x_pos = (c_min_x + c_max_x) / 2
120                                 q1.v[idx].y_pos = 0
121                                 q1.v[idx].color = frozen_color()
122                         ;;
123                         std.slfree(name)
124                 ;;
125         else
126                 for var i = 0; i < c.len; ++i
127                         var name : byte[:] = std.fmt("A{}", c[i])
128                         var x_pos : int64 = ((c.len - 1 - i : int64) * (c_max_x - c_min_x)) / (c.len - 1) + c_min_x
129                         var y_pos : int64 = 0 + std.htgetv(node_xshifts, c[i], 0)
130                         match quiver.add_vertex(q1, name, (get_weight(kc, c[i]).0 : uint32))
131                         | `std.Err e: -> `std.Err std.fmt("cannot add {}: {}", name, e)
132                         | `std.Ok idx:
133                                 q1.v[idx].x_pos = x_pos
134                                 q1.v[idx].y_pos = y_pos
135                                 q1.v[idx].color = frozen_color()
136                         ;;
137                         std.slfree(name)
138                 ;;
139         ;;
141         /* Now, hook up the edges */
143         /* Connect each D-like quiver */
144         for (i1, i2) : dynkin_edges
145                 for var j = 1; j <= l; ++j
146                         var name_from : byte[:] = vname(i1, j)
147                         var name_to   : byte[:] = vname(i2, j)
148                         match quiver.add_to_edge(q1, name_from, name_to, auto yakmo.QfromZ(1))
149                         | `std.Err e: -> `std.Err std.fmt("cannot construct edge {} -> {} in Q_1: {}", name_from, name_to, e)
150                         | `std.Ok void:
151                         ;;
152                         std.slfree(name_from)
153                         std.slfree(name_to)
154                 ;;
156                 var name_B_from : byte[:] = std.fmt("B{}", i1)
157                 var name_C_from : byte[:] = std.fmt("C{}", i1)
158                 var name_B_to   : byte[:] = std.fmt("B{}", i2)
159                 var name_C_to   : byte[:] = std.fmt("C{}", i2)
160                 match quiver.add_to_edge(q1, name_B_from, name_B_to, half)
161                 | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_B_from, name_B_to, e)
162                 | `std.Ok void:
163                 ;;
164                 match quiver.add_to_edge(q1, name_C_from, name_C_to, half)
165                 | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_C_from, name_C_to, e)
166                 | `std.Ok void:
167                 ;;
168                 std.slfree(name_B_from)
169                 std.slfree(name_C_from)
170                 std.slfree(name_B_to)
171                 std.slfree(name_C_to)
172         ;;
174         /* Connect each horizontal row */
175         if l >= 1
176                 for i : c
177                         var name_B : byte[:] = std.fmt("B{}", i)
178                         var name_C : byte[:] = std.fmt("C{}", i)
179                         var name_lmost : byte[:] = vname(i, l)
180                         var name_rmost : byte[:] = vname(i, 1)
182                         match quiver.add_to_edge(q1, name_B, name_lmost, one)
183                         | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_B, name_lmost, e)
184                         | `std.Ok void:
185                         ;;
187                         match quiver.add_to_edge(q1, name_rmost, name_C, one)
188                         | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_rmost, name_C, e)
189                         | `std.Ok void:
190                         ;;
192                         std.slfree(name_B)
193                         std.slfree(name_C)
194                         std.slfree(name_lmost)
195                         std.slfree(name_rmost)
197                         for var j = 1; j + 1 <= l; ++j
198                                 var name_from : byte[:] = vname(i, j + 1)
199                                 var name_to   : byte[:] = vname(i, j + 0)
201                                 match quiver.add_to_edge(q1, name_from, name_to, one)
202                                 | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_from, name_to, e)
203                                 | `std.Ok void:
204                                 ;;
205                         ;;
206                 ;;
207         else
208                 for i : c
209                         var name_B : byte[:] = std.fmt("B{}", i)
210                         var name_C : byte[:] = std.fmt("C{}", i)
212                         match quiver.add_to_edge(q1, name_B, name_C, one)
213                         | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_B, name_C, e)
214                         | `std.Ok void:
215                         ;;
217                         std.slfree(name_B)
218                         std.slfree(name_C)
219                 ;;
220         ;;
222         /* Now, the diagonals */
223         for (i1, i2) : dynkin_edges
224                 var name_B : byte[:] = std.fmt("B{}", i1)
225                 var name_lmost : byte[:] = vname(i2, l)
226                 var name_C : byte[:] = std.fmt("C{}", i2)
227                 var name_rmost : byte[:] = vname(i1, 1)
229                 match quiver.add_to_edge(q1, name_lmost, name_B, one)
230                 | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_lmost, name_B, e)
231                 | `std.Ok void:
232                 ;;
234                 match quiver.add_to_edge(q1, name_C, name_rmost, one)
235                 | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_C, name_rmost, e)
236                 | `std.Ok void:
237                 ;;
239                 std.slfree(name_B)
240                 std.slfree(name_C)
241                 std.slfree(name_lmost)
242                 std.slfree(name_rmost)
244                 for var j = 1; j + 1 <= l; ++j
245                         var name_from : byte[:] = vname(i2, j + 0)
246                         var name_to   : byte[:] = vname(i1, j + 1)
248                         match quiver.add_to_edge(q1, name_from, name_to, one)
249                         | `std.Err e: -> `std.Err std.fmt("cannot construct {} -> {} in Q_1: {}", name_from, name_to, e)
250                         | `std.Ok void:
251                         ;;
253                         std.slfree(name_from)
254                         std.slfree(name_to)
255                 ;;
256         ;;
258         /*
259            Now, we have Q_1. We need to rotate forwards and backwards,
260            which will give us the edges. There appears to be a more
261            clearly algorithmic construction for this based on ``The
262            first k for which hi shows up in the numerator for
263            w_k^{-1}(h) or something'', but this is easier.
264          */
265         var mu : byte[:][:] = [][:]
267         match get_mutwistingrot(kc)
268         | `std.Err e: -> `std.Err std.fmt("murot: {}", e)
269         | `std.Ok mu_: mu = mu_
270         ;;
272         var q1_prime : quiver.quiver# = auto t.dup(q1)
273         for vertex : mu
274                 match quiver.mutate_ip(q1_prime, vertex)
275                 | `std.Ok void:
276                 | `std.Err e: -> `std.Err std.fmt("cannot construct Q_1': {}", e)
277                 ;;
278         ;;
280         /* Don't mutate Q'' yet -- the renamings need to be done first */
281         var q1_primeprime : quiver.quiver# = auto t.dup(q1)
283         /*
284            We'll need to permute some vertices according to ÏƒG, and to
285            ÏƒA_l. The latter is always j <-> l-j+1, the former needs to
286            be calculated. Luckily, both of these things are involutions,
287            so we'll freely use ÏƒG as its own inverse when renaming for
288            Q''.
289          */
290         var sG
291         match get_sigmaG_permutation(kc)
292         | `std.Err e: -> `std.Err std.fmt("cannot calculate ÏƒG: {}", e)
293         | `std.Ok sGc: sG = sGc
294         ;;
296         match rename_according_to_twistingrot(kc, q1, q1_prime)
297         | `std.Err e: -> `std.Err std.fmt("renaming Q_1' failed: {}", e)
298         | `std.Ok void:
299         ;;
301         match rename_according_to_twistingrotinv(kc, q1, q1_primeprime)
302         | `std.Err e: -> `std.Err std.fmt("renaming Q_1' failed: {}", e)
303         | `std.Ok void:
304         ;;
306         /* Now finish off the mutation for Q'' */
307         for vertex : iter.byreverse(mu)
308                 match quiver.mutate_ip(q1_primeprime, vertex)
309                 | `std.Ok void:
310                 | `std.Err e: -> `std.Err std.fmt("cannot construct Q_1'': {}", e)
311                 ;;
312         ;;
314         /*
315            Now, we're ready to construct Q_2 = Q. We need to check
316            through all pairs (x, y) of vertices in Q_1 (by name). For
317            each pair, we take the unique non-zero edge weight, and use
318            that as the edge weight for Q_2.
319          */
321         /* First, copy over all vertices */
322         var q2 : quiver.quiver# = quiver.mk_with_opts([.use_a_coords = true])
323         for vertex : q1.v
324                 match quiver.add_vertex(q2, vertex.name, vertex.d)
325                 | `std.Err e: -> `std.Err std.fmt("cannot add {} to Q_2: {}", vertex.name, e)
326                 | `std.Ok idx:
327                         q2.v[idx].x_pos = q1.v[idx].x_pos
328                         q2.v[idx].y_pos = q1.v[idx].y_pos
329                         q2.v[idx].color = q1.v[idx].color
330                 ;;
331         ;;
333         /* Now the long haul of pairs. */
334         for var i = 0; i < q1.v.len; ++i
335                 var x = q1.v[i].name
336                 for var j = 0; j < q1.v.len; ++j
337                         var y = q1.v[j].name
339                         var eps1 = q1.epsilon[i][j]
340                         var eps2 = epsilon_of(q1_prime, x, y, zero)
341                         var eps3 = epsilon_of(q1_primeprime, x, y, zero)
343                         /*
344                            The lemma is "In the set {eps1, eps2, eps3}, there
345                            is at most one non-zero element. Repetitions
346                            are allowed." We must check this.
347                          */
348                         /* TODO: types */
349                         var maybe_non_zero : yakmo.Q# = zero
350                         for eps : [ eps1, eps2, eps3 ][:]
351                                 if std.eq(eps, zero)
352                                         continue
353                                 ;;
355                                 if std.eq(maybe_non_zero, zero)
356                                         maybe_non_zero = eps
357                                 elif !std.eq(maybe_non_zero, eps)
358                                         std.put("{}", quiver.to_bytes(q1))
359                                         std.put("=================\n")
360                                         std.put("{}", quiver.to_bytes(q1_prime))
361                                         std.put("=================\n")
362                                         std.put("{}", quiver.to_bytes(q1_primeprime))
363                                         -> `std.Err std.fmt("Eps uniqueness lemma fails: edge {} -> {}, weights {}, {}", x, y, eps, maybe_non_zero)
364                                 ;;
365                         ;;
367                         // TODO: __dispose__(q2.epsilon[i][j])
368                         q2.epsilon[i][j] = t.dup(maybe_non_zero)
369                 ;;
370         ;;
372         -> `std.Ok q2
375 /* Returns exactly epsilon[i][j] or zero. No duping */
376 const epsilon_of = {q, x, y, zero
377         for var i = 0; i < q.v.len; ++i
378                 if !std.eq(q.v[i].name, x)
379                         continue
380                 ;;
382                 for var j = 0; j < q.v.len; ++j
383                         if !std.eq(q.v[j].name, y)
384                                 continue
385                         ;;
387                         -> q.epsilon[i][j]
388                 ;;
389         ;;
391         -> zero
394 const bound_quiver_bit = {q : quiver.quiver#, prefix : char
395         var pb : byte = (prefix : byte)
396         var anything : bool = false
397         var min_x = 0
398         var max_x = 0
399         var min_y = 0
400         var max_y = 0
402         for var j = 0; j < q.v.len; ++j
403                 if q.v[j].name[0] != pb
404                         continue
405                 ;;
407                 anything = true
409                 min_x = q.v[j].x_pos
410                 max_x = q.v[j].x_pos
411                 min_y = q.v[j].y_pos
412                 max_y = q.v[j].y_pos
414                 break
415         ;;
417         if !anything
418                 -> (0, 0, 0, 0)
419         ;;
421         for var j = 0; j < q.v.len; ++j
422                 if q.v[j].name[0] != pb
423                         continue
424                 ;;
426                 min_x = std.min(min_x, q.v[j].x_pos)
427                 max_x = std.min(max_x, q.v[j].x_pos)
428                 min_y = std.min(min_y, q.v[j].y_pos)
429                 max_y = std.min(max_y, q.v[j].y_pos)
430         ;;
432         -> (min_x, max_x, min_y, max_y)
435 const mk_Qdv = {kc
436         var c = [][:]
437         match get_coxeter_elt(kc)
438         | `std.Err e: -> `std.Err std.fmt("get_coxeter_elt({}): {}", kc, e)
439         | `std.Ok cc: c = cc
440         ;;
441         var h = coxeter_num(kc)
442         var l = h / 2 - 1
443         var L = 2 * l + 1
444         var Q : quiver.quiver#
445         var min_x, max_x, min_y, max_y
446         match mk_Q(kc)
447         | `std.Err e: -> `std.Err std.fmt("mk_Q({}): {}", kc, e)
448         | `std.Ok QQ: Q = QQ
449         ;;
450         auto Q
452         /* Left: a copy of Q, rotated by pi/3, with C-edge centered on (0, 0) */
453         var left_Q : quiver.quiver# = auto t.dup(Q)
454         for var j = 0; j < left_Q.v.len; ++j
455                 var x = left_Q.v[j].x_pos
456                 var y = left_Q.v[j].y_pos
457                 left_Q.v[j].x_pos = (866 * x) / 1000 - y / 2
458                 left_Q.v[j].y_pos = x / 2 + (866 * y) / 1000
459         ;;
461         (min_x, max_x, min_y, max_y) = bound_quiver_bit(left_Q, 'C')
462         var c_mid_x = (min_x + max_x) / 2
463         var c_mid_y = (min_y + max_y) / 2
465         for var j = 0; j < left_Q.v.len; ++j
466                 left_Q.v[j].x_pos -= c_mid_x
467                 left_Q.v[j].y_pos -= c_mid_y
468         ;;
470         /* Right: a copy of Q, rotated by pi/2, adjusted so that the A-edge is centered on (0, 0) */
471         var right_Q : quiver.quiver# = Q
472         for var j = 0; j < right_Q.v.len; ++j
473                 var x = right_Q.v[j].x_pos
474                 var y = right_Q.v[j].y_pos
475                 right_Q.v[j].x_pos = -1 * y
476                 right_Q.v[j].y_pos = x
477         ;;
479         (min_x, max_x, min_y, max_y) = bound_quiver_bit(right_Q, 'A')
480         var a_mid_x = (min_x + max_x) / 2
481         var a_mid_y = (min_y + max_y) / 2
483         for var j = 0; j < right_Q.v.len; ++j
484                 right_Q.v[j].x_pos -= a_mid_x
485                 right_Q.v[j].y_pos -= a_mid_y
486         ;;
487         
488         /* Now embed into Qdv */
489         var Qdv : quiver.quiver# = quiver.mk_with_opts([.use_a_coords = true])
491         /*
492            FOO_map[BAR] = BAZ means that vertex BAR in Q corresponds to
493            vertex BAZ in the FOO half of Qdv.
494          */
495         var left_map : std.htab(byte[:], byte[:])# = std.mkht()
496         var right_map : std.htab(byte[:], byte[:])# = std.mkht()
498         for cc : c
499                 std.htput(left_map, std.fmt("A{}", cc), std.fmt("D{}", cc))
500                 std.htput(left_map, std.fmt("B{}", cc), std.fmt("E{}", cc))
501                 std.htput(left_map, std.fmt("C{}", cc), wname(cc, l + 1))
503                 std.htput(right_map, std.fmt("A{}", cc), wname(cc, l + 1))
504                 std.htput(right_map, std.fmt("B{}", cc), std.fmt("F{}", cc))
505                 std.htput(right_map, std.fmt("C{}", cc), std.fmt("G{}", cc))
507                 for var j = 1; j <= l; ++j
508                         std.htput(left_map, vname(cc, j), wname(cc, j + l + 1))
509                         std.htput(right_map, vname(cc, j), wname(cc, j))
510                 ;;
511         ;;
513         embed_quiver(left_Q, Qdv, left_map)
514         embed_quiver(right_Q, Qdv, right_map)
516         /* Un-freeze the middle vertices */
517         for cc : c
518                 var name = wname(cc, l + 1)
519                 match quiver.find_vertex(Qdv, name)
520                 | `std.Some (_, vv): vv.color = mutable_color()
521                 | _:
522                 ;;
523                 std.slfree(name)
524         ;;
526         /* Kill the maps. TODO: bother Ori about integrating disposable tighter here. */
527         for (key, val) : std.byhtkeyvals(left_map)
528                 std.slfree(key)
529                 std.slfree(val)
530         ;;
531         std.htfree(left_map)
533         for (key, val) : std.byhtkeyvals(right_map)
534                 std.slfree(key)
535                 std.slfree(val)
536         ;;
537         std.htfree(right_map)
539         -> `std.Ok Qdv
542 const embed_quiver = {q_from, q_to, map
543         for (name_from, name_to) : std.byhtkeyvals(map)
544                 match quiver.find_vertex(q_from, name_from)
545                 | `std.None:
546                 | `std.Some (_, v_from):
547                         match quiver.add_vertex(q_to, std.sldup(name_to), v_from.d)
548                         | `std.Err e: std.slfree(e)
549                         | `std.Ok j:
550                                 q_to.v[j].color = v_from.color
551                                 q_to.v[j].x_pos = v_from.x_pos
552                                 q_to.v[j].y_pos = v_from.y_pos
554                                 match v_from.acoord
555                                 | `std.None: q_to.v[j].acoord = `std.None
556                                 | `std.Some a: q_to.v[j].acoord = `std.Some t.dup(a)
557                                 ;;
559                                 match v_from.xcoord
560                                 | `std.None: q_to.v[j].xcoord = `std.None
561                                 | `std.Some x: q_to.v[j].xcoord = `std.Some t.dup(x)
562                                 ;;
563                         ;;
564                 ;;
565         ;;
567         for var j1 = 0; j1 < q_from.v.len; ++j1
568                 var name_to1
569                 match std.htget(map, q_from.v[j1].name)
570                 | `std.None: continue
571                 | `std.Some n: name_to1 = n
572                 ;;
574                 for var j2 = 0; j2 < j1; ++j2
575                         var name_to2
576                         match std.htget(map, q_from.v[j2].name)
577                         | `std.None: continue
578                         | `std.Some n: name_to2 = n
579                         ;;
581                         match quiver.get_sigma_ii(q_from, j1, j2)
582                         | `std.None:
583                         | `std.Some sigma:
584                                 match quiver.add_to_edge(q_to, name_to1, name_to2, sigma)
585                                 | `std.Ok void:
586                                 | `std.Err e: std.slfree(e)
587                                 ;;
589                                 /* TODO: type bs */
590                                 auto (sigma : yakmo.Q#)
591                         ;;
592                 ;;
593         ;;
596 const mk_Qdh = {kc
597         var c = [][:]
598         match get_coxeter_elt(kc)
599         | `std.Err e: -> `std.Err std.fmt("get_coxeter_elt({}): {}", kc, e)
600         | `std.Ok cc: c = cc
601         ;;
602         var h = coxeter_num(kc)
603         var l = h / 2 - 1
604         var L = 2 * l + 1
605         var Q : quiver.quiver#
606         var min_x, max_x, min_y, max_y
607         match mk_Q(kc)
608         | `std.Err e: -> `std.Err std.fmt("mk_Q({}): {}", kc, e)
609         | `std.Ok QQ: Q = QQ
610         ;;
611         auto Q
613         /* Bottom: a copy of Q, rotated by pi/3, with B-edge centered on (0, 0) */
614         var bottom_Q : quiver.quiver# = auto t.dup(Q)
615         for var j = 0; j < bottom_Q.v.len; ++j
616                 var x = bottom_Q.v[j].x_pos
617                 var y = bottom_Q.v[j].y_pos
618                 bottom_Q.v[j].x_pos = x / 2 - (866 * y) / 1000
619                 bottom_Q.v[j].y_pos = (866 * x) / 1000 + y / 2
620         ;;
622         (min_x, max_x, min_y, max_y) = bound_quiver_bit(bottom_Q, 'B')
623         var b_mid_x = (min_x + max_x) / 2
624         var b_mid_y = (min_y + max_y) / 2
626         for var j = 0; j < bottom_Q.v.len; ++j
627                 bottom_Q.v[j].x_pos -= b_mid_x
628                 bottom_Q.v[j].y_pos -= b_mid_y
629         ;;
631         /* Top: a copy of Q, rotated by 2pi/3, with C-edge is centered on (0, 0) */
632         var top_Q : quiver.quiver# = Q
633         for var j = 0; j < top_Q.v.len; ++j
634                 var x = top_Q.v[j].x_pos
635                 var y = top_Q.v[j].y_pos
636                 top_Q.v[j].x_pos = -1 * x / 2 - (866 * y) / 1000
637                 top_Q.v[j].y_pos = (866 * x) / 1000 - y / 2
638         ;;
640         (min_x, max_x, min_y, max_y) = bound_quiver_bit(top_Q, 'C')
641         var c_mid_x = (min_x + max_x) / 2
642         var c_mid_y = (min_y + max_y) / 2
644         for var j = 0; j < top_Q.v.len; ++j
645                 top_Q.v[j].x_pos -= c_mid_x
646                 top_Q.v[j].y_pos -= c_mid_y
647         ;;
648         
649         /* Now embed into Qdh */
650         var Qdh : quiver.quiver# = quiver.mk_with_opts([.use_a_coords = true])
652         var bottom_map : std.htab(byte[:], byte[:])# = std.mkht()
653         var top_map : std.htab(byte[:], byte[:])# = std.mkht()
655         for cc : c
656                 std.htput(bottom_map, std.fmt("A{}", cc), std.fmt("D{}", cc))
657                 std.htput(bottom_map, std.fmt("B{}", cc), wname(cc, l + 1))
658                 std.htput(bottom_map, std.fmt("C{}", cc), std.fmt("G{}", cc))
660                 std.htput(top_map, std.fmt("A{}", cc), std.fmt("E{}", cc))
661                 std.htput(top_map, std.fmt("B{}", cc), std.fmt("F{}", cc))
662                 std.htput(top_map, std.fmt("C{}", cc), wname(cc, l + 1))
664                 for var j = 1; j <= l; ++j
665                         std.htput(bottom_map, vname(cc, j), wname(cc, j))
666                         std.htput(top_map, vname(cc, j), wname(cc, j + l + 1))
667                 ;;
668         ;;
670         embed_quiver(top_Q, Qdh, top_map)
671         embed_quiver(bottom_Q, Qdh, bottom_map)
673         /* Un-freeze the middle vertices */
674         for cc : c
675                 var name = wname(cc, l + 1)
676                 match quiver.find_vertex(Qdh, name)
677                 | `std.Some (_, vv): vv.color = mutable_color()
678                 | _:
679                 ;;
680                 std.slfree(name)
681         ;;
683         /* Kill the maps. TODO: bother Ori about integrating disposable tighter here. */
684         for (key, val) : std.byhtkeyvals(bottom_map)
685                 std.slfree(key)
686                 std.slfree(val)
687         ;;
688         std.htfree(bottom_map)
690         for (key, val) : std.byhtkeyvals(top_map)
691                 std.slfree(key)
692                 std.slfree(val)
693         ;;
694         std.htfree(top_map)
696         -> `std.Ok Qdh