Exemple #1
0
def make_continent_naive(veering_isosig, max_num_tetrahedra=50):
    tri, angle = isosig_to_tri_angle(veering_isosig)
    vt = veering_triangulation(tri, angle)  #, tet_shapes = tet_shapes)
    initial_tet_face = tet_face(vt, 0, 0, verts_pos=[None, None, None, None])
    con = continent(vt,
                    initial_tet_face)  #, desired_vertices = desired_vertices )
    con.build_naive(max_num_tetrahedra=max_num_tetrahedra)
    con.make_convex()
    print(len(con.triangles), len(con.vertices), len(con.tetrahedra))
    return con
def fan_stacks(tri, angle):
    """Find lists of tetrahedra that make up the fans, including the toggles at either end"""
    vt = veering_triangulation(tri, angle)
    veering_colours = vt.veering_colours  ## "red" or "blue"
    tet_types = vt.tet_types  ### "toggle", "red" or "blue"
    tet_vert_coors = vt.coorientations
    toggle_nums = [
        i for i in range(len(tet_types)) if tet_types[i] == "toggle"
    ]

    out = []
    for toggle_num in toggle_nums:
        tops, bottoms = get_top_and_bottom_nums(tet_vert_coors, toggle_num)
        for i in range(2):

            tet_nums = [toggle_num]
            trailing_vertex = bottoms[i]
            other_b = bottoms[(i + 1) % 2]
            t0, t1 = tops
            if vt.get_edge_between_verts_colour(
                    toggle_num, (t0, t1)) == vt.get_edge_between_verts_colour(
                        toggle_num, (t0, other_b)):
                leading_vertex = t0
            else:
                leading_vertex = t1

            tet = tri.tetrahedron(toggle_num)
            while True:
                gluing = tet.adjacentGluing(trailing_vertex)
                tet = tet.adjacentTetrahedron(trailing_vertex)
                # e0, e1 = gluing[e0], gluing[e1]
                leading_vertex, trailing_vertex = gluing[
                    trailing_vertex], gluing[leading_vertex]
                tet_nums.append(tet.index())
                if tet_types[tet.index()] == "toggle":
                    break
            out.append(tet_nums)
    return out
def excise_fans(tri, angle, fan_nums=None):
    vt = veering_triangulation(tri, angle)
    veering_colours = vt.veering_colours  ## "red" or "blue"
    tet_types = vt.tet_types  ### "toggle", "red" or "blue"
    tet_vert_coors = vt.coorientations
    if fan_nums == None:  ## do all fans
        fan_nums = [
            n for n in range(len(tet_types)) if tet_types[n] != "toggle"
        ]

    excisedAngle = angle[:]
    for fan_num in sorted(fan_nums, reverse=True):
        del excisedAngle[fan_num]

    minority_edge_pairs = []
    for fan_num in fan_nums:
        assert tet_types[fan_num] != "toggle"
        tops, bottoms = get_top_and_bottom_nums(tet_vert_coors, fan_num)
        if 0 in tops:
            pi_pair = list(tops)
        else:
            pi_pair = list(bottoms)
        other = pi_pair[(pi_pair.index(0) + 1) % 2]
        for i in range(1, 4):
            if i != other:
                if vt.get_edge_between_verts_colour(
                        fan_num,
                    (0, i)) != tet_types[fan_num]:  ### "red" or "blue"
                    last = 6 - i - other  ## the fourth vertex
                    minority_edge_pairs.append([(0, i), (other, last)])
                    break
    excisedTri = regina.Triangulation3(tri)  ### copy

    fan_tets = [excisedTri.tetrahedron(fan_num) for fan_num in fan_nums]
    for k, tet in enumerate(fan_tets):
        # print 'k', k, 'tet.index', tet.index()
        minority_edge_pair = minority_edge_pairs[k]
        # print minority_edge_pair
        ### record gluings for neighbours
        neighbours = [tet.adjacentSimplex(i) for i in range(4)]
        gluings = [tet.adjacentGluing(i) for i in range(4)]

        # print [neighbour.index() for neighbour in neighbours]
        # print gluings

        tet.isolate()

        ## now glue neighbours to each other

        if tet not in neighbours:
            to_glue = [0, 1, 2, 3]
            while to_glue != []:
                i = to_glue.pop()
                if i in minority_edge_pair[0]:
                    j = minority_edge_pair[0][(minority_edge_pair[0].index(i) +
                                               1) % 2]
                else:
                    j = minority_edge_pair[1][(minority_edge_pair[1].index(i) +
                                               1) % 2]

                teti = neighbours[i]
                tetj = neighbours[j]
                teti.join(
                    gluings[i][i], tetj,
                    gluings[j] * regina.Perm4(j, i) * (gluings[i].inverse()))
                to_glue.remove(j)
                # print i,j
        else:  ### tet is glued to itself, which makes it trickier to remove
            ### first, find self-gluings
            self_gluings = []
            self_gluings = [neighbours.index(tet)]
            self_gluings.append(neighbours.index(tet, self_gluings[0] +
                                                 1))  ### add second entry

            other_gluings = [0, 1, 2, 3]
            other_gluings.remove(self_gluings[0])
            other_gluings.remove(self_gluings[1])
            i, j = other_gluings
            teti = neighbours[i]
            tetj = neighbours[j]
            if i in minority_edge_pair[0]:
                p = minority_edge_pair[0][(minority_edge_pair[0].index(i) + 1)
                                          % 2]
                q = minority_edge_pair[1][(minority_edge_pair[1].index(j) + 1)
                                          % 2]
            else:
                p = minority_edge_pair[1][(minority_edge_pair[1].index(i) + 1)
                                          % 2]
                q = minority_edge_pair[0][(minority_edge_pair[0].index(j) + 1)
                                          % 2]
            teti.join(
                gluings[i][i], tetj, gluings[j] * regina.Perm4(j, q) *
                gluings[p] * regina.Perm4(p, i) * (gluings[i].inverse()))

        excisedTri.removeTetrahedron(tet)

    return excisedTri, excisedAngle
