

/* IMPLEMENTATIONS */
  
stmesh_t stmesh_read_STL(char *fileName, bool_t binary, float eps, uint32_t nfGuess, bool_t even, bool_t checkSorted)
  {
    char *format = ((char *[2]){ "ascii", "binary" })[binary];
    fprintf(stderr, "reading mesh from file %s (%s)\n", fileName, format);
    fprintf(stderr, "expecting about %u triangles\n", nfGuess);
    fprintf(stderr, "quantizing vertex coords to%s multiples of %.8f mm\n", (even ? " even" : ""), eps);
    if (checkSorted) { fprintf(stderr, "expecting triangles sorted by {.minZ}\n"); }

    /* While reading the STL file and building the structure, the
      entries must be linked by *indices* of elements, rather
      than pointers, because the hash tables may be reallocated. Only
      after the reading is finished can we translate the indices into
      pointers.
      
      During the reading, a vertex is represented by its
      integer position (as an {i3.t}); an (unoriented) edge is represented
      by indices of the two endpoints, in increasing order (as an
      {stmesh_vert_unx_pair_t}); and a face is represented by the indices of the three
      (unoriented) edges that are its sides (as an {stmesh_edge_unx_triple_t}),
      also in increasing order.
      
      The fields {uf.minZ} and {uf.maxZ}, that are not needed during reading,
      are set later, when these pairs and triples of integers are converted to
      records of types {stmesh_vert_rep_t}, {stmesh_edge_rep_t}, and
      {stmesh_face_rep_t}. */

    /* Lookup table to uniquify faces: */
    size_t sz_face = sizeof(stmesh_edge_unx_triple_t);
    uint32_t ng_face = nfGuess;  /* Expected number of faces. */
    bvtable_t *tb_face = bvtable_new(sz_face, ng_face);
    auto uint64_t hash_face(void *fp, size_t sz);
    auto int cmp_face(void *xp, void *yp, size_t sz);
    
    /* Lookup table to uniquify edges: */
    size_t sz_edge = sizeof(stmesh_vert_unx_pair_t);
    uint32_t ng_edge = (3 * ng_face + 2)/2;
    bvtable_t *tb_edge = bvtable_new(sz_edge, ng_edge);
    auto uint64_t hash_edge(void *ep, size_t sz);
    auto int cmp_edge(void *xp, void *yp, size_t sz);

    /* Lookup table to uniquify vertices: */
    size_t sz_vert = sizeof(i3_t);
    uint32_t ng_vert = (ng_face + 1)/2;
    bvtable_t *tb_vert = bvtable_new(sz_vert, ng_vert);
    auto uint64_t hash_vert(void *vp, size_t sz);
    auto int cmp_vert(void *xp, void *yp, size_t sz);
    
    auto void process_STL_face(int line, stmesh_STL_face_t *face);
      /* Procedure that quantizes an STL face {face} and stores it in
         the mesh, if not degenerate. */

    uint32_t nf_read = 0;  /* Number of faces read from the STL file. */
    uint32_t nf_keep = 0;  /* Number of faces retained in the mesh. */
    
    int32_t prevZ = INT32_MIN; /* If {checkSorted}, the {minZ} of the next face must be at least this big. */

    stmesh_STL_read(fileName, binary, &process_STL_face);
    
    fprintf(stderr, "read %u triangles, kept %u\n", nf_read, nf_keep);

    /* Close the tables and get the basic data: */
    uint32_t nv, ne, nf; /* Number of vertices, edges, and faces. */
    i3_t *vpos;                     /* Quantized coordinates of vertices. */
    stmesh_vert_unx_pair_t *endv;   /* Edges, as pairs of vertex indices. */
    stmesh_edge_unx_triple_t *side; /* Faces, as triplets of unoriented edge indices. */
    
    bvtable_close(tb_vert, &nv, (void**)&(vpos)); 
    bvtable_close(tb_edge, &ne, (void**)&(endv)); 
    bvtable_close(tb_face, &nf, (void**)&(side)); 
    
    assert(nf == nf_keep);
    fprintf(stderr, "found %u distinct vertices and %u distinct edges\n", nv, ne);

    /* Build the mesh data structure. */
    stmesh_t mesh = stmesh_build(eps, nv, vpos, ne, endv, nf, side, checkSorted);
    
    return mesh;
    
    /* INTERNAL IMPLEMENTATIONS */
    
    void process_STL_face(int line, stmesh_STL_face_t *stl_face)
      { 
        nf_read++;
        
        /* Quantize vertices, assign indices: */
        stmesh_edge_unx_triple_t uxv; /* Indices of corner vertices, assigned or recovered. */
        int32_t minZ = INT32_MAX; /* Minimum {Z}-coordinate, to check order. */
        int k;
        for (k = 0; k < 3; k++)
          { /* Quantize the coordinates of corner {k}: */
            i3_t vposk = stmesh_STL_round_point(&(stl_face->v[k]), eps, even);
            /* Update the min {Z} coordinate of the triangle: */
            int32_t zk = vposk.c[2];
            if (zk < minZ) { minZ = zk; }
            /* Get the unique vertex index: */
            uxv.c[k] = bvtable_add(tb_vert, (void*)(&vposk), &hash_vert, &cmp_vert);
            demand(uxv.c[k] <= stmesh_nv_MAX, "too many vertices in mesh");
            /* Check for repeated vertices: */
            int i;
            for (i = 0; i < k; i++)
              { if (uxv.c[i] == uxv.c[k])
                  { fprintf(stderr, "%s:%d: !! warning: vertices %d %d coincide, triangle ignored\n", fileName, line, i, k);
                    stmesh_STL_print_triangle(stderr, stl_face);
                    fprintf(stderr, "\n");
                    return;
                  }
              }
          }

        if (checkSorted)
          { /* Check that the {.minZ} fields are non-decreasing: */
            if (minZ < prevZ)
              { fprintf(stderr, "%s:%d: ** error: triangles not sorted by {minZ}\n", fileName, line);
                exit(1);
              }
            prevZ = minZ;
          }
        
        /* Get or assign indices to edges: */
        stmesh_edge_unx_triple_t uxside; /* Indices of the three unoriented edges that bound the face. */
        for (k = 0; k < 3; k++)
          { stmesh_vert_unx_pair_t uxendvk;       /* Temporary record for edge {k}. */
            stmesh_vert_unx_t uxv0 = uxv.c[k];       /* One endpoint. */
            stmesh_vert_unx_t uxv1 = uxv.c[(k+1)%3]; /* The other endpoint. */
            assert(uxv0 != uxv1);         /* We should have discarded degenerate triangles before. */
            /* Make sure that {uxv0} is the vertex with lowest index: */
            if (uxv0 > uxv1) { stmesh_vert_unx_t t = uxv0; uxv0 = uxv1; uxv1 = t; }
            uxendvk.c[0] = uxv0;
            uxendvk.c[1] = uxv1;
            uxside.c[k] = bvtable_add(tb_edge, (void*)(&uxendvk), &hash_edge, &cmp_edge);
            demand(uxside.c[k] <= stmesh_ne_MAX, "too many edges in mesh");
          }
          
        /* Sort the edge indices in increasing order: */
        if (uxside.c[0] > uxside.c[1]) { stmesh_edge_unx_t t = uxside.c[0]; uxside.c[0] = uxside.c[1]; uxside.c[1] = t; }
        if (uxside.c[1] > uxside.c[2]) { stmesh_edge_unx_t t = uxside.c[1]; uxside.c[1] = uxside.c[2]; uxside.c[2] = t; }
        if (uxside.c[0] > uxside.c[1]) { stmesh_edge_unx_t t = uxside.c[0]; uxside.c[0] = uxside.c[1]; uxside.c[1] = t; }
        
        /* Since the three vertices are distinct, the three sides must be distinct edges too. */

        /* Assign an index to the face: */
        stmesh_face_unx_t uxf = bvtable_add(tb_face, (void *)(&uxside), &hash_face, &cmp_face);
        if (uxf < nf_keep) 
          { /* Repeated face: */
            fprintf(stderr, "%s:%d: !! repeated triangle, ignored\n", fileName, line);
          }
        else
          { nf_keep++; }
      }

    /* Hashing and comparison procedures for faces, edges, and vertices: */
    
    uint64_t hash_face(void *fp, size_t sz)
      { assert(sz == sizeof(stmesh_edge_unx_triple_t));
        stmesh_edge_unx_triple_t *f = (stmesh_edge_unx_triple_t *)fp;
        /* Requires the edge indices {f.c[0..2]} to be increasing: */
        assert((f->c[0] < f->c[1]) && (f->c[1] < f->c[2]));
        /* Hash side indices: */
        uint64_t h = bvhash_bytes(fp, sizeof(stmesh_edge_unx_triple_t));
        return h;
      }
        
    auto int cmp_face(void *xp, void *yp, size_t sz)
      { assert(sz == sizeof(stmesh_edge_unx_triple_t));
        stmesh_edge_unx_triple_t *x = (stmesh_edge_unx_triple_t *)xp;
        stmesh_edge_unx_triple_t *y = (stmesh_edge_unx_triple_t *)yp;
        /* Compare the edge indices lexicographically: */
        int k;
        for (k = 0; k < 3; k++)
          { stmesh_edge_unx_t uxex = x->c[k];
            stmesh_edge_unx_t uxey = y->c[k];
            if (uxex < uxey)
              { return -1; }
            else if (uxex > uxey)
              { return +1; }
          }
        return 0;
      }
        
    uint64_t hash_edge(void *ep, size_t sz)
      { assert(sz == sizeof(stmesh_vert_unx_pair_t));
        stmesh_vert_unx_pair_t *e = (stmesh_vert_unx_pair_t *)ep;
        /* Requires endpoint indices in increasing order: */
        assert(e->c[0] < e->c[1]);
        /* Hash the endpoint indices: */
        uint64_t h = bvhash_bytes(ep, sizeof(stmesh_vert_unx_pair_t));
        return h;
      }
        
    auto int cmp_edge(void *xp, void *yp, size_t sz)
      { assert(sz == sizeof(stmesh_vert_unx_pair_t));
        stmesh_vert_unx_pair_t *x = (stmesh_vert_unx_pair_t *)xp;
        stmesh_vert_unx_pair_t *y = (stmesh_vert_unx_pair_t *)yp;
        /* Compare the endpoint indices lexicographically: */
        int k;
        for (k = 0; k < 2; k++)
          { stmesh_vert_unx_t uxvx = x->c[k];
            stmesh_vert_unx_t uxvy = y->c[k];
            if (uxvx < uxvy)
              { return -1; }
            else if (uxvx > uxvy)
              { return +1; }
          }
        return 0;
      }
        
    uint64_t hash_vert(void *vp, size_t sz)
      { assert(sz == sizeof(i3_t));
        /* Hash the quantized coords: */
        uint64_t h = bvhash_bytes(vp, sizeof(i3_t));
        return h;
      }
        
    auto int cmp_vert(void *xp, void *yp, size_t sz)
      { assert(sz == sizeof(i3_t));
        i3_t *x = (i3_t *)xp;
        i3_t *y = (i3_t *)yp;
        /* Compare quantized coords lexicographically in order {Z,Y,X}: */
        int k;
        for (k = 0; k < 3; k++)
          { int32_t xk = x->c[2-k];
            int32_t yk = y->c[2-k];
            if (xk < yk)
              { return -1; }
            else if (xk > yk)
              { return +1; }
          }
        return 0;
      }
  }
  
