12 /* vertex fatness -- usually 1, 2, or 3 */
15 /* For when I get around to writing a Clav-like GUI */
18 color : (uint8, uint8, uint8)
20 /* TODO: yakmo.polynomial(@a)#, and then genericize everything that follows */
21 acoord : std.option(yakmo.polynomial#)
22 xcoord : std.option(yakmo.ratfunc#)
24 impl disposable vertex
33 /* calculate sigma on the fly */
34 epsilon : yakmo.Q#[:][:]
36 /* Whether A- and X-coordinates should be used */
40 impl disposable quiver#
41 impl std.equatable quiver#
42 impl t.dupable quiver#
44 type quiver_mk_opts = struct
49 const mk : (-> quiver#)
50 const mk_with_opts : (opts : quiver_mk_opts -> quiver#)
53 Can't return the vertex# itself right now... there's some weird typing thing. TODO: bother Ori
55 Note: dups name, so you can free it.
57 const add_vertex : (q : quiver#, name : byte[:], weight : uint32 -> std.result(std.size, byte[:]))
59 const delete_vertex : (q : quiver#, name : byte[:] -> std.result(void, byte[:]))
61 const add_to_edge : (q : quiver#, from_name : byte[:], to_name : byte[:], sigma : yakmo.Q# -> std.result(void, byte[:]))
62 const mutate_ip : (q : quiver#, vertex_name : byte[:] -> std.result(void, byte[:]))
63 const mutate_ip_i : (q : quiver#, vertex_index : std.size -> std.result(void, byte[:]))
64 const mutateseq_ip : (q : quiver#, vertex_names : byte[:][:] -> std.result(void, byte[:]))
65 const mutateseq_ip_i : (q : quiver#, vertex_indices : std.size[:] -> std.result(void, byte[:]))
67 /* To get epsilon, just use q.epsilon[from][to] */
68 const get_sigma : (q : quiver#, from_name : byte[:], to_name : byte[:] -> std.result(yakmo.Q#, byte[:]))
69 const get_sigma_ii : (q : quiver#, j : std.size, k : std.size -> std.option(yakmo.Q#))
71 /* Returns (j, &q.v[j]) for unique j s.t. q.v[j].name is name */
72 const find_vertex : (q : quiver#, name : byte[:] -> std.option((std.size, vertex#)))
75 const from_bytes : (b : byte[:], opts : quiver_mk_opts -> std.result(quiver#, byte[:]))
76 const to_bytes : (q : quiver# -> byte[:])
80 /* As graphs: names/weights/edge weights only */
81 const graph_equivalent : (q1 : quiver.quiver#, q2 : quiver.quiver# -> bool)
85 /* TODO: these are already done in libyakmo. Kill 'em once the types detect that. */
86 impl disposable yakmo.ratfunc# =
87 __dispose__ = {x : yakmo.ratfunc#
90 std.bytefree((x : byte#), sizeof(yakmo.ratfunc))
93 impl disposable yakmo.polynomial# =
94 __dispose__ = {p : yakmo.polynomial#
95 for var j = 0; j < p.terms.len; ++j
96 __dispose__(p.terms[j])
99 /* TODO: std.free(p) */
100 std.bytefree((p : byte#), sizeof(yakmo.polynomial))
103 impl disposable yakmo.var_power =
104 __dispose__ = {vp : yakmo.var_power
105 std.slfree(vp.variable)
106 __dispose__(vp.power)
109 impl disposable yakmo.monomial =
110 __dispose__ = {x : yakmo.monomial
118 impl disposable yakmo.Q# =
119 __dispose__ = {q : yakmo.Q#
122 std.bytefree((q : byte#), sizeof(yakmo.Q))
125 impl disposable yakmo.Qi# =
126 __dispose__ = {z : yakmo.Qi#
129 std.bytefree((z : byte#), sizeof(yakmo.Qi))
132 impl disposable yakmo.Z# =
134 var ba : std.bigint# = (a : std.bigint#)
139 var findnum : std.result(regex.regex#, regex.status)
140 var findvert : std.result(regex.regex#, regex.status)
141 var findedge : std.result(regex.regex#, regex.status)
144 findnum = regex.compile("^(\\d+) Vertices\n")
145 findvert = regex.compile("^v[[](\\d+)[]]\\hf:(\\d+)\\hx:(-?\\d+)\\hy:(-?\\d+) c:0x(..)(..)(..) l:(\\d+) n:")
146 findedge = regex.compile("^e[[](\\d+)[]][[](\\d+)[]] (-?\\d+)/(\\d+)\n")
150 impl disposable vertex =
151 __dispose__ = {v : vertex
153 | `std.Some a: __dispose__(a)
157 | `std.Some x: __dispose__(x)
163 impl t.dupable vertex =
167 | `std.None: da = `std.None
168 | `std.Some a: da = `std.Some t.dup(a)
173 | `std.None: dx = `std.None
174 | `std.Some x: dx = `std.Some t.dup(x)
178 .name = std.sldup(v.name),
189 impl disposable quiver# =
190 __dispose__ = {q : quiver#
196 for var j = 0; j < q.epsilon.len; ++j
197 for var k = 0; k < q.epsilon[j].len; ++k
198 __dispose__(q.epsilon[j][k])
200 std.slfree(q.epsilon[j])
202 std.slfree(q.epsilon)
204 std.bytefree((q : byte#), sizeof(quiver))
209 impl t.dupable quiver# =
211 var new_q : quiver = [
213 .epsilon = t.dup(q.epsilon),
214 .use_a_coords = q.use_a_coords,
215 .use_x_coords = q.use_x_coords,
217 -> (std.mk(new_q) : quiver#)
221 impl std.equatable quiver# =
223 if qa.v.len != qb.v.len
227 for var j = 0; j < qa.v.len; ++j
228 var v1 : vertex# = &qa.v[j]
229 match find_vertex(qb, qa.v[j].name)
230 | `std.None: -> false
233 TODO: split this off into vertex equality.
234 That may screw up types, though.
236 if !std.eq(v1.name, v2.name)
243 if v1.x_pos != v2.x_pos
246 if v1.y_pos != v2.y_pos
249 if v1.color.0 != v2.color.0
252 if v1.color.1 != v2.color.1
255 if v1.color.2 != v2.color.2
258 match (v1.acoord, v2.acoord)
259 | (`std.None, `std.None):
260 | (`std.Some _, `std.None): -> false
261 | (`std.None, `std.Some _): -> false
262 | (`std.Some a1, `std.Some a2):
267 match (v1.xcoord, v2.xcoord)
268 | (`std.None, `std.None):
269 | (`std.Some _, `std.None): -> false
270 | (`std.None, `std.Some _): -> false
271 | (`std.Some x1, `std.Some x2):
279 for var i = 0; i < qa.epsilon.len; ++i
280 for var j = 0; j < qa.epsilon.len; ++j
281 if !std.eq(qa.epsilon[i][j], qb.epsilon[i][j])
291 const mk_with_opts = { opts : quiver_mk_opts
295 .use_a_coords = opts.use_a_coords,
296 .use_x_coords = opts.use_x_coords,
304 .use_a_coords = false,
305 .use_x_coords = false,
309 const add_vertex = {q, name, weight
310 /* No duplicate names allowed */
312 if std.eq(vv.name, name)
313 -> `std.Err std.fmt("vertex \"{}\" already exists in quiver", name)
317 /* Start out the A-coord and X-coord as trivial */
319 .name = std.sldup(name),
325 /* Add each epsilon(-, v) */
326 for var j = 0; j < q.v.len; ++j
327 std.slpush(&q.epsilon[j], yakmo.gid())
330 /* Add each epsilon(v, -) */
331 var ns : yakmo.Q#[:] = [][:]
332 for var j = 0; j <= q.v.len; ++j
333 std.slpush(&ns, yakmo.gid())
335 std.slpush(&q.epsilon, ns)
337 /* And finally, add v */
340 -> `std.Ok (q.v.len - 1)
343 const delete_vertex = {q, name
345 TODO: this appears ugly and inefficient -- you're right. This
346 is to remind me to implement some sort of t.sldispose
347 function that combines __dispose__ and sldel. With that in
348 place, this would look much cleaner.
353 for j = 0; j < q.v.len; ++j
354 if std.eq(q.v[j].name, name)
361 -> `std.Err std.fmt("vertex \"{}\" not present", name)
367 for var i = 0; i < q.epsilon.len; ++i
368 __dispose__(q.epsilon[i][j])
369 std.sldel(&q.epsilon[i], j)
372 for var i = 0; i < q.epsilon[j].len; ++i
373 __dispose__(q.epsilon[j][i])
375 std.slfree(q.epsilon[j])
376 std.sldel(&q.epsilon, j)
381 const add_to_edge = {q, from_name, to_name, sigma
382 if std.eq(from_name, to_name)
383 -> `std.Err std.fmt("vertices must be different")
388 var found_j : bool = false
389 var found_k : bool = false
392 for var i = 0; i < q.v.len; ++i
393 if std.eq(q.v[i].name, from_name)
398 if std.eq(q.v[i].name, to_name)
405 -> `std.Err std.fmt("vertex \"{}\" not present", from_name)
409 -> `std.Err std.fmt("vertex \"{}\" not present", to_name)
412 -> add_to_edge_ii(q, j, k, sigma)
415 const add_to_edge_ii = {q, from, to, sigma
416 if from < 0 || from >= q.v.len || to < 0 || to >= q.v.len
417 -> `std.Err std.fmt("indices out of range")
420 var weightto = auto yakmo.QfromZ(q.v[to].d)
421 var weightfrom = auto yakmo.QfromZ(q.v[from].d)
422 var gcd : yakmo.Q# = auto yakmo.gcd(weightto, weightfrom)
423 match yakmo.finv(gcd)
424 | `std.None: -> `std.Err std.fmt("vertex weight gcd ({}, {}) = {} is uninvertible", weightto, weightfrom, gcd)
426 yakmo.rmul_ip(inv_gcd, sigma)
427 yakmo.gadd_ip(q.epsilon[from][to], auto yakmo.rmul(inv_gcd, weightto))
428 yakmo.gneg_ip(inv_gcd)
429 yakmo.gadd_ip(q.epsilon[to][from], auto yakmo.rmul(inv_gcd, weightfrom))
436 const mutate_ip = {q, vertex_name
438 var found_k : bool = false
440 for var j = 0; j < q.v.len; ++j
441 if std.eq(q.v[j].name, vertex_name)
449 -> `std.Err std.fmt("vertex “{}” not present", vertex_name)
455 const mutate_ip_i = {q, k
456 if k < 0 || k >= q.v.len
457 -> `std.Err std.fmt("index {} out of range", k)
460 /* To begin with, get the A-coordinates right */
462 /* TODO: all the __dispose__ littered throughout should become "auto" */
463 var coords_plus = yakmo.rid()
464 var coords_minus = yakmo.rid()
468 | `std.None: -> `std.Err std.fmt("Quiver has A-coordinates enabled, but {} has no A-coordinate set", q.v[k].name)
469 | `std.Some a: qvka = a
472 for var j = 0; j < q.v.len; ++j
473 var eps = q.epsilon[k][j]
476 | `std.None: -> `std.Err std.fmt("Quiver has A-coordinates enabled, but {} has no A-coordinate set", q.v[j].name)
477 | `std.Some a: qvja = a
480 match good_rpowr_poly(qvja, eps)
482 yakmo.rmul_ip(coords_plus, x)
485 auto (e : t.doomed_str)
486 -> `std.Err std.fmt("good_rpowr_poly({}, {}): {}", qvja, eps, e)
489 var neps = yakmo.gneg(eps)
490 match good_rpowr_poly(qvja, neps)
492 yakmo.rmul_ip(coords_minus, x)
495 auto (e : t.doomed_str)
496 -> `std.Err std.fmt("good_rpowr_poly({}, {}): {}", qvja, neps, e)
502 var sum = auto yakmo.gadd(coords_plus, coords_minus)
503 __dispose__(coords_plus)
504 __dispose__(coords_minus)
506 match yakmo.div_maybe(sum, qvka)
507 | `std.None: -> `std.Err std.fmt("Laurent phenomenon fails when mutating at {}: cannot divide {} by {}", q.v[k].name, sum, qvka)
508 | `std.Some new_acoord:
509 var old_acoord = qvka
510 q.v[k].acoord = `std.Some new_acoord
511 __dispose__(old_acoord)
515 /* Now the X-coordinates */
519 | `std.None: -> `std.Err std.fmt("Quiver has X-coordinates enabled, but {} has no X-coordinate set", q.v[k].name)
520 | `std.Some x: qvkx = x
529 -> `std.Err std.fmt("X-coord of {} is 0", q.v[k].name)
532 for var j = 0; j < q.v.len; ++j
539 | `std.None: -> `std.Err std.fmt("Quiver has X-coordinates enabled, but {} has no X-coordinate set", q.v[j].name)
540 | `std.Some x: qvjx = x
543 var prod = yakmo.rid()
544 var exp = yakmo.gneg(q.epsilon[j][k])
546 match yakmo.cmp_zero(exp)
552 yakmo.gadd_ip(prod, xki)
553 match yakmo.finv(prod)
559 -> `std.Err std.fmt("(1 + X^eps) is 0")
562 yakmo.gadd_ip(prod, xk)
565 match good_rpowr_ratfunc(prod, exp)
567 yakmo.rmul_ip(qvjx, y)
568 q.v[j].xcoord = `std.Some qvjx
571 auto (e : t.doomed_str)
572 -> `std.Err std.fmt("good_rpowr_ratfunc({}, {}): {}", prod, exp, e)
581 q.v[k].xcoord = `std.Some xki
584 /* Finally, get the quiver right. First, complete all triangles */
585 for var i = 0; i < q.v.len; ++i
586 var eps_ik = q.epsilon[i][k]
587 if i == k || yakmo.eqQ(eps_ik, 0, 1)
591 for var j = 0; j < q.v.len; ++j
592 var eps_kj = q.epsilon[k][j]
593 if j == k || j == i || yakmo.eqQ(eps_kj, 0, 1)
597 if eps_ik.p.sign * eps_kj.p.sign <= 0
601 var abs_eps_ik = auto yakmo.abs(eps_ik)
602 var summand = auto yakmo.rmul(abs_eps_ik, eps_kj)
604 yakmo.gadd_ip(q.epsilon[i][j], summand)
608 /* Second, flip all incident edges */
609 for var i = 0; i < q.v.len; ++i
613 q.epsilon[k][i].p.sign *= -1
614 q.epsilon[i][k].p.sign *= -1
620 const mutateseq_ip = {q, vertex_names
621 for var j = 0; j < vertex_names.len; ++j
622 var vertex_name = vertex_names[j]
624 match mutate_ip(q, vertex_name)
627 auto (e : t.doomed_str)
628 -> `std.Err std.fmt("mutate(mu[{}]=\"{}\"): {}", j, vertex_name, e)
635 const mutateseq_ip_i = {q, vertex_indices
636 for var j = 0; j < vertex_indices.len; ++j
637 match mutate_ip_i(q, vertex_indices[j])
640 auto (e : t.doomed_str)
641 -> `std.Err std.fmt("mutate(mu[{}]=\"{}\"): {}", j, vertex_indices[j], e)
648 /* *** This should be in libyakmo (and be generic), but the types are fucking up */
650 /* and why does only _poly need the explicit labelling in the prototype ??? */
651 const good_rpowr_poly : (b : yakmo.polynomial#, exp : yakmo.Q# -> std.result(yakmo.polynomial#, byte[:])) = {b : yakmo.polynomial#, exp : yakmo.Q#
653 -> `std.Err std.fmt("{} must be >= 0", exp)
656 if !std.bigeqi(exp.q, 1)
657 -> `std.Err std.fmt("{} must be an integer", exp)
660 var res = yakmo.rid()
661 var double = t.dup(b)
662 var np : std.bigint# = std.bigdup(exp.p)
663 while !std.bigiszero(np)
664 if !std.bigiseven(np)
665 yakmo.rmul_ip(res, double)
668 yakmo.rmul_ip(double, double)
676 const good_rpowr_ratfunc = {b : yakmo.ratfunc#, exp : yakmo.Q#
678 -> `std.Err std.fmt("{} must be >= 0", exp)
681 if !std.bigeqi(exp.q, 1)
682 -> `std.Err std.fmt("{} must be an integer", exp)
685 var res = yakmo.rid()
686 var double = t.dup(b)
687 var np : std.bigint# = std.bigdup(exp.p)
688 while !std.bigiszero(np)
689 if !std.bigiseven(np)
690 yakmo.rmul_ip(res, double)
693 yakmo.rmul_ip(double, double)
703 const get_sigma = {q, from_name, to_name
706 var found_j : bool = false
707 var found_k : bool = false
710 for var i = 0; i < q.v.len; ++i
711 if std.eq(q.v[i].name, from_name)
716 if std.eq(q.v[i].name, to_name)
723 -> `std.Err std.fmt("vertex “{}” not present", from_name)
727 -> `std.Err std.fmt("vertex “{}” not present", to_name)
730 match get_sigma_ii(q, j, k)
731 | `std.None: -> `std.Err std.fmt("Malformed vertex weights")
732 | `std.Some res: -> `std.Ok res
736 const get_sigma_ii = {q, j, k
737 if j < 0 || j >= q.v.len || k < 0 || k >= q.v.len
740 if q.v[j].d <= 0 || q.v[k].d <= 0
744 var eps = q.epsilon[j][k]
755 var t = a - b*(a / b)
759 var mul = auto std.try(yakmo.Qfrom(gcd, q.v[k].d))
760 -> `std.Some yakmo.rmul(eps, mul)
763 const find_vertex = {q : quiver#, name : byte[:]
764 var found : bool = false
766 for var k = 0; k < q.v.len; ++k
767 if std.eq(name, q.v[k].name)
769 /* No unique j, return nothing (should be impossible) */
779 /* TODO: dummy assignment needed to help types out */
780 var v : vertex# = &q.v[j]
781 -> `std.Some( (j, v) )
787 const from_bytes = {b, opts : quiver_mk_opts
788 var q : quiver# = mk_with_opts(opts)
789 var e : byte[:] = [][:]
790 var res : std.result(quiver#, byte[:]) = `std.Ok q
792 var numvert : std.size = 0
797 res = `std.Err std.fmt("can't parse; error compiling findnum regex: {}", s)
800 match regex.search(r, b)
803 numvert = intparsedef(m[1], 0)
805 res = `std.Err std.fmt("input should start with something like “8 Vertices”")
810 /* "v[3] f:1 x:401 y:76 c:0x4ce7ff l:2 n:A1" */
813 res = `std.Err std.fmt("can't parse; error compiling findvert regex: {}", s)
816 for var j = 0; j < numvert; ++j
817 match regex.search(r, b)
820 res = `std.Err std.fmt("malformed [vertex] line {}", j + 2)
825 Calculate the name first, so we can
826 advance past it for the next line
828 var l : std.size = intparsedef(m[8], 0)
830 if b.len - m[0].len < l
831 res = `std.Err std.fmt("malformed [vertex] line {}", j + 2)
835 var name = b[m[0].len:m[0].len + l]
836 b = b[m[0].len + l + 1:]
838 /* Now grab the boring stuff: index, weight, pos, ... */
839 var idx : std.size = intparsedef(m[1], -1)
841 res = `std.Err std.fmt("malformed [vertex] line {}; out-of-sequence vertex idx", j + 2)
845 var weight : uint32 = intparsedef(m[2], 0)
846 var x_pos : int64 = intparsedef(m[3], 0)
847 var y_pos : int64 = intparsedef(m[4], 0)
848 var c_r : uint8 = intparsehexdef(m[5], 0)
849 var c_g : uint8 = intparsehexdef(m[6], 0)
850 var c_b : uint8 = intparsehexdef(m[7], 0)
851 match add_vertex(q, name, weight)
853 res = `std.Err std.fmt("error for v[{}]: {}", j, er)
857 q.v[jj].x_pos = x_pos
858 q.v[jj].y_pos = y_pos
859 q.v[jj].color = (c_r, c_g, c_b)
862 res = `std.Err std.fmt("malformed [vertex] line {}", j + 2)
870 res = `std.Err std.fmt("can't parse; error compiling findedge regex: {}", s)
873 var ln : std.size = numvert + 2
875 match regex.search(r, b)
877 var j : std.size = intparsedef(m[1], -1)
878 var k : std.size = intparsedef(m[2], -1)
879 var num : int64 = intparsedef(m[3], 0)
880 var den : int64 = intparsedef(m[4], 1)
883 if j < 0 || j > q.v.len || k < 0 || k > q.v.len
884 res = `std.Err std.fmt("[edge] line {}: bad indices", ln)
889 match yakmo.Qfrom(num, den)
891 res = `std.Err std.fmt("[edge] line {}: bad fraction: {}", ln, er)
895 __dispose__(q.epsilon[j][k])
896 q.epsilon[j][k] = epsilon
904 res = `std.Err std.fmt("malformed [edge] line {}: {}", ln, b)
922 std.intparse with a default value. After all, we matched this stuff
923 with regex, it's got to be valid, right?
925 generic intparsedef = {s : byte[:], def : @a :: integral,numeric @a
926 match std.intparse(s)
932 generic intparsehexdef = {s : byte[:], def : @a :: integral,numeric @a
933 match std.intparsebase(s, 16)
940 var sb : std.strbuf# = std.mksb()
942 std.sbfmt(sb, "{} Vertices\n", q.v.len)
944 for var j = 0; j < q.v.len; ++j
945 var v : vertex# = &q.v[j]
946 std.sbfmt(sb, "v[{}] f:{} x:{} y:{} c:0x{w=2,p=0,x}{w=2,p=0,x}{w=2,p=0,x} l:{} n:{}\n",
947 j, v.d, v.x_pos, v.y_pos, v.color.0, v.color.1, v.color.2, v.name.len, v.name)
950 for var j = 0; j < q.epsilon.len; ++j
951 for var k = 0; k < q.epsilon[j].len; ++k
952 var e : yakmo.Q# = q.epsilon[j][k]
953 if !yakmo.eqQ(e, 0, 1)
954 std.sbfmt(sb, "e[{}][{}] {}/{}\n", j, k, e.p, e.q)
962 /* Whether two quivers are equivalent as graphs. No vertex renaming is permitted */
963 const graph_equivalent = {q1 : quiver.quiver#, q2 : quiver.quiver#
964 if q1.v.len != q2.v.len
968 var perm : std.size[:] = std.slalloc(q1.v.len)
972 We're allowed to be sloppy here building the permutation
973 because no duplicate names are permitted in the quivers.
975 for var j = 0; j < q1.v.len; ++j
976 for var k = 0; k < q2.v.len; ++k
977 if std.eq(q1.v[j].name, q2.v[k].name)
979 if q1.v[j].d != q2.v[k].d
987 for var j = 0; j < q1.v.len; ++j
988 for var k = 0; k < q1.v.len; ++k
989 if !std.eq(q1.epsilon[j][k], q2.epsilon[perm[j]][perm[k]])