Exemple #4
0
def make_continent_drill_flow_cycle(veering_isosig, flow_cycle, num_steps):
    tri, angle = isosig_to_tri_angle(veering_isosig)
    vt = veering_triangulation(tri, angle)  #, tet_shapes = tet_shapes)
    ### format for loops: it is a list of tuples,
    ### each tuple is (tet_index, edge_index within this tet that we exit through)
    tet_0 = flow_cycle[0][0]
    face_0 = 0  ### only used for initial numbering of vertices of the continent, so who cares
    initial_tet_face = tet_face(vt,
                                tet_0,
                                face_0,
                                verts_pos=[None, None, None, None])
    con = continent(vt, initial_tet_face)

    side_face_collections, side_tet_collections = edge_side_face_collections(
        vt.tri,
        vt.angle,
        tet_vert_coorientations=vt.coorientations,
        return_tets=True,
        order_bottom_to_top=False)
    # print('sfc', side_face_collections)
    # print('stc', side_tet_collections)
    ### identify the next edge in the cycle

    init_tetrahedron = con.tetrahedra[0]
    init_edge = flow_edge_in_continent(init_tetrahedron, flow_cycle[0][1])

    flow_tetrahedra = [init_tetrahedron]
    flow_edges = [init_edge]
    ### both in the continent

    upwards_flow_index = 0
    downwards_flow_index = 0
    for i in range(num_steps):
        # print(i)
        if i % 2 == 0:  ### go up
            edge = flow_edges[-1]
            last_tet = None
            while True:
                con.update_boundary()
                upper_boundary_triangles = [
                    t for t in edge.boundary_triangles if t.is_upper
                ]
                if len(upper_boundary_triangles) == 0:
                    break  ## out of while loop
                last_tet = con.bury(upper_boundary_triangles[0])
            assert last_tet != None
            upwards_flow_index = (upwards_flow_index + 1) % len(flow_cycle)
            assert last_tet.index == flow_cycle[upwards_flow_index][0]
            flow_tetrahedra.append(last_tet)
            flow_edges.append(
                flow_edge_in_continent(last_tet,
                                       flow_cycle[upwards_flow_index][1]))
            con.make_convex(
            )  ### could this take us further up dual_cycle? We don't think so
        else:  ### go down
            tet = flow_tetrahedra[0]
            edge = tet.lower_edge()
            flow_edges = [edge] + flow_edges
            ### now build the continent to get new_lowest_tet
            manifold_edge = tri.edge(edge.index)
            downwards_flow_index = (downwards_flow_index - 1) % len(flow_cycle)
            ### is the flow cycle vertical through the tetrahedron?
            tet_below = get_tet_above_edge(
                vt.tri,
                vt.angle,
                manifold_edge,
                tet_vert_coorientations=vt.coorientations,
                get_tet_below_edge=True)
            tet_below_num = tet_below.index()
            top_vert_nums = get_tet_top_vert_nums(vt.coorientations,
                                                  tet_below_num)
            top_edge_num = vert_pair_to_edge_num[tuple(top_vert_nums)]

            if (tet_below_num, top_edge_num) == flow_cycle[
                    downwards_flow_index]:  ### flow cycle went straight up
                while True:
                    con.update_boundary()
                    lower_boundary_triangles = [
                        t for t in edge.boundary_triangles if not t.is_upper
                    ]
                    if len(lower_boundary_triangles) == 0:
                        break  ## out of while loop
                    last_tet = con.bury(lower_boundary_triangles[0])
            else:
                ### find which side of the edge our tet is in
                # print('edge index', edge.index)
                side_tet_collections_at_edge = side_tet_collections[
                    edge.index]  ## index in the manifold
                side_face_collections_at_edge = side_face_collections[
                    edge.index]
                downward_path = None
                flow_step = flow_cycle[downwards_flow_index]
                for i, side_tet_collection in enumerate(
                        side_tet_collections_at_edge):
                    if flow_step in side_tet_collection:
                        downward_path = side_tet_collection[:
                                                            side_tet_collection
                                                            .index(flow_step) +
                                                            1]
                        downward_path_faces = side_face_collections_at_edge[
                            i][:side_tet_collection.index(flow_step) + 1]
                assert downward_path != None
                for j, (tet_num, edge_num) in enumerate(downward_path):
                    con.update_boundary()
                    lower_boundary_triangles = [
                        t for t in edge.boundary_triangles if not t.is_upper
                        and t.index == downward_path_faces[j][0]
                    ]
                    assert len(lower_boundary_triangles) == 1
                    last_tet = con.bury(lower_boundary_triangles[0])
            assert last_tet != None
            flow_tetrahedra = [last_tet] + flow_tetrahedra
            con.make_convex(
            )  ### could this take us further down dual_cycle? We don't think so

    con.update_boundary()
    return con, flow_tetrahedra, flow_edges
Exemple #5
0
def make_continent_drill_dual_cycle(veering_isosig, dual_cycle, num_steps):
    tri, angle = isosig_to_tri_angle(veering_isosig)
    vt = veering_triangulation(tri, angle)  #, tet_shapes = tet_shapes)
    ### initial tetrahedron is above face 0 of the dual cycle
    face0 = vt.tri.triangles()[dual_cycle[0]]
    embeds = face0.embeddings()
    tet_0, face_0 = None, None
    for embed in embeds:
        tet_num = embed.simplex().index()
        face_num = embed.face()
        if vt.coorientations[tet_num][face_num] == -1:  ## into the tet
            tet_0, face_0 = tet_num, face_num
            break
    assert tet_0 != None and face_0 != None
    initial_tet_face = tet_face(vt,
                                tet_0,
                                face_0,
                                verts_pos=[None, None, None, None])
    con = continent(vt, initial_tet_face)

    ### identify the triangle corresponding to dual_cycle[1]
    for triangle in con.triangles:
        if not triangle.is_upper and triangle.index == dual_cycle[0]:
            lowest_triangle = triangle
        if triangle.is_upper and triangle.index == dual_cycle[1]:
            highest_triangle = triangle
    triangle_path = [lowest_triangle, highest_triangle]
    lowest_path_index = 0
    highest_path_index = 1
    # for i in range(num_steps):
    #     last_added_tet = con.bury(highest_triangle)

    #     ### find next_triangle
    #     highest_triangle = None
    #     for triangle in last_added_tet.upper_triangles:
    #         if triangle.index == dual_cycle[(path_index + 1) % len(dual_cycle)]:
    #             highest_triangle = triangle
    #             break
    #     assert highest_triangle != None
    #     triangle_path.append(highest_triangle)
    #     path_index = path_index + 1

    #     con.make_convex() ### could this take us further up dual_cycle?

    for i in range(num_steps):
        if i % 2 == 0:
            last_added_tet = con.bury(triangle_path[-1])  ## go up
            for triangle in last_added_tet.upper_triangles:
                if triangle.index == dual_cycle[(highest_path_index + 1) %
                                                len(dual_cycle)]:
                    triangle_path.append(triangle)
                    break
            highest_path_index = highest_path_index + 1
        else:
            last_added_tet = con.bury(triangle_path[0])  ## go down
            for triangle in last_added_tet.lower_triangles:
                if triangle.index == dual_cycle[(lowest_path_index - 1) %
                                                len(dual_cycle)]:
                    triangle_path.insert(0, triangle)
                    break
            lowest_path_index = lowest_path_index - 1

        con.make_convex()  ### could this take us further up dual_cycle?

    con.update_boundary()
    con.triangle_path = triangle_path
    return con
