Put reference to arXiv preprint in README.
[fgc-section-5.git] / M.myr
blob3690c953d13fcaeecd498da8f4068b45ef14aa63
1 use std
3 use t
4 use yakmo
6 use "types"
7 use "factorization-coords"
8 use "kc"
9 use "roots"
10 use "zzz"
12 pkg =
13         /*
14            Given minor coords for { h1, h2, h3, u }, return the interior
15            coordinates of T_Q. The ordering is by column, then by
16            coxeter element: if c = { 2, 1, 3, 4} and T is the result,
17            then
19              T[0] = v2;1, T[1] = v1;1, T[2] = v3;1, T[3] = v4;1,
20              T[4] = v2;2, T[5] = v1;2, T[6] = v3;2, T[7] = v4;2,
22            and so on.
23          */
24         const apply_M : (kc : KC_type, alpha : conf_3_star#, d_class : Dynkin_automorphism_class -> std.result((T_Q#, Dynkin_automorphism_class), byte[:]))
25         const unapply_M : (kc : KC_type, TQ : T_Q#, d_class : Dynkin_automorphism_class -> std.result((conf_3_star#, Dynkin_automorphism_class), byte[:]))
27         /* The above, but for use with the flip */
28         const apply_Mdv : (kc : KC_type, alpha : (conf_3_star#, conf_3_star#) -> std.result(T_Q02#, byte[:]))
29         const apply_Mdh : (kc : KC_type, alpha : (conf_3_star#, conf_3_star#) -> std.result(T_Q13#, byte[:]))
30         const unapply_Mdv : (kc : KC_type, TQ : T_Q13# -> std.result((conf_3_star#, conf_3_star#), byte[:]))
31         const unapply_Mdh : (kc : KC_type, TQ : T_Q13# -> std.result((conf_3_star#, conf_3_star#), byte[:]))
34 type in_alpha = union
35         `alpha_u std.size
36         `alpha_h1 int
37         `alpha_h2 int
38         `alpha_h3 int
40 type in_TQ = union
41         `TQ_v (weyl_reflection, int)
42         `TQ_A weyl_reflection
43         `TQ_B weyl_reflection
44         `TQ_C weyl_reflection
48    This is to eliminate redundancy in apply_M and unapply_M.
49  */
50 const alpha_TQ_bijection = {kc : KC_type, d_class : Dynkin_automorphism_class
51         var mapping : (in_alpha, in_TQ)[:] = [][:]
53         var sigmaG
54         match get_sigmaG_permutation(kc)
55         | `std.Ok perm: sigmaG = perm
56         | `std.Err e: -> `std.Err std.fmt("get_sigmaG_permutation({}): {}", kc, e)
57         ;;
59         var rank = get_n(kc)
60         var c : weyl_reflection[:] = [][:]
61         var h = coxeter_num(kc)
62         var l = h / 2 - 1
63         var triv = trivial(d_class)
64         match get_coxeter_elt(kc)
65         | `std.Ok cc: c = cc
66         | `std.Err e: -> `std.Err std.fmt("get_coxeter_elt({}): {}", kc, e)
67         ;;
69         for var j = 0; j < rank; ++j
70                 var jz = (j + 1 : int)
71                 var sgjz = (sigmaG[jz] : int)
72                 var jw = (j + 1 : weyl_reflection)
73                 var sgjw = sigmaG[jw]
74                 std.slpush(&mapping, (`alpha_h1(triv ?   jz :   jz ), `TQ_A(triv ?   jw :   jw)))
75                 std.slpush(&mapping, (`alpha_h2(triv ?   jz :   jz ), `TQ_B(triv ?   jw :   jw)))
76                 std.slpush(&mapping, (`alpha_h3(triv ?   jz :   jz ), `TQ_C(triv ? sgjw :   jw)))
77         ;;
79         /*
80            Δ^{γ_1}(u) = our_u[0] should correspond to vname(c[0], 1)
82            Δ^{γ_2}(u) = our_u[1] should correspond to vname(c[1], 1)
84            and so on.
85          */
86         var k = 0
87         for var j = 1; j <= l; ++j
88                 for i : c
89                         var sgi = sigmaG[i]
90                         std.slpush(&mapping, (`alpha_u(k), `TQ_v(triv ? i : i, j)))
91                         k++
92                 ;;
93         ;;
95         -> `std.Ok mapping
98 const apply_M = {kc : KC_type, alpha : conf_3_star#, d_class : Dynkin_automorphism_class
99         var ret : T_Q = [ .A = [][:], .B = [][:], .C = [][:], .v = [][:] ]
100         var mapping = [][:]
101         /*
102            The general formula is that, for
104                 XXXXXXXXXXXXX
106            we have
108                 vij = Δ^{γ_k}(y) · YYYYYYYYYYY
109          */
111         var zrank = (get_n(kc) : std.size)
112         var w0 = get_w0(kc)
113         var h = coxeter_num(kc)
114         var l = h / 2 - 1
116         /* Just double-check alpha */
117         var fc_h1 = [][:], fc_h2 = [][:], fc_h3 = [][:]
118         match edge_fc_from_mc(kc, alpha.h1)
119         | `std.Ok f: fc_h1 = f
120         | `std.Err e:
121                 auto (e : t.doomed_str)
122                 -> `std.Err std.fmt("edge_fc_from_mc(h1): {}", e)
123         ;;
124         match edge_fc_from_mc(kc, alpha.h2)
125         | `std.Ok f: fc_h2 = f
126         | `std.Err e:
127                 auto (e : t.doomed_str)
128                 -> `std.Err std.fmt("edge_fc_from_mc(h2): {}", e)
129         ;;
130         match edge_fc_from_mc(kc, alpha.h3)
131         | `std.Ok f: fc_h3 = f
132         | `std.Err e:
133                 auto (e : t.doomed_str)
134                 -> `std.Err std.fmt("edge_fc_from_mc(h3): {}", e)
135         ;;
136         auto fc_h1
137         auto fc_h2
138         auto fc_h3
139         var our_u = [][:]
140         if alpha.u.len == w0.len - zrank
141                 our_u = alpha.u
142         elif alpha.u.len == w0.len
143                 our_u = alpha.u[zrank:]
144         else
145                 -> `std.Err std.fmt("alpha.u.len = {}, should be {}", alpha.u.len, w0.len - zrank)
146         ;;
148         /* Get bijection */
149         match alpha_TQ_bijection(kc, d_class)
150         | `std.Err e:
151                 auto (e : t.doomed_str)
152                 -> `std.Err std.fmt("alpha_TQ_bijection: {}", e)
153         | `std.Ok lmapping: mapping = lmapping
154         ;;
156         /* Construct T_Q return value */
157         ret.A = std.slalloc(zrank + 1)
158         ret.B = std.slalloc(zrank + 1)
159         ret.C = std.slalloc(zrank + 1)
160         for var j = 0; j <= zrank; ++j
161                 ret.A[j] = yakmo.gid()
162                 ret.B[j] = yakmo.gid()
163                 ret.C[j] = yakmo.gid()
164         ;;
166         ret.v = std.slalloc(zrank + 1)
167         for var i = 0; i < ret.v.len; ++i
168                 ret.v[i] = std.slalloc((l + 1 : std.size))
169                 for var j = 0; j < ret.v[i].len; ++j
170                         ret.v[i][j] = yakmo.gid()
171                 ;;
172         ;;
174         /* Assign via bijection we constructed earlier */
175         for (ina, intq) : mapping
176                 var rf
177                 match ina
178                 | `alpha_u (k): rf = t.dup(our_u[k].c)
179                 | `alpha_h1 (k): rf = std.get(chi(fc_h1, (k : weyl_reflection)))
180                 | `alpha_h2 (k): rf = std.get(chi(fc_h2, (k : weyl_reflection)))
181                 | `alpha_h3 (k): rf = std.get(chi(fc_h3, (k : weyl_reflection)))
182                 ;;
184                 match intq
185                 | `TQ_v (i, j):
186                         __dispose__(ret.v[i][j])
187                         ret.v[i][j] = rf
188                 | `TQ_A (j):
189                         __dispose__(ret.A[j])
190                         ret.A[j] = rf
191                 | `TQ_B (j):
192                         __dispose__(ret.B[j])
193                         ret.B[j] = rf
194                 | `TQ_C (j):
195                         __dispose__(ret.C[j])
196                         ret.C[j] = rf
197                 ;;
198         ;;
200         /* ... and apply monomial */
201         match just_monomial_part(kc, &ret, d_class)
202         | `std.Ok void:
203         | `std.Err e: -> `std.Err e
204         ;;
206         -> `std.Ok (std.mk(ret), d_class)
210 const unapply_M = {kc : KC_type, TQ_orig : T_Q#, d_class : Dynkin_automorphism_class
211         var TQ = t.dup(TQ_orig)
212         auto TQ
213         var mapping = [][:]
215         var zrank = (get_n(kc) : std.size)
216         var w0 = get_w0(kc)
217         var h = coxeter_num(kc)
218         var l = h / 2 - 1
220         /* Double-check T_Q */
221         if TQ.A.len != zrank + 1
222                 -> `std.Err std.fmt("TQ.A.len = {}, should be {}", TQ.A.len, zrank + 1)
223         elif TQ.B.len != zrank + 1
224                 -> `std.Err std.fmt("TQ.B.len = {}, should be {}", TQ.B.len, zrank + 1)
225         elif TQ.C.len != zrank + 1
226                 -> `std.Err std.fmt("TQ.C.len = {}, should be {}", TQ.C.len, zrank + 1)
227         elif TQ.v.len != zrank + 1
228                 -> `std.Err std.fmt("TQ.v.len = {}, should be {}", TQ.v.len, zrank + 1)
229         ;;
231         for var j = 0; j < TQ.v.len; ++j
232                 if TQ.v[j].len != l + 1
233                         -> `std.Err std.fmt("TQ.v[{}].len = {}, should be {}", j, TQ.v[j].len, l + 1)
234                 ;;
235         ;;
237         /* Get bijection */
238         match alpha_TQ_bijection(kc, d_class)
239         | `std.Err e:
240                 auto (e : t.doomed_str)
241                 -> `std.Err std.fmt("alpha_TQ_bijection: {}", e)
242         | `std.Ok lmapping: mapping = lmapping
243         ;;
245         /* Construct conf_3_star return value */
246         var alpha : conf_3_star = [
247                 .h1 = [][:],
248                 .h2 = [][:],
249                 .h3 = [][:],
250                 /* Remember, TQ.v is padded strangely so that it can be 1-based */
251                 .u = std.slalloc(w0.len - zrank),
252         ]
254         for var k = 0; k < w0.len - zrank; ++k
255                 alpha.u[k] = [ .k = (zrank + k + 1 : int), .c = yakmo.gid() ]
256         ;;
258         var fc_h1 : factorization_coordinate[:] = [][:]
259         var fc_h2 : factorization_coordinate[:] = [][:]
260         var fc_h3 : factorization_coordinate[:] = [][:]
262         /*
263            To apply the monomial map in reverse, we temporarily invert
264            all edge coordinates, then apply the normal monomial map.
265          */
266         invert_edges(kc, TQ)
267         match just_monomial_part(kc, TQ, d_class)
268         | `std.Ok void:
269         | `std.Err e: -> `std.Err e
270         ;;
272         invert_edges(kc, TQ)
274         /* Assign via bijection we constructed earlier */
275         for (ina, intq) : mapping
276                 var rf
277                 match intq
278                 | `TQ_v (i, j): rf = TQ.v[i][j]
279                 | `TQ_A (j): rf = TQ.A[j]
280                 | `TQ_B (j): rf = TQ.B[j]
281                 | `TQ_C (j): rf = TQ.C[j]
282                 ;;
284                 match ina
285                 | `alpha_u (k):
286                         __dispose__(alpha.u[k].c)
287                         alpha.u[k].c = t.dup(rf)
288                         yakmo.reduceratfunc(alpha.u[k].c)
289                 | `alpha_h1 (k): std.slpush(&fc_h1, `H((k : weyl_reflection), t.dup(rf)))
290                 | `alpha_h2 (k): std.slpush(&fc_h2, `H((k : weyl_reflection), t.dup(rf)))
291                 | `alpha_h3 (k): std.slpush(&fc_h3, `H((k : weyl_reflection), t.dup(rf)))
292                 ;;
293         ;;
295         auto fc_h1
296         auto fc_h2
297         auto fc_h3
298         match edge_mc_from_fc(kc, fc_h1)
299         | `std.Ok mc: alpha.h1 = mc
300         | `std.Err e:
301                 auto (e : t.doomed_str)
302                 -> `std.Err std.fmt("edge_mc_from_fc(h1): {}", e)
303         ;;
304         match edge_mc_from_fc(kc, fc_h2)
305         | `std.Ok mc: alpha.h2 = mc
306         | `std.Err e:
307                 auto (e : t.doomed_str)
308                 -> `std.Err std.fmt("edge_mc_from_fc(h2): {}", e)
309         ;;
310         match edge_mc_from_fc(kc, fc_h3)
311         | `std.Ok mc: alpha.h3 = mc
312         | `std.Err e:
313                 auto (e : t.doomed_str)
314                 -> `std.Err std.fmt("edge_mc_from_fc(h3): {}", e)
315         ;;
317         match fill_in_alpha(kc, &alpha)
318         | `std.Ok void:
319         | `std.Err e: -> `std.Err std.fmt("fill_in_alpha: {}", e)
320         ;;
322         -> `std.Ok (std.mk(alpha), d_class)
325 const invert_edges = {kc, TQ : T_Q#
326         var new_A = std.slalloc(TQ.A.len)
327         var new_B = std.slalloc(TQ.B.len)
328         var new_C = std.slalloc(TQ.C.len)
330         for var j = 0; j < TQ.A.len; ++j
331                 match yakmo.finv(TQ.A[j])
332                 | `std.Some y: new_A[j] = y
333                 | `std.None: new_A[j] = t.dup(TQ.A[j])
334                 ;;
335         ;;
337         for var j = 0; j < TQ.B.len; ++j
338                 match yakmo.finv(TQ.B[j])
339                 | `std.Some y: new_B[j] = y
340                 | `std.None: new_B[j] = t.dup(TQ.B[j])
341                 ;;
342         ;;
344         for var j = 0; j < TQ.C.len; ++j
345                 match yakmo.finv(TQ.C[j])
346                 | `std.Some y: new_C[j] = y
347                 | `std.None: new_C[j] = t.dup(TQ.C[j])
348                 ;;
349         ;;
351         __dispose__(TQ.A)
352         __dispose__(TQ.B)
353         __dispose__(TQ.C)
354         TQ.A = new_A
355         TQ.B = new_B
356         TQ.C = new_C
360    This applies the monomial part of M, in place, to TQ. It's split out
361    in order that we can use the dumb inversion trick to invert M and
362    keep the complicated w_k-fiddling in one spot.
363  */
364 const just_monomial_part = {kc : KC_type, TQ : T_Q#, d_class : Dynkin_automorphism_class
365         var sigmaG
366         match get_sigmaG_permutation(kc)
367         | `std.Ok perm: sigmaG = perm
368         | `std.Err e: -> `std.Err std.fmt("get_sigmaG_permutation({}): {}", kc, e)
369         ;;
371         var rank = get_n(kc)
372         var zrank : std.size = (rank : std.size)
373         var c : weyl_reflection[:] = [][:]
374         var h = coxeter_num(kc)
375         var l = h / 2 - 1
376         var our_u = [][:]
377         match get_coxeter_elt(kc)
378         | `std.Ok cc: c = cc
379         | `std.Err e: -> `std.Err std.fmt("get_coxeter_elt({}): {}", kc, e)
380         ;;
381         var w0 = get_w0(kc)
382         var one : yakmo.polynomial# = yakmo.rid()
383         auto one
385         var fc_sigmaGh1 : factorization_coordinate[:] = std.slalloc(zrank)
386         var fc_h2 : factorization_coordinate[:] = std.slalloc(zrank)
387         var fc_sigmaGh1_inv : factorization_coordinate[:] = std.slalloc(zrank)
388         for var j = 0; j < rank; ++j
389                 var sigmaj = inv_coxeter(kc, sigmaG[c[j]])
391                 match yakmo.finv(TQ.A[sigmaG[c[j]]])
392                 | `std.None: -> `std.Err std.fmt("cannot invert A[{}] = {}", sigmaG[c[j]], TQ.A[sigmaG[c[j]]])
393                 | `std.Some y:
394                         fc_sigmaGh1_inv[j] = `H(c[j], y)
395                 ;;
397                 fc_sigmaGh1[j] = `H((c[j] : weyl_reflection), t.dup(TQ.A[sigmaG[c[j]]]))
398                 fc_h2[j] = `H((c[j] : weyl_reflection), t.dup(TQ.B[c[j]]))
399         ;;
401         /*
402            w_k^{-1} = Length (w0.len - k + 1) prefix of w0^{-1}, backwards
403                     = Length (w0.len - k + 1) suffix of w0, forwards
404          */
405         var vi = c.len - 1
406         var vj = l
407         for var k = w0.len; k > rank; --k
408                 match apply_wk(kc, k, fc_sigmaGh1,               +1)
409                 | `std.Err e: -> `std.Err std.fmt("apply_wk(k={}) : {}", k, e)
410                 | `std.Ok loc_h1:
411                         auto loc_h1
412                         /*
413                            We can't reach directly into fc_XYZ here: the
414                            H elements all commute, and applying the word
415                            may have re-ordered them or (in pathological
416                            cases) eliminated them.
417                          */
418                         match chi(loc_h1, w0[k - 1])
419                         | `std.Some x:
420                                 auto (x : yakmo.ratfunc#)
421                                 yakmo.rmul_ip(TQ.v[c[vi]][vj], x)
422                         | `std.None:
423                         ;;
425                         match chi(fc_sigmaGh1_inv, w0[k - 1])
426                         | `std.Some x:
427                                 auto (x : yakmo.ratfunc#)
428                                 yakmo.rmul_ip(TQ.v[c[vi]][vj], x)
429                         | `std.None:
430                         ;;
432                         match chi(fc_h2, w0[k - 1])
433                         | `std.Some x:
434                                 auto (x : yakmo.ratfunc#)
435                                 yakmo.rmul_ip(TQ.v[c[vi]][vj], x)
436                         | `std.None:
437                         ;;
439                         yakmo.reduceratfunc(TQ.v[c[vi]][vj])
440                 ;;
442                 vi--
443                 if vi < 0
444                         vi = c.len - 1
445                         vj--
446                 ;;
447         ;;
449         -> `std.Ok void
452 const apply_Mdv = {kc, alpha
453         var rank = get_n(kc)
454         var h = coxeter_num(kc)
455         var l = h / 2 - 1
456         var alpha_left = alpha.0
457         var alpha_right = alpha.1
459         var T_Q_left
460         match apply_M(kc, alpha_left, get_trivial_Dynkin_automorphism(kc))
461         | `std.Ok (q, _): T_Q_left = q
462         | `std.Err e: -> `std.Err std.fmt("apply_M(left): {}", e)
463         ;;
464         auto T_Q_left
466         var T_Q_right
467         match apply_M(kc, alpha_right, get_trivial_Dynkin_automorphism(kc))
468         | `std.Ok (q, _): T_Q_right = q
469         | `std.Err e: -> `std.Err std.fmt("apply_M(right): {}", e)
470         ;;
471         auto T_Q_right
473         /* Make sure the two agree on the joining edge */
474         for var j = 0; j < T_Q_left.C.len; ++j
475                 if !std.eq(T_Q_left.C[j], T_Q_right.A[j])
476                         -> `std.Err std.fmt("cannot amalgamate: left.C = {}, right.A = {}", T_Q_left.C, T_Q_right.A)
477                 ;;
478         ;;
480         /* Copy it over */
481         var D = std.slalloc(T_Q_left.A.len)
482         for var j = 0; j < T_Q_left.A.len; ++j
483                 D[j] = t.dup(T_Q_left.A[j])
484         ;;
486         var E = std.slalloc(T_Q_left.B.len)
487         for var j = 0; j < T_Q_left.B.len; ++j
488                 E[j] = t.dup(T_Q_left.B[j])
489         ;;
491         var F = std.slalloc(T_Q_right.B.len)
492         for var j = 0; j < T_Q_right.B.len; ++j
493                 F[j] = t.dup(T_Q_right.B[j])
494         ;;
496         var G = std.slalloc(T_Q_right.C.len)
497         for var j = 0; j < T_Q_right.C.len; ++j
498                 G[j] = t.dup(T_Q_right.C[j])
499         ;;
501         var w = std.slalloc(T_Q_left.v.len)
502         var L = 2 * l + 1
503         for var j = 0; j < w.len; ++j
504                 w[j] = std.slalloc((L + 1 : std.size))
505                 w[j][0] = yakmo.rid()
507                 for var k = 1; k <= l; ++k
508                         w[j][k] = t.dup(T_Q_right.v[j][k])
509                 ;;
511                 w[j][l + 1] = t.dup(T_Q_right.A[j])
513                 for var k = 1; k <= l; ++k
514                         w[j][l + 1 + k] = t.dup(T_Q_left.v[j][k])
515                 ;;
516         ;;
518         var ret = [
519                 .D = D,
520                 .E = E,
521                 .F = F,
522                 .G = G,
523                 .w = w,
524         ]
525         -> `std.Ok std.mk(ret)
528 const apply_Mdh = {kc, alpha
529         var rank = get_n(kc)
530         var h = coxeter_num(kc)
531         var l = h / 2 - 1
532         var alpha_top = alpha.0
533         var alpha_bottom = alpha.1
534         var d = get_trivial_Dynkin_automorphism(kc)
536         var T_Q_top
537         match apply_M(kc, alpha_top, d)
538         | `std.Ok (q, _): T_Q_top = q
539         | `std.Err e: -> `std.Err std.fmt("apply_M(top): {}", e)
540         ;;
541         auto T_Q_top
543         var T_Q_bottom
544         match apply_M(kc, alpha_bottom, d)
545         | `std.Ok (q, _): T_Q_bottom = q
546         | `std.Err e: -> `std.Err std.fmt("apply_M(bottom): {}", e)
547         ;;
548         auto T_Q_bottom
550         /* Make sure the two agree on the joining edge */
551         for var j = 0; j < T_Q_top.C.len; ++j
552                 if !std.eq(T_Q_top.C[j], T_Q_bottom.B[j])
553                         -> `std.Err std.fmt("cannot amalgamate: top.C = {}, bottom.B = {}", T_Q_top.C, T_Q_bottom.B)
554                 ;;
555         ;;
557         /* Copy it over */
558         var D = std.slalloc(T_Q_bottom.A.len)
559         for var j = 0; j < T_Q_bottom.A.len; ++j
560                 D[j] = t.dup(T_Q_bottom.A[j])
561         ;;
563         var E = std.slalloc(T_Q_top.A.len)
564         for var j = 0; j < T_Q_top.A.len; ++j
565                 E[j] = t.dup(T_Q_top.A[j])
566         ;;
568         var F = std.slalloc(T_Q_top.B.len)
569         for var j = 0; j < T_Q_top.B.len; ++j
570                 F[j] = t.dup(T_Q_top.B[j])
571         ;;
573         var G = std.slalloc(T_Q_bottom.C.len)
574         for var j = 0; j < T_Q_bottom.C.len; ++j
575                 G[j] = t.dup(T_Q_bottom.C[j])
576         ;;
578         var w = std.slalloc(T_Q_top.v.len)
579         var L = 2 * l + 1
580         for var j = 0; j < w.len; ++j
581                 w[j] = std.slalloc((L + 1 : std.size))
582                 w[j][0] = yakmo.rid()
584                 for var k = 1; k <= l; ++k
585                         w[j][k] = t.dup(T_Q_top.v[j][k])
586                 ;;
588                 w[j][l + 1] = t.dup(T_Q_top.C[j])
590                 for var k = 1; k <= l; ++k
591                         w[j][l + 1 + k] = t.dup(T_Q_bottom.v[j][k])
592                 ;;
593         ;;
595         var ret = [
596                 .D = D,
597                 .E = E,
598                 .F = F,
599                 .G = G,
600                 .w = w,
601         ]
602         -> `std.Ok std.mk(ret)
605 const unapply_Mdv = {kc : KC_type, TQ_orig : T_Q13#
606         var rank = get_n(kc)
607         var h = coxeter_num(kc)
608         var l = h / 2 - 1
609         var L = 2 * l + 1
610         var middle = std.slalloc((rank + 1 : std.size))
611         for var j = 0; j <= rank; ++j
612                 middle[j] = TQ_orig.w[j][l + 1]
613         ;;
615         var v_left = std.slalloc((rank + 1 : std.size))
616         var v_right = std.slalloc((rank + 1 : std.size))
617         for var j = 0; j <= rank; ++j
618                 v_left[j] = TQ_orig.w[j][l + 1:L + 1]
619                 v_right[j] = TQ_orig.w[j][0:l + 1]
620         ;;
622         var TQ_left = [
623                 .A = TQ_orig.D,
624                 .B = TQ_orig.E,
625                 .C = middle,
626                 .v = v_left,
627         ]
629         var TQ_right = [
630                 .A = middle,
631                 .B = TQ_orig.F,
632                 .C = TQ_orig.G,
633                 .v = v_right,
634         ]
636         var d = get_trivial_Dynkin_automorphism(kc)
638         var ret
639         match (unapply_M(kc, &TQ_left, d), unapply_M(kc, &TQ_right, d))
640         | (`std.Err e, _): ret = `std.Err std.fmt("unapply_M(left): {}", e)
641         | (_, `std.Err e): ret = `std.Err std.fmt("unapply_M(right): {}", e)
642         | (`std.Ok (aleft, _), `std.Ok (aright, _)):
643                 ret = `std.Ok (aleft, aright)
644         ;;
646         std.slfree(middle)
647         std.slfree(v_left)
648         std.slfree(v_right)
650         -> ret
653 const unapply_Mdh = {kc : KC_type, TQ_orig : T_Q13#
654         var rank = get_n(kc)
655         var h = coxeter_num(kc)
656         var l = h / 2 - 1
657         var L = 2 * l + 1
658         var d = get_trivial_Dynkin_automorphism(kc)
659         var middle = std.slalloc((rank + 1 : std.size))
660         for var j = 0; j <= rank; ++j
661                 middle[j] = TQ_orig.w[j][l + 1]
662         ;;
664         var v_top = std.slalloc((rank + 1 : std.size))
665         var v_bottom = std.slalloc((rank + 1 : std.size))
666         for var j = 0; j <= rank; ++j
667                 /* My apologies for the 1/l/Ls here */
668                 /*
669                    TODO: top and bottom might need to be flipped. This
670                    might also affect rename_according_to_muflip
671                  */
672                 v_top[j] = TQ_orig.w[j][0:l + 1]
673                 v_bottom[j] = TQ_orig.w[j][l + 1:L + 1]
674         ;;
676         var TQ_top = [
677                 .A = TQ_orig.E,
678                 .B = TQ_orig.F,
679                 .C = middle,
680                 .v = v_top,
681         ]
683         var TQ_bottom = [
684                 .A = TQ_orig.D,
685                 .B = middle,
686                 .C = TQ_orig.G,
687                 .v = v_bottom,
688         ]
690         var ret
691         match (unapply_M(kc, &TQ_top, d), unapply_M(kc, &TQ_bottom, d))
692         | (`std.Err e, _): ret = `std.Err std.fmt("unapply_M(top): {}", e)
693         | (_, `std.Err e): ret = `std.Err std.fmt("unapply_M(bottom): {}", e)
694         | (`std.Ok (atop, _), `std.Ok (abottom, _)): ret = `std.Ok (atop, abottom)
695         ;;
697         std.slfree(middle)
698         std.slfree(v_top)
699         std.slfree(v_bottom)
701         -> ret