private static void SwapBorderEdgeToFront(BigFaceRef[] efra, long[] fera, long vi)
        {
            if (fera[vi] == 0)
            {
                return;
            }
            long firstEdgeRef   = BigEdgeRef.Create(vi, 0);
            long otherVertexRef = efra[firstEdgeRef].Index;

            if (otherVertexRef < 0)
            {
                return;
            }
            if (TopologyVertexSideOf(otherVertexRef) != 1)
            {
                return;
            }
            long edgeRef = efra[firstEdgeRef | 1].Index;

            while (edgeRef >= 0)
            {
                otherVertexRef = efra[edgeRef].Index;
                if (TopologyVertexSideOf(otherVertexRef) != 1)
                {
                    Fun.Swap(ref efra[firstEdgeRef].Index, ref efra[edgeRef].Index);
                    return;
                }
                edgeRef = efra[edgeRef | 1].Index;
            }
        }
 /// <summary>
 /// Check if the edge is in the edge list of vertex v0.
 /// </summary>
 private static long FindEdgeInList(
     BigFaceRef[] edgeFaceRefArray, long v0, long v1)
 {
     #if DEBUG
     long count = 0;
     #endif
     long otherVertexRef;
     long edgeRef = BigEdgeRef.Create(v0, 0);
     do
     {
         otherVertexRef = edgeFaceRefArray[edgeRef].Index;
         if (TopologyVertexIndexOf(otherVertexRef) == v1)  // found edge!
         {
             // check if its a nonmanifold edge, use it if unused,
             // search first unused, use it and mark it
             if (TopologyVertexSideOf(otherVertexRef) < 3)
             {
                 if (TopologyVertexSideOf(otherVertexRef) == 2)
                 {
                     edgeFaceRefArray[edgeRef].Index
                         = TopologyVertexRef(v1, 3);
                 }
                 return(edgeRef);
             }
         }
         edgeRef = edgeFaceRefArray[edgeRef | 1].Index;
         #if DEBUG
         ++count;
         #endif
     }while (edgeRef >= 0);
     return(-1);
 }
        private void BuildTopology(long eulerCharacteristic, bool strict)
        {
            Report.BeginTimed(12, "building topology for {0} {1}",
                              m_faceCount,
                              "face".Plural(m_faceCount));

            // use Eulers Formula to compute number of edges, but there
            // must be at least one edge per vertex, otherwise the mesh
            // cannot be topologically correct
            long edgeCount
                = Fun.Max(m_vertexCount,
                          m_vertexCount + m_faceCount - eulerCharacteristic);
            long nonManifoldEdgeCount = 0;

            BigFaceRef[] edgeFaceRefArray
                = new BigFaceRef[2 * edgeCount].Set(new BigFaceRef(long.MinValue, int.MinValue));

            // make this array large enough to hold one counter (for the
            // borderedges) per vertex, even if there are unused vertices
            long[] faceEdgeRefArray
                = new long[Fun.Max(m_faceVertexCount,
                                   m_vertexCount)].Set(0);
            long freeEdgeRef = BigEdgeRef.Create(m_vertexCount, 0);

            long[] fvia = m_faceVertexIndexArray;

            // create list of edges for each vertex stored in edgeFaceRefArray
            for (long fi = 0; fi < m_faceCount; fi++)
            {
                long ffi = m_firstIndexArray[fi];
                long fvc = m_firstIndexArray[fi + 1] - ffi;
                for (long fs0 = fvc - 1, fs1 = 0; fs1 < fvc; fs0 = fs1, fs1++)
                {
                    nonManifoldEdgeCount +=
                        AddEdge(ref edgeFaceRefArray, ref freeEdgeRef,
                                faceEdgeRefArray,
                                fvia[ffi + fs0], fvia[ffi + fs1], strict, true);
                }
            }

            long borderEdgeCount = 0;

            for (long vi = 0; vi < m_vertexCount; vi++)
            {
                borderEdgeCount += faceEdgeRefArray[vi];
            }

            if (borderEdgeCount > 0)
            {
                for (long vi = 0; vi < m_vertexCount; vi++)
                {
                    SwapBorderEdgeToFront(edgeFaceRefArray,
                                          faceEdgeRefArray, vi);
                }
            }

            // fill faceEdgeRefArray by searching edge in both vertex lists

            for (long fi = 0; fi < m_faceCount; fi++)
            {
                long ffi = m_firstIndexArray[fi];
                long fvc = m_firstIndexArray[fi + 1] - ffi;
                for (long fs0 = fvc - 1, fs1 = 0; fs1 < fvc; fs0 = fs1, fs1++)
                {
                    long v0 = fvia[ffi + fs0], v1 = fvia[ffi + fs1];
                    long edgeRef = FindEdgeInList(edgeFaceRefArray, v0, v1);
                    if (edgeRef < 0)
                    {
                        edgeRef = BigEdgeRef.Reversed(
                            FindEdgeInList(edgeFaceRefArray, v1, v0));
                    }
                    faceEdgeRefArray[ffi + fs0] = edgeRef;
                }
            }

            if (nonManifoldEdgeCount > 0)
            {
                for (long fi = 0; fi < m_faceCount; fi++)
                {
                    long ffi = m_firstIndexArray[fi];
                    long fvc = m_firstIndexArray[fi + 1] - ffi;
                    for (long fs0 = fvc - 1, fs1 = 0; fs1 < fvc; fs0 = fs1, fs1++)
                    {
                        ConnectNonManifoldEdges(edgeFaceRefArray,
                                                fvia[ffi + fs0], fvia[ffi + fs1]);
                    }
                }
                for (long edgeRef = 0; edgeRef < freeEdgeRef; edgeRef += 2)
                {
                    if (edgeFaceRefArray[edgeRef].Index < 0)
                    {
                        edgeFaceRefArray[edgeRef | 1].Index =
                            edgeFaceRefArray[edgeRef].Index;
                    }
                    else
                    {
                        edgeFaceRefArray[edgeRef | 1].Index = long.MinValue;
                    }
                    edgeFaceRefArray[edgeRef].Index = long.MinValue;
                    edgeFaceRefArray[edgeRef].Side  = int.MinValue;
                }
            }
            else
            {
                edgeFaceRefArray.Set(new BigFaceRef(long.MinValue, int.MinValue));
            }

            // go through faces, store backpointers from edges to faces

            for (long fi = 0; fi < m_faceCount; fi++)
            {
                long ffi = m_firstIndexArray[fi];
                long fvc = m_firstIndexArray[fi + 1] - ffi;
                for (int fs = 0; fs < fvc; fs++)
                {
                    long edgeRef = faceEdgeRefArray[ffi + fs];
                    edgeFaceRefArray[edgeRef].Index = fi;
                    edgeFaceRefArray[edgeRef].Side  = fs;
                }
            }

            EdgeCount            = BigEdgeRef.Index(freeEdgeRef);
            BorderEdgeCount      = borderEdgeCount;
            NonManifoldEdgeCount = nonManifoldEdgeCount;
            FaceEdgeRefArray     = faceEdgeRefArray;
            EdgeFaceRefArray     = edgeFaceRefArray;
            EulerCharacteristic  = m_vertexCount + m_faceCount - EdgeCount;

            if (nonManifoldEdgeCount > 0)
            {
                Report.End(12, ": non-manifold");
            }
            else
            {
                Report.End(12, ": euler characteristic {0}", EulerCharacteristic);
            }
        }
        /// <summary>
        /// Create rings of non-manifold edges. The references are stored
        /// in faceRef 0 of each edge, and need to be moved to the
        /// other faceRef before they can be used.
        /// </summary>
        private static void ConnectNonManifoldEdges(BigFaceRef[] efra, long v0, long v1)
        {
            long pEdgeRef = -1, pSide = -1;

            long v0FirstEdgeRef = BigEdgeRef.Create(v0, 0), v0count = 0;

            if (efra[v0FirstEdgeRef].Side == 0)
            {
                int  hasNonManifolds = int.MinValue;
                long v0edgeRef       = v0FirstEdgeRef;
                while (v0edgeRef >= 0)
                {
                    long v1VertexRef = efra[v0edgeRef].Index;
                    if (TopologyVertexSideOf(v1VertexRef) > 1)
                    {
                        if (TopologyVertexIndexOf(v1VertexRef) == v1)
                        {
                            pEdgeRef = v0edgeRef; pSide = 0; ++v0count;
                        }
                        hasNonManifolds = 0;
                    }
                    v0edgeRef = efra[v0edgeRef | 1].Index;
                }
                efra[v0FirstEdgeRef].Side = hasNonManifolds;
            }

            long v1FirstEdgeRef = BigEdgeRef.Create(v1, 0), v1count = 0;

            if (efra[v1FirstEdgeRef].Side == 0)
            {
                int  hasNonManifolds = int.MinValue;
                long v1edgeRef       = v1FirstEdgeRef;
                while (v1edgeRef >= 0)
                {
                    long v0VertexRef = efra[v1edgeRef].Index;
                    if (TopologyVertexSideOf(v0VertexRef) > 1)
                    {
                        if (TopologyVertexIndexOf(v0VertexRef) == v0)
                        {
                            pEdgeRef = v1edgeRef; pSide = 1; ++v1count;
                        }
                        hasNonManifolds = 0;
                    }
                    v1edgeRef = efra[v1edgeRef | 1].Index;
                }
                efra[v1FirstEdgeRef].Side = hasNonManifolds;
            }

            #if DEBUG
            long nonManifoldCount = v0count + v1count;
            if (nonManifoldCount == 1)
            {
                Report.Warn("single non-manifold edge");
            }
            #endif

            if (v0count > 0)
            {
                long v0edgeRef = v0FirstEdgeRef;
                long thisRef   = -1;
                while (v0count > 0)
                {
                    long v1VertexRef = efra[v0edgeRef].Index;
                    long nextRef     = v0edgeRef | 1;
                    if (TopologyVertexIndexOf(v1VertexRef) == v1 &&
                        TopologyVertexSideOf(v1VertexRef) > 1)
                    {
                        efra[v0edgeRef].Index = -pEdgeRef - (pSide == 0 ? 2 : 1);
                        pEdgeRef = v0edgeRef; pSide = 0; --v0count;
                        if (thisRef >= 0)
                        {
                            efra[thisRef].Index = efra[nextRef].Index;
                        }
                    }
                    v0edgeRef = efra[nextRef].Index;
                    thisRef   = nextRef;
                }
            }
            if (v1count > 0)
            {
                long v1edgeRef = v1FirstEdgeRef;
                long thisRef   = -1;
                while (v1count > 0)
                {
                    long v0VertexRef = efra[v1edgeRef].Index;
                    long nextRef     = v1edgeRef | 1;
                    if (TopologyVertexIndexOf(v0VertexRef) == v0 &&
                        TopologyVertexSideOf(v0VertexRef) > 1)
                    {
                        efra[v1edgeRef].Index = -pEdgeRef - (pSide == 1 ? 2 : 1);
                        pEdgeRef = v1edgeRef; pSide = 1; --v1count;
                        if (thisRef >= 0)
                        {
                            efra[thisRef].Index = efra[nextRef].Index;
                        }
                    }
                    v1edgeRef = efra[nextRef].Index;
                    thisRef   = nextRef;
                }
            }
        }
        /// <summary>
        /// The edgeFaceRefArray is misused to contain a list of edges for
        /// each vertex. For each vertex v, the list starts at the index v. As
        /// the edgeFaceRefArray can hold two values at each index, the
        /// first value (side 0) is used as the vertex index of the other
        /// vertex of the edge, and the second value (side 1) is used as a
        /// pointer to the next edge in the list. Each edge should be member
        /// of exactly one of the two edge lists of its vertices. When
        /// building up this data structure, it is necessary to look into both
        /// vertex lists if an edge has already been inserted.
        /// By adding external edges at the front of the vertex v0 list, and
        /// internal edges at the back, it is very likely, that border edges
        /// are the first edges in the list.
        /// Additionally we set the side filed of the side 0 references to
        /// indicate if a list contains (-1) or does not contain (0)
        /// non-manifold references.
        /// </summary>
        private static long AddEdge(
            ref BigFaceRef[] efra, ref long freeEdgeRef, long[] fera,
            long v0, long v1, bool strict, bool isExternalEdge)
        {
            long result         = -1;
            long v1FirstEdgeRef = BigEdgeRef.Create(v1, 0);
            // check if the edge is in the edge list of vertex v1
            long edgeRef            = v1FirstEdgeRef;
            long nonManifoldEdgeRef = -1;

            do
            {
                long otherVertexRef = efra[edgeRef].Index;
                if (TopologyVertexIndexOf(otherVertexRef) == v0)  // found
                {
                    if (TopologyVertexSideOf(otherVertexRef) > 0) // in use
                    {
                        if (strict)
                        {
                            Report.End(3, " FAILED!");
                            throw new Exception("Edge already in use!");
                        }
                        else
                        {
                            nonManifoldEdgeRef = edgeRef;
                            break;
                        }
                    }
                    else // fill other side of edge
                    {
                        fera[v1]           -= 1;
                        efra[edgeRef].Index = TopologyVertexRef(v0, 1);
                        result = edgeRef | 1;
                        break;
                    }
                }
                edgeRef = efra[edgeRef | 1].Index;
            }while (edgeRef >= 0);

            // check the edge list of vertex v0, if the edge is there

            long v0FirstEdgeRef = BigEdgeRef.Create(v0, 0);
            long v0LastEdgeRef  = -1;

            if (nonManifoldEdgeRef < 0)
            {
                edgeRef = v0FirstEdgeRef;
                do
                {
                    long otherVertexRef = efra[edgeRef].Index;
                    if (TopologyVertexIndexOf(otherVertexRef) == v1)
                    {
                        if (strict)
                        {
                            Report.End(3, " FAILED!");
                            throw new Exception("Edge already in use!");
                        }
                        else
                        {
                            nonManifoldEdgeRef = edgeRef;
                            break;
                        }
                    }
                    v0LastEdgeRef = edgeRef;
                    edgeRef       = efra[edgeRef | 1].Index;
                }while (edgeRef >= 0);
            }

            if (nonManifoldEdgeRef >= 0)
            {
                long nonManifoldEdgeCount = 1;
                long otherVertexRef       = efra[nonManifoldEdgeRef].Index;
                if (TopologyVertexSideOf(otherVertexRef) == 1)
                {
                    // swap edge to the front, split it,  and add 3rd edge
                    fera[v0]             += 1; fera[v1] += 1;
                    nonManifoldEdgeCount += 2;
                    if (TopologyVertexIndexOf(otherVertexRef) == v0)
                    {
                        if (nonManifoldEdgeRef != v1FirstEdgeRef)
                        {
                            efra[nonManifoldEdgeRef].Index = efra[v1FirstEdgeRef].Index;
                        }
                        efra[v1FirstEdgeRef].Index = TopologyVertexRef(v0, 2);
                        InsertEdge(ref efra, ref freeEdgeRef, v0FirstEdgeRef,
                                   TopologyVertexRef(v1, 2));
                    }
                    else
                    {
                        if (nonManifoldEdgeRef != v0FirstEdgeRef)
                        {
                            efra[nonManifoldEdgeRef].Index = efra[v0FirstEdgeRef].Index;
                        }
                        efra[v0FirstEdgeRef].Index = TopologyVertexRef(v1, 2);
                        InsertEdge(ref efra, ref freeEdgeRef, v1FirstEdgeRef,
                                   TopologyVertexRef(v0, 2));
                    }
                    efra[v1FirstEdgeRef].Side = 0;
                }
                else if (TopologyVertexSideOf(otherVertexRef) == 0)
                {
                    nonManifoldEdgeCount          += 1;
                    efra[nonManifoldEdgeRef].Index = TopologyVertexRef(v1, 2);
                }

                fera[v0] += 1;
                InsertEdge(ref efra, ref freeEdgeRef, v0FirstEdgeRef,
                           TopologyVertexRef(v1, 2));
                efra[v0FirstEdgeRef].Side = 0;
                return(nonManifoldEdgeCount);
            }

            if (result < 0) // we have to create a new (normal) edge
            {
                if (isExternalEdge)
                {
                    InsertEdge(ref efra, ref freeEdgeRef, v0FirstEdgeRef,
                               TopologyVertexRef(v1, 0));
                }
                else
                {
                    AppendEdge(ref efra, ref freeEdgeRef, v0FirstEdgeRef,
                               v0LastEdgeRef, TopologyVertexRef(v1, 0));
                }
                fera[v0] += 1;
            }
            return(0);
        }