def initial_path_single(tri, angle, tet_shapes, verbose=0.0):
    vt = veering_triangulation(tri, angle, tet_shapes=tet_shapes)
    lower_faces = [i for i in range(4) if vt.coorientations[0][i] == -1]
    verts_CP1 = [[1, 0], [0, 1], [1, 1], [tet_shapes[0], 1]]
    return ([ct_edge(0, lower_faces[1], verts_CP1, 0, vt)], [])
Exemple #7
0
def run_tests(num_to_check=10, smaller_num_to_check = 10):

    import taut
    veering_isosigs = parse_data_file("Data/veering_census.txt")
    print("testing is_taut")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        assert taut.is_taut(tri, angle), sig

    print("testing isosig round trip")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        recovered_sig = taut.isosig_from_tri_angle(tri, angle)
        assert sig == recovered_sig, sig
        # we only test this round trip - the other round trip does not
        # make sense because tri->isosig is many to one.

    import transverse_taut
    print("testing is_transverse_taut")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        assert transverse_taut.is_transverse_taut(tri, angle), sig

    non_transverse_taut_isosigs = parse_data_file("Data/veering_non_transverse_taut_examples.txt")
    print("testing not is_transverse_taut")
    for sig in non_transverse_taut_isosigs:
        tri, angle = taut.isosig_to_tri_angle(sig)
        assert not transverse_taut.is_transverse_taut(tri, angle), sig

    import veering
    print("testing is_veering")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        assert veering.is_veering(tri, angle), sig

    # tri, angle = taut.isosig_to_tri_angle("cPcbbbdxm_10")
    # explore_mobius_surgery_graph(tri, angle, max_tetrahedra = 12)
    # # tests to see that it makes only veering triangulations as it goes

    import veering_dehn_surgery
    print("testing veering_dehn_surgery")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        for face_num in veering_dehn_surgery.get_mobius_strip_indices(tri):
            (tri_s, angle_s, face_num_s) = veering_dehn_surgery.veering_mobius_dehn_surgery(tri, angle, face_num)
            assert veering.is_veering(tri_s, angle_s), sig
            
    import veering_fan_excision
    print("testing veering_fan_excision")
    m003, _ = taut.isosig_to_tri_angle('cPcbbbdxm_10')
    m004, _ = taut.isosig_to_tri_angle('cPcbbbiht_12')
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        tet_types = veering.is_veering(tri, angle, return_type = "tet_types")
        if tet_types.count("toggle") == 2:
            excised_tri, _ = veering_fan_excision.excise_fans(tri, angle)
            assert ( excised_tri.isIsomorphicTo(m003) != None or
                     excised_tri.isIsomorphicTo(m004) != None ), sig

    import pachner
    print("testing pachner with taut structure")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        face_num = random.randrange(tri.countTriangles())
        result = pachner.twoThreeMove(tri, face_num, angle = angle, return_edge = True)  
        if result != False: 
            tri2, angle2, edge_num = result
            tri3, angle3 = pachner.threeTwoMove(tri2, edge_num, angle = angle2)
            assert taut.isosig_from_tri_angle(tri, angle) == taut.isosig_from_tri_angle(tri3, angle3), sig

    import branched_surface
    import regina
    print("testing branched_surface and pachner with branched surface")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        tri_original = regina.Triangulation3(tri) #copy
        branch = branched_surface.upper_branched_surface(tri, angle, return_lower = random.choice([True, False]))
        
        ### test branch isosig round trip
        sig_with_branch = branched_surface.isosig_from_tri_angle_branch(tri, angle, branch)
        tri2, angle2, branch2 = branched_surface.isosig_to_tri_angle_branch(sig_with_branch)
        assert (branch == branch2) and (angle == angle2), sig

        branch_original = branch[:] #copy
        face_num = random.randrange(tri.countTriangles())
        out = pachner.twoThreeMove(tri, face_num, branch = branch, return_edge = True)
        if out != False:
            tri, possible_branches, edge_num = out
            tri, branch = pachner.threeTwoMove(tri, edge_num, branch = possible_branches[0])
            all_isoms = tri.findAllIsomorphisms(tri_original)
            all_branches = [branched_surface.apply_isom_to_branched_surface(branch, isom) for isom in all_isoms]
            assert branch_original in all_branches, sig

    import flow_cycles
    import drill
    print("testing taut and branched drill + semiflows on drillings")
    for sig in random.sample(veering_isosigs, smaller_num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        branch = branched_surface.upper_branched_surface(tri, angle) ### also checks for veering and transverse taut
        found_loops = flow_cycles.find_flow_cycles(tri, branch)
        for loop in random.sample(found_loops, min(len(found_loops), 5)):  ## drill along at most 5 loops
            tri, angle = taut.isosig_to_tri_angle(sig)
            branch = branched_surface.upper_branched_surface(tri, angle) 
            tri_loop = flow_cycles.flow_cycle_to_triangle_loop(tri, branch, loop)
            if tri_loop != False: 
                if not flow_cycles.tri_loop_is_boundary_parallel(tri_loop, tri):
                    drill.drill(tri, tri_loop, angle = angle, branch = branch, sig = sig)
                    assert branched_surface.has_non_sing_semiflow(tri, branch), sig

    print("all basic tests passed")

    try:
        import snappy
        import snappy_util
        snappy_working = True
    except:
        print("failed to import from snappy?")
        snappy_working = False

    if snappy_working:        
        print("testing algebraic intersection")
        census = snappy.OrientableCuspedCensus() # not a set or list, so can't use random.sample
        for i in range(10):
            M = random.choice(census)
            n = M.num_cusps()
            peripheral_curves = M.gluing_equations()[-2*n:]
            for i in range(2*n):
                for j in range(i, 2*n):
                    alg_int = snappy_util.algebraic_intersection(peripheral_curves[i], peripheral_curves[j])
                    if i % 2 == 0 and j == i + 1:
                        assert alg_int == 1, M.name()
                    else:
                        assert alg_int == 0, M.name()
                       
    if snappy_working:
        import veering_drill_midsurface_bdy
        print("testing veering drilling and filling")
        for sig in random.sample(veering_isosigs[:3000], num_to_check):
            T, per = veering_drill_midsurface_bdy.drill_midsurface_bdy(sig)
            M = snappy.Manifold(T.snapPea())
            M.set_peripheral_curves("shortest")
            L = snappy_util.get_slopes_from_peripherals(M, per)
            M.dehn_fill(L)
            N = snappy.Manifold(sig.split("_")[0])
            assert M.is_isometric_to(N), sig

    if snappy_working:
        print("all tests depending on snappy passed")
   
    # try:
    #     from hashlib import md5
    #     from os import remove
    #     import pyx
    #     from boundary_triangulation import draw_triangulation_boundary_from_veering_isosig
    #     pyx_working = True
    # except:
    #     print("failed to import from pyx?")
    #     pyx_working = False

    # ladders_style_sigs = {
    #     "cPcbbbiht_12": "f34c1fdf65db9d02994752814803ae01",
    #     "gLLAQbecdfffhhnkqnc_120012": "091c85b4f4877276bfd8a955b769b496",
    #     "kLALPPzkcbbegfhgijjhhrwaaxnxxn_1221100101": "a0f15a8454f715f492c74ce1073a13a4",
    # }

    # geometric_style_sigs = {
    #     "cPcbbbiht_12": "1e74d0b68160c4922e85a5adb20a0f1d",
    #     "gLLAQbecdfffhhnkqnc_120012": "856a1fce74eb64f519bcda083303bd8f",
    #     "kLALPPzkcbbegfhgijjhhrwaaxnxxn_1221100101": "33bd23b34c5d977a103fa50ffe63120a",
    # }

    # args = {
    #     "draw_boundary_triangulation":True,
    #     "draw_triangles_near_poles": False,
    #     "ct_depth":-1,
    #     "ct_epsilon":0.03,
    #     "global_drawing_scale": 4,
    #     "delta": 0.2,
    #     "ladder_width": 10.0,
    #     "ladder_height": 20.0,
    #     "draw_labels": True,
    # }

    # shapes_data = read_from_pickle("Data/veering_shapes_up_to_ten_tetrahedra.pkl")

    # if pyx_working:
    #     for sig in ladders_style_sigs:
    #         print("testing boundary triangulation pictures, ladder style", sig)
    #         args["tet_shapes"] = shapes_data[sig]
    #         args["style"] = "ladders"
    #         file_name = draw_triangulation_boundary_from_veering_isosig(sig, args = args) 
    #         f = open(file_name, "rb")
    #         file_hash = md5(f.read())
    #         assert file_hash.hexdigest() == ladders_style_sigs[sig]
    #         f.close()
    #         remove(file_name)
        
    # if pyx_working:
    #     for sig in geometric_style_sigs:
    #         print("testing boundary triangulation pictures, ladder style", sig)
    #         args["tet_shapes"] = shapes_data[sig]
    #         args["style"] = "geometric"
    #         file_name = draw_triangulation_boundary_from_veering_isosig(sig, args = args) 
    #         f = open(file_name, "rb")
    #         file_hash = md5(f.read())
    #         assert file_hash.hexdigest() == geometric_style_sigs[sig]
    #         f.close()
    #         remove(file_name)

    # if pyx_working: 
    #     print("all tests depending on pyx passed")

    veering_polys = {
        "cPcbbbiht_12": [-4, -1, 1, 4],
        "eLMkbcddddedde_2100": [-2, -2, -2, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 2, 2],
        "gLLAQbecdfffhhnkqnc_120012": [-1, -1, -1, -1, 1, 1, 1, 1],
        "gLLPQcdfefefuoaaauo_022110": [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1],
    }

    # veering_polys = { ### old
    #     "cPcbbbiht_12": "a^3 - 4*a^2 + 4*a - 1",
    #     "eLMkbcddddedde_2100": "a^6*b - a^6 - 2*a^5*b - a^4*b^2 + a^5 + 2*a^4*b + a^3*b^2 - 2*a^3*b + a^3 + 2*a^2*b + a*b^2 - a^2 - 2*a*b - b^2 + b",
    #     "gLLAQbecdfffhhnkqnc_120012": "a^7 + a^6 + a^5 + a^4 - a^3 - a^2 - a - 1",
    #     "gLLPQcdfefefuoaaauo_022110": "a^12*b^3 - a^11*b^2 - a^10*b^3 - a^10*b^2 - a^7*b^3 - a^7*b^2 - a^6*b^3 + a^7*b + a^5*b^2 - a^6 - a^5*b - a^5 - a^2*b - a^2 - a*b + 1",
    # }

    taut_polys = {
        "cPcbbbiht_12": [-3, 1, 1],
        "eLMkbcddddedde_2100": [-1, -1, -1, 1, 1],
        "iLLAwQcccedfghhhlnhcqeesr_12001122": [],
    }

    # taut_polys = { ### old
    #     "cPcbbbiht_12": "a^2 - 3*a + 1",
    #     "eLMkbcddddedde_2100": "a^2*b - a^2 - a*b - b^2 + b",
    #     "iLLAwQcccedfghhhlnhcqeesr_12001122": "0",
    # }

    torus_bundles = [
        "cPcbbbiht_12",
        "eLMkbcdddhhqqa_1220",
        "gLMzQbcdefffhhqqqdl_122002",
    ]

    measured = [
        "gLLAQbecdfffhhnkqnc_120012",
        "iLLALQcccedhgghhlnxkxrkaa_12001112",
        "iLLAwQcccedfghhhlnhcqeesr_12001122",
    ]

    empties = [
        "fLAMcaccdeejsnaxk_20010",
        "gLALQbcbeeffhhwsras_211220",
        "hLALAkbcbeefgghhwsraqj_2112202",
    ]

    try:
        from sage.rings.integer_ring import ZZ
        sage_working = True
    except:
        print("failed to import from sage?")
        sage_working = False

    if sage_working:
        import taut_polytope
        print("testing is_layered")
        for sig in veering_isosigs[:17]:
            assert taut_polytope.is_layered(sig), sig
        for sig in veering_isosigs[17:21]:
            assert not taut_polytope.is_layered(sig), sig

    if sage_working:
        import fibered
        print("testing is_fibered")
        mflds = parse_data_file("Data/mflds_which_fiber.txt")
        mflds = [line.split("\t")[0:2] for line in mflds]
        for (name, kind) in random.sample(mflds, num_to_check):        
            assert fibered.is_fibered(name) == (kind == "fibered"), name

    if sage_working:
        import veering_polynomial
        import taut_polynomial
        print("testing veering poly")
        for sig in veering_polys:
            p = veering_polynomial.veering_polynomial(sig)
            assert check_polynomial_coefficients(p, veering_polys[sig]), sig
            ### Nov 2021: sage 9.4 changed how smith normal form works, which changed our polynomials
            ### to equivalent but not equal polynomials. To avoid this kind of change breaking things
            ### in the future, we changed to comparing the list of coefficients.
            # assert p.__repr__() == veering_polys[sig]
        print("testing taut poly")
        for sig in taut_polys:
            p = taut_polynomial.taut_polynomial_via_tree(sig)
            assert check_polynomial_coefficients(p, taut_polys[sig]), sig
        #     assert p.__repr__() == taut_polys[sig]
        print("testing divide")
        for sig in random.sample(veering_isosigs[:3000], num_to_check):
            p = veering_polynomial.veering_polynomial(sig)
            q = taut_polynomial.taut_polynomial_via_tree(sig)
            if q == 0:
                assert p == 0, sig
            else:
                assert q.divides(p), sig

    if sage_working:
        print("testing alex")
        for sig in random.sample(veering_isosigs[:3000], num_to_check):        
            snap_sig = sig.split("_")[0]
            M = snappy.Manifold(snap_sig)
            if M.homology().betti_number() == 1:
                assert taut_polynomial.taut_polynomial_via_tree(sig, mode = "alexander") == M.alexander_polynomial(), sig

    if sage_working:
        # would be nice to automate this - need to fetch the angle
        # structure say via z_charge.py...
        print("testing is_torus_bundle")
        for sig in torus_bundles: 
            assert taut_polytope.is_torus_bundle(sig), sig

    if sage_working:
        # ditto
        print("testing is_layered")
        for sig in torus_bundles:
            assert taut_polytope.is_layered(sig), sig
        print("testing measured")
        for sig in measured:
            assert taut_polytope.LMN_tri_angle(sig) == "M", sig
        print("testing empty")
        for sig in empties:
            assert taut_polytope.LMN_tri_angle(sig) == "N", sig

    if sage_working:  # warning - this takes random amounts of time!
        print("testing hom dim")
        for sig in random.sample(veering_isosigs[:3000], 3): # magic number
            # dimension = zero if and only if nothing is carried.
            assert (taut_polytope.taut_cone_homological_dim(sig) == 0) == (taut_polytope.LMN_tri_angle(sig) == "N"), sig

    if sage_working:      

        boundary_cycles = {
            ("eLMkbcddddedde_2100",(2,5,5,1,3,4,7,1)): "((-7, -7, 0, 0, 4, -3, 7, 0), (7, 7, 0, 0, -4, 3, -7, 0))",
            ("iLLLQPcbeegefhhhhhhahahha_01110221",(0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,0)): "((0, 0, -1, 1, 1, 0, 1, 1, -1, 0, 0, 0, 0, 1, 0, 1), (0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1))",
            ("ivvPQQcfhghgfghfaaaaaaaaa_01122000",(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)): "((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0), (-2, 0, -3, 1, 2, -1, 0, 2, -1, 0, 3, 1, -2, 1, 0, -1), (0, -2, 1, -3, 0, -1, 2, 0, -1, 2, -1, 1, 0, 1, -2, 3))",
        }

        taut_polys_with_cycles = {
            ("eLMkbcddddedde_2100", ((7, 7, 0, 0, -4, 3, -7, 0),)): [-1, -1, -1, 1, 1],
            ("iLLLQPcbeegefhhhhhhahahha_01110221", ((0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1),)): [1, 1, 2],
            ("ivvPQQcfhghgfghfaaaaaaaaa_01122000", ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))): [-4, -1, -1, 1, 1],
        }

        # taut_polys_with_cycles = {
        #     ("eLMkbcddddedde_2100", ((7, 7, 0, 0, -4, 3, -7, 0),)): "a^14 - a^8 - a^7 - a^6 + 1",
        #     ("iLLLQPcbeegefhhhhhhahahha_01110221", ((0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1),)): "a^2 + 2*a + 1",
        #     ("ivvPQQcfhghgfghfaaaaaaaaa_01122000", ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))): "a*b^2 - a^2 - 4*a*b - b^2 + a",
        # }


        taut_polys_image = {
            ('eLMkbcddddedde_2100', ((7, 8, -1, 0, -4, 4, -8, 0),)):[-1, -1, -1, 1, 1],
            ('ivvPQQcfhghgfghfaaaaaaaaa_01122000', ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2),)):[-2, -2, -1, -1, 1, 1],
            ('ivvPQQcfhghgfghfaaaaaaaaa_01122000', ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))):[-4, -1, -1, 1, 1]
        }

        # taut_polys_image = {
        #     ('eLMkbcddddedde_2100', ((7, 8, -1, 0, -4, 4, -8, 0),)):"a^16 - a^9 - a^8 - a^7 + 1",
        #     ('ivvPQQcfhghgfghfaaaaaaaaa_01122000', ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2),)):"a*b^2*c - 2*a*b*c - b^2*c - a^2 - 2*a*b + a",
        #     ('ivvPQQcfhghgfghfaaaaaaaaa_01122000', ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))):"a*b^2 - a^2 - 4*a*b - b^2 + a"
        # }

        alex_polys_with_cycles = {
            ("eLMkbcddddedde_2100",((7, 7, 0, 0, -4, 3, -7, 0),)): [-2, -1, -1, -1, 1, 1, 1, 2],
            ("iLLLQPcbeegefhhhhhhahahha_01110221", ((0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1),)): [-3, -1, 1, 3],
            ("ivvPQQcfhghgfghfaaaaaaaaa_01122000", ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))): [-1, -1, 1, 1],
        }

        # alex_polys_with_cycles = {
        #     ("eLMkbcddddedde_2100",((7, 7, 0, 0, -4, 3, -7, 0),)): "a^15 - a^14 + a^9 - 2*a^8 + 2*a^7 - a^6 + a - 1",
        #     ("iLLLQPcbeegefhhhhhhahahha_01110221", ((0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1),)): "3*a^3 - a^2 + a - 3",
        #     ("ivvPQQcfhghgfghfaaaaaaaaa_01122000", ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))): "a*b^2 - a^2 - b^2 + a",
        # }

    if sage_working:
        import taut_carried     
        print("testing boundary cycles")
        for sig, surface in boundary_cycles:
            surface_list = list(surface)
            cycles = taut_carried.boundary_cycles_from_surface(sig, surface_list)
            cycles = tuple(tuple(cycle) for cycle in cycles)
            assert cycles.__repr__() == boundary_cycles[(sig, surface)], sig

    if sage_working:
        print("testing taut with cycles")
        for sig, cycles in taut_polys_with_cycles:
            cycles_in = [list(cycle) for cycle in cycles]
            p = taut_polynomial.taut_polynomial_via_tree(sig, cycles_in)
            assert check_polynomial_coefficients(p, taut_polys_with_cycles[(sig, cycles)]), sig
            # assert p.__repr__() == taut_polys_with_cycles[(sig, cycles)]

    if sage_working:
        print("testing taut with images")
        for sig, cycles in taut_polys_image:
            cycles_in = [list(cycle) for cycle in cycles]
            p = taut_polynomial.taut_polynomial_image(sig, cycles_in)
            assert check_polynomial_coefficients(p, taut_polys_image[(sig, cycles)]), sig
            # assert p.__repr__() == taut_polys_image[(sig, cycles)]

    if sage_working:
        print("testing alex with cycles")
        for sig, cycles in alex_polys_with_cycles:
            cycles_in = [list(cycle) for cycle in cycles]
            p = taut_polynomial.taut_polynomial_via_tree(sig, cycles_in, mode = "alexander")
            assert check_polynomial_coefficients(p, alex_polys_with_cycles[(sig, cycles)]), sig
            # assert p.__repr__() == alex_polys_with_cycles[(sig, cycles)]

    if sage_working:
        import edge_orientability
        import taut_euler_class
        print("testing euler and edge orientability")
        for sig in random.sample(veering_isosigs[:3000], 3):
            # Theorem: If (tri, angle) is edge orientable then e = 0.
            assert not ( edge_orientability.is_edge_orientable(sig) and
                         (taut_euler_class.order_of_euler_class_wrapper(sig) == 2) ), sig

    if sage_working:
        # Theorem: If (tri, angle) is edge orientable then taut poly = alex poly.
        # taut_polynomial.taut_polynomial_via_tree(sig, mode = "alexander") ==
        #      taut_polynomial.taut_polynomial_via_tree(sig, mode = "taut")
        pass
            
    if sage_working:
        print("testing exotics")
        for sig in random.sample(veering_isosigs[:3000], 3):
            tri, angle = taut.isosig_to_tri_angle(sig)
            T = veering.veering_triangulation(tri, angle)
            is_eo = T.is_edge_orientable()
            for angle in T.exotic_angles():
                assert taut_polytope.taut_cone_homological_dim(tri, angle) == 0, sig
                assert is_eo == transverse_taut.is_transverse_taut(tri, angle), sig

    ### test for drill_midsurface_bdy: drill then fill, check you get the same manifold

    if sage_working:
        from sage.combinat.words.word_generators import words
        from sage.modules.free_module_integer import IntegerLattice
        from sage.modules.free_module import VectorSpace
        from sage.matrix.constructor import Matrix
        import z_charge
        import z2_taut
        import regina

        ZZ2 = ZZ.quotient(ZZ(2))

        sig_starts = ["b+-LR", "b++LR"]

        print("testing lattice for punc torus bundle")
        for i in range(3):
            for sig_start in sig_starts:
                sig = sig_start + str(words.RandomWord(8, 2, "LR"))  # 8 is a magic number
                M = snappy.Manifold(sig)
                tri = regina.Triangulation3(M)
                t, A = z_charge.sol_and_kernel(M)
                B = z_charge.leading_trailing_deformations(M)
                C = z2_taut.cohomology_loops(tri)

                AA = IntegerLattice(A)
                BB = IntegerLattice(B)
                assert AA == BB.saturation(), sig

                dim = 3*M.num_tetrahedra()
                V = VectorSpace(ZZ2, dim)
                AA = V.subspace(A)
                BB = V.subspace(B)
                CM = Matrix(ZZ2, C)
                CC = CM.right_kernel()
                assert AA.intersection(CC) == BB , sig
                ## so l-t defms are the part of the kernel that doesn't flip over

    if sage_working:
        print("testing charges for punc torus bundle")
        for i in range(3):
            for sig_start in sig_starts:
                sig = sig_start + str(words.RandomWord(8, 2, "LR"))  # 8 is a magic number
                M = snappy.Manifold(sig)
                assert z_charge.can_deal_with_reduced_angles(M), sig
    
    if sage_working:
        import carried_surface
        import mutation
        print("testing building carried surfaces and mutations")
        sigs_weights = [
            ['iLLLPQccdgefhhghqrqqssvof_02221000',  (0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0)], 
            ['jLLAvQQcedehihiihiinasmkutn_011220000', (2, 0, 1, 0, 0, 0, 1, 2, 0, 2, 0, 2, 1, 0, 0, 0, 1, 0)],
            ['jLLAvQQcedehihiihiinasmkutn_011220000', (0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0)],
            ['jLLLMPQcdgfhfhiiihshassspiq_122201101', (0, 0, 4, 0, 4, 1, 0, 2, 2, 0, 1, 0, 0, 4, 0, 4, 0, 0)]
        ]
        strata = [
            ((1, 2), [2, 2]), 
            ((2, 4), [5, 5, 1, 1]),
            ((0, 3), [2, 0, 0]),
            ((6, 1), [22])
        ]
        orders_of_veering_symmetry_groups = [4, 2, 2, 2]
        
        for i in range(len(sigs_weights)):
            tri, angle = taut.isosig_to_tri_angle(sigs_weights[i][0])
            weights = sigs_weights[i][1]
            surface, edge_colours = carried_surface.build_surface(tri, angle, weights, return_edge_colours = True)
            assert strata[i] == carried_surface.stratum_from_weights_surface(weights, surface)
            veering_isoms = carried_surface.veering_symmetry_group(surface, edge_colours)
            assert len(veering_isoms) == orders_of_veering_symmetry_groups[i]
            isom = veering_isoms[1]
            mutation.mutate(tri, angle, weights, isom, quiet = True)
            if i == 0:
                assert tri.isoSig() == 'ivLLQQccfhfeghghwadiwadrv'
                #print('svof to wadrv passed')
            elif i == 1:
                assert tri.isoSig() == 'jvLLAQQdfghhfgiiijttmtltrcr'
                #print('smkutn to tltrcr passed')
            elif i == 2:
                assert tri.isoSig() == 'jLLMvQQcedehhiiihiikiwnmtxk'
                #print('smkutn to mtxk passed')
            elif i == 3:
                assert tri.isoSig() == 'jLLALMQcecdhggiiihqrwqwrafo'
                #print('spiq to rafo passed')
                
                        
    if sage_working:
        print("all tests depending on sage passed")
def drill_midsurface_bdy(tri, angle):
    vt = veering_triangulation(tri, angle)
    veering_colours = vt.veering_colours  ## "red" or "blue"
    tet_types = vt.tet_types  ### "toggle", "red" or "blue"
    tet_vert_coors = vt.coorientations

    drilledTri = regina.Triangulation3()
    subtet_indices = []
    subtet_addresses = []

    num_toggles = tet_types.count("toggle")
    drilled_num_tet = 8 * num_toggles + 4 * (tri.countTetrahedra() -
                                             num_toggles)

    meridians = []
    ### in each toggle, we record the meridians for the two boundary components we are drilling
    ### note that this repeats the same meridian many times - the construction here is purely local: it is much
    ### easier to figure out which are duplicates of which later

    for i in range(tri.countTetrahedra()):
        tet = tri.tetrahedron(i)
        first_subtet_index = drilledTri.countTetrahedra()
        if tet_types[i] == 'toggle':
            num_to_add = 8
        else:
            num_to_add = 4
        for j in range(num_to_add):
            drilledTri.newTetrahedron()

        subtet_indices.append(
            range(first_subtet_index, first_subtet_index + num_to_add))

        ### glue sub-tetrahedra together. Note that some tetrahedra will be negatively oriented

        [(t0, t1), (b0, b1)] = get_top_and_bottom_nums(tet_vert_coors, i)
        this_tet_subtet_addresses = {}
        if tet_types[i] == 'toggle':
            ## first two subtetrahedra are top two, second two are bottom two, then the four side tetrahedra
            tet_t0, tet_t1 = drilledTri.tetrahedron(
                first_subtet_index), drilledTri.tetrahedron(
                    first_subtet_index + 1)
            # tet_ti is opposite vertex bi
            tet_b0, tet_b1 = drilledTri.tetrahedron(first_subtet_index +
                                                    2), drilledTri.tetrahedron(
                                                        first_subtet_index + 3)
            # tet_bi is opposite vertex ti
            tet_s00 = drilledTri.tetrahedron(first_subtet_index + 4)
            tet_s01 = drilledTri.tetrahedron(first_subtet_index + 5)
            tet_s10 = drilledTri.tetrahedron(first_subtet_index + 6)
            tet_s11 = drilledTri.tetrahedron(first_subtet_index + 7)
            # tet_sij meets vertices ti and bj

            ## keys are (vert not on face, vert not on edge), returns the subtet which meets the face, edge
            this_tet_subtet_addresses[(b0, b1)] = tet_t0
            this_tet_subtet_addresses[(b1, b0)] = tet_t1
            this_tet_subtet_addresses[(t0, t1)] = tet_b0
            this_tet_subtet_addresses[(t1, t0)] = tet_b1
            this_tet_subtet_addresses[(t1, b1)] = tet_s00
            this_tet_subtet_addresses[(b1, t1)] = tet_s00
            this_tet_subtet_addresses[(t1, b0)] = tet_s01
            this_tet_subtet_addresses[(b0, t1)] = tet_s01
            this_tet_subtet_addresses[(t0, b1)] = tet_s10
            this_tet_subtet_addresses[(b1, t0)] = tet_s10
            this_tet_subtet_addresses[(t0, b0)] = tet_s11
            this_tet_subtet_addresses[(b0, t0)] = tet_s11

            tet_t0.join(b0, tet_t1,
                        regina.Perm4(b0, b1, b1, b0, t0, t0, t1,
                                     t1))  ## b0 <-> b1, t0 and t1 fixed
            tet_b0.join(t0, tet_b1,
                        regina.Perm4(t0, t1, t1, t0, b0, b0, b1,
                                     b1))  ## t0 <-> t1, b0 and b1 fixed

            tet_s00.join(b0, tet_t1,
                         regina.Perm4(t0, t0, b1, b1, t1, b0, b0, t1))
            tet_s00.join(t0, tet_b1,
                         regina.Perm4(b0, b0, t1, t1, b1, t0, t0, b1))

            tet_s01.join(b1, tet_t0,
                         regina.Perm4(t0, t0, b0, b0, t1, b1, b1, t1))
            tet_s01.join(t0, tet_b1,
                         regina.Perm4(b1, b1, t1, t1, b0, t0, t0, b0))

            tet_s10.join(b0, tet_t1,
                         regina.Perm4(t1, t1, b1, b1, t0, b0, b0, t0))
            tet_s10.join(t1, tet_b0,
                         regina.Perm4(b0, b0, t0, t0, b1, t1, t1, b1))

            tet_s11.join(b1, tet_t0,
                         regina.Perm4(t1, t1, b0, b0, t0, b1, b1, t0))
            tet_s11.join(t1, tet_b0,
                         regina.Perm4(b1, b1, t0, t0, b0, t1, t1, b0))

            ### meridian around upper hole
            meridian = [0] * (3 * drilled_num_tet)
            meridian[3 * tet_t1.index() +
                     unsorted_vert_pair_to_edge_pair[b0, t0]] = -1
            meridian[3 * tet_s00.index() +
                     unsorted_vert_pair_to_edge_pair[b1, t1]] = 1
            meridian[3 * tet_b1.index() +
                     unsorted_vert_pair_to_edge_pair[t0, t1]] = 1
            meridian[3 * tet_s01.index() +
                     unsorted_vert_pair_to_edge_pair[b0, t1]] = 1
            meridian[3 * tet_t0.index() +
                     unsorted_vert_pair_to_edge_pair[b1, t0]] = -1
            meridians.append(meridian)

            ### meridian around lower hole - swap b with t everywhere. Note this also swaps s01 with s10
            meridian = [0] * (3 * drilled_num_tet)
            meridian[3 * tet_b1.index() +
                     unsorted_vert_pair_to_edge_pair[t0, b0]] = -1
            meridian[3 * tet_s00.index() +
                     unsorted_vert_pair_to_edge_pair[t1, b1]] = 1
            meridian[3 * tet_t1.index() +
                     unsorted_vert_pair_to_edge_pair[b0, b1]] = 1
            meridian[3 * tet_s10.index() +
                     unsorted_vert_pair_to_edge_pair[t0, b1]] = 1
            meridian[3 * tet_b0.index() +
                     unsorted_vert_pair_to_edge_pair[t1, b0]] = -1
            meridians.append(meridian)

        else:  ## fan
            # first two tetrahedra are the top and bottom
            tet_t, tet_b = drilledTri.tetrahedron(
                first_subtet_index), drilledTri.tetrahedron(
                    first_subtet_index + 1)
            # second two tetrahedra are the sides, s0 meets vertex t0, s1 meets vertex t1
            tet_s0, tet_s1 = drilledTri.tetrahedron(first_subtet_index +
                                                    2), drilledTri.tetrahedron(
                                                        first_subtet_index + 3)

            # tet_types[i] == 'blue'
            this_tet_subtet_addresses[(b0, b1)] = tet_t
            this_tet_subtet_addresses[(b1, b0)] = tet_t
            this_tet_subtet_addresses[(t0, t1)] = tet_b
            this_tet_subtet_addresses[(t1, t0)] = tet_b
            ### if this is a blue fan tet then each side tet meet a blue edge
            # tet_types[i] could be 'blue' or 'red'

            ### find which bottom vertex, when linked to t0, gives an edge of the correct colour
            if vt.get_edge_between_verts_colour(i, (t0, b0)) == tet_types[i]:
                s0b = b0  ## bottom vert of s0
                s1b = b1  ## bottom vert of s1
                this_tet_subtet_addresses[(t0, b0)] = tet_s1
                this_tet_subtet_addresses[(b0, t0)] = tet_s1
                this_tet_subtet_addresses[(t1, b1)] = tet_s0
                this_tet_subtet_addresses[(b1, t1)] = tet_s0
            else:
                s0b = b1  ## bottom vert of s0
                s1b = b0  ## bottom vert of s1
                this_tet_subtet_addresses[(t0, b1)] = tet_s1
                this_tet_subtet_addresses[(b1, t0)] = tet_s1
                this_tet_subtet_addresses[(t1, b0)] = tet_s0
                this_tet_subtet_addresses[(b0, t1)] = tet_s0

            tet_s0.join(s0b, tet_t,
                        regina.Perm4(t0, t0, s0b, t1, s1b, s1b, t1, s0b))
            tet_s0.join(t0, tet_b,
                        regina.Perm4(s0b, s0b, t0, s1b, t1, t1, s1b, t0))
            tet_s1.join(s1b, tet_t,
                        regina.Perm4(t1, t1, s1b, t0, s0b, s0b, t0, s1b))
            tet_s1.join(t1, tet_b,
                        regina.Perm4(s1b, s1b, t1, s0b, t0, t0, s0b, t1))

        subtet_addresses.append(this_tet_subtet_addresses)

    ### now glue subtetrahedra from different original tetrahedra together
    ### fan tetrahedra only glue two of three subtetrahedra on each face.
    ### toggles glue one of their subfaces all the way through to the next toggle...

    unglued_flags = []
    for f in range(tri.countTriangles()):
        for e in range(3):
            unglued_flags.append((f, e))

    while unglued_flags != []:
        (f, e) = unglued_flags.pop()
        # print f,e
        face = tri.triangle(f)
        embed0 = face.embedding(0)
        embed1 = face.embedding(1)
        tet_index_0 = embed0.simplex().index()
        tet_index_1 = embed1.simplex().index()
        face0 = embed0.face()
        face1 = embed1.face()
        vertperm0 = embed0.vertices()
        vertperm1 = embed1.vertices()

        edge0 = vertperm0[e]
        edge1 = vertperm1[e]

        otherverts0 = [0, 1, 2, 3]
        otherverts0.remove(face0)
        otherverts0.remove(edge0)
        otherverts1 = [0, 1, 2, 3]
        otherverts1.remove(face1)
        otherverts1.remove(edge1)

        if (face0, edge0) in subtet_addresses[tet_index_0]:
            subtet0 = subtet_addresses[tet_index_0][(face0, edge0)]
        else:
            subtet0 = None
        if (face1, edge1) in subtet_addresses[tet_index_1]:
            subtet1 = subtet_addresses[tet_index_1][(face1, edge1)]
        else:
            subtet1 = None

        if subtet0 == None and subtet1 == None:
            pass  ### both are fans, with no subtet, skip
        elif subtet0 != None and subtet1 != None:
            ### glue
            u, v = otherverts0
            tet0perm = regina.Perm4(face0, edge0, edge0, face0, u, u, v, v)
            u, v = otherverts1
            tet1perm = regina.Perm4(face1, edge1, edge1, face1, u, u, v, v)
            gluing = embed0.simplex().adjacentGluing(face0)
            subtet0.join(edge0, subtet1,
                         tet1perm * gluing * tet0perm)  ### perms act on left

        else:  ### we have to walk around to find the right place to glue this toggle subtet, and remove the other unglued flag from the list
            if subtet1 == None:
                assert tet_types[tet_index_0] == 'toggle'
                toggle_tet_index = tet_index_0
                toggle_face = face0
                toggle_edge = edge0
                subtet = subtet0
            else:
                assert tet_types[tet_index_1] == 'toggle'
                toggle_tet_index = tet_index_1
                toggle_face = face1
                toggle_edge = edge1
                subtet = subtet1
            edge_verts = [0, 1, 2, 3]
            edge_verts.remove(toggle_edge)
            edge_verts.remove(toggle_face)
            toggle_e0, toggle_e1 = edge_verts

            e0, e1 = toggle_e0, toggle_e1
            leading_vertex = toggle_edge
            trailing_vertex = toggle_face
            tet = tri.tetrahedron(toggle_tet_index)
            while True:
                gluing = tet.adjacentGluing(trailing_vertex)
                tet = tet.adjacentTetrahedron(trailing_vertex)
                e0, e1 = gluing[e0], gluing[e1]
                leading_vertex, trailing_vertex = gluing[
                    trailing_vertex], gluing[leading_vertex]
                if tet_types[tet.index()] == "toggle":
                    break
            other_toggle_tet_index = tet.index()
            other_toggle_face = leading_vertex
            other_toggle_edge = trailing_vertex
            other_toggle_e0 = e0
            other_toggle_e1 = e1
            other_subtet = subtet_addresses[other_toggle_tet_index][(
                other_toggle_face, other_toggle_edge)]
            tetperm = regina.Perm4(toggle_face, toggle_edge, toggle_edge,
                                   toggle_face, toggle_e0, toggle_e0,
                                   toggle_e1, toggle_e1)
            other_tetperm = regina.Perm4(other_toggle_face, other_toggle_edge,
                                         other_toggle_edge, other_toggle_face,
                                         other_toggle_e0, other_toggle_e0,
                                         other_toggle_e1, other_toggle_e1)
            gluing = regina.Perm4(toggle_e0, other_toggle_e0, toggle_e1,
                                  other_toggle_e1, toggle_face,
                                  other_toggle_face, toggle_edge,
                                  other_toggle_edge)
            subtet.join(toggle_edge, other_subtet,
                        other_tetperm * gluing * tetperm)

    return drilledTri, meridians