/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//				
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/

#include "opennurbs.h"

#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined 
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif

bool
ON_Brep::IsValidVertexTopology( int vertex_index, ON_TextLog* text_log ) const
{
  if ( vertex_index < 0 || vertex_index >= m_V.Count() )
  {
    if ( text_log )
      text_log->Print("brep vertex_index = %d (should be >=0 and <%d=brep.m_V.Count() ).\n",
                      vertex_index, m_V.Count());
    return false;
  }
  const ON_BrepVertex& vertex = m_V[vertex_index];
  if ( vertex.m_vertex_index != vertex_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
      text_log->PushIndent();
      text_log->Print("vertex.m_vertex_index = %d (should be %d).\n",
                       vertex.m_vertex_index, vertex_index );
      text_log->PopIndent();
    }
    return false;
  }

  const int vertex_edge_count = vertex.m_ei.Count();
  int i, j, vei, ei;
  for ( vei = 0; vei < vertex_edge_count; vei++ ) 
  {
    ei = vertex.m_ei[vei];

    if ( ei < 0 || ei >= m_E.Count() )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
        text_log->PushIndent();
        text_log->Print("vertex.m_ei[%d] = %d (should be >=0 and <%d).\n", vei, ei, m_E.Count());
        text_log->PopIndent();
      }
      return false;
    }

    const ON_BrepEdge& edge = m_E[ei];

    if ( ei != edge.m_edge_index )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
        text_log->PushIndent();
        text_log->Print("vertex.m_ei[%d] = %d is a deleted edge.\n", vei, ei);
        text_log->PopIndent();
      }
      return false;
    }

    if ( edge.m_vi[0] != vertex_index && edge.m_vi[1] != vertex_index )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_V[%d] vertex or brep.m_E[%d] edge is not valid.\n",vertex_index,ei);
        text_log->PushIndent();
        text_log->Print("vertex.m_ei[%d] = %d but brep.m_E[%d].m_vi[] = [%d,%d]. "
                        "At least one edge m_vi[] value should be %d.\n",
                        vei,ei,ei,edge.m_vi[0],edge.m_vi[1],vertex_index);
        text_log->PopIndent();
      }
      return false;
    }

    for ( i = 0; i < vei; i++ ) 
    {
      if ( vertex.m_ei[i] == ei ) 
      {
        // edge should be closed
        if ( edge.m_vi[0] != vertex_index || edge.m_vi[1] != vertex_index )
        {
          if ( text_log )
          {
            text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
            text_log->PushIndent();
            text_log->Print("vertex.m_ei[%d] and vertex.m_ei[%d] = %d but brep.m_E[%d].m_vi[0] = %d",
                             i,vei,ei,ei,edge.m_vi[0]);
            text_log->Print("and ON_Brep.m_E[%d].m_vi[1] = %d (both m_vi[] values should be %d).\n",
                            ei,edge.m_vi[1],vertex_index);
            text_log->PopIndent();
          }
          return false;
        }
        for (j = i+1; j < vei; j++ ) 
        {
          if ( vertex.m_ei[j] == ei )
          {
            if ( text_log )
            {
              text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
              text_log->PushIndent();
              text_log->Print("vertex.m_ei[%d,%d,%d] = %d. An open edge index should appear once\n",i,vei,j,ei);
              text_log->Print("in vertex.m_ei[] and a closed edge index should appear twice.\n");
              text_log->PopIndent();
            }
            return false;
          }
        }
        break;
      }
    }

  }

  return true;
}

bool
ON_Brep::IsValidFaceTopology( int face_index, ON_TextLog* text_log  ) const
{
  if ( face_index < 0 || face_index >= m_F.Count() )
  {
    if ( text_log )
    {
      text_log->Print("brep face_index = %d (should be >=0 and <%d=brep.m_F.Count()).\n",
                      face_index, m_F.Count());
    }
    return false;
  }
  const ON_BrepFace& face = m_F[face_index];
  if ( face.m_face_index != face_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("face.m_face_index = %d (should be %d).\n",
                       face.m_face_index, face_index );
      text_log->PopIndent();
    }
    return false;
  }
  if ( face.m_brep != this )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("face.m_brep does not point to parent brep.\n");
      text_log->PopIndent();
    }
    return false;
  }

  const int face_loop_count = face.m_li.Count();
  if ( face_loop_count <= 0 )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("face.m_li.Count() <= 0 (should be >= 1)\n");
      text_log->PopIndent();
    }
    return false;
  }

  int i, fli, li;
  for ( fli = 0; fli < face_loop_count; fli++ ) 
  {
    li = face.m_li[fli];
    for ( i = 0; i < fli; i++ ) 
    {
      if ( face.m_li[i] == li )
      {
        if ( text_log )
        {
          text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
          text_log->PushIndent();
          text_log->Print("face.m_li[%d]=face.m_li[%d]=%d (a loop index should appear once in face.m_li[])\n",
                          fli,i,li);
          text_log->PopIndent();
        }
        return false;
      }
    }
    if ( !IsValidLoop( li, text_log ) )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
        text_log->PushIndent();
        text_log->Print("brep.m_L[face.m_li[%d]=%d] is not valid.\n",fli,li);
        text_log->PopIndent();
      }
      return false;
    }
    const ON_BrepLoop& loop = m_L[li];
    if ( loop.m_loop_index != li )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
        text_log->PushIndent();
        text_log->Print("face.m_li[%d]=%d is a deleted loop\n",
                        fli,li);
        text_log->PopIndent();
      }
      return false;
    }
    if ( loop.m_fi != face_index )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
        text_log->PushIndent();
        text_log->Print("face.m_li[%d]=%d but brep.m_L[%d].m_fi=%d (m_fi should be %d)\n",
                        fli,li,li,loop.m_fi,face_index);
        text_log->PopIndent();
      }
      return false;
    }
    if ( fli == 0 ) {
      if ( loop.m_type != ON_BrepLoop::outer )
      {
        if ( text_log )
        {
          text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
          text_log->PushIndent();
          text_log->Print("brep.m_L[face.m_li[0]=%d].m_type is not outer.\n",li);
          text_log->PopIndent();
        }
        return false;
      }
    }
    else {
      if ( loop.m_type != ON_BrepLoop::inner )
      {
        if ( text_log )
        {
          text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
          text_log->PushIndent();
          text_log->Print("brep.m_L[face.m_li[%d]=%d].m_type is not inner.\n",fli,li);
          text_log->PopIndent();
        }
        return false;
      }
    }
  }

  const int si = face.m_si;
  if ( si < 0 || si >= m_S.Count() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("face.m_si=%d (should be >=0 and <%d=m_S.Count())\n",
                      face.m_si,m_S.Count());                      
      text_log->PopIndent();
    }
    return false;
  }

  if ( 0 == m_S[si] )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("brep.m_S[face.m_si=%d] is nullptr\n",face.m_si);
      text_log->PopIndent();
    }
    return false;
  }

  if ( 0 == face.ProxySurface() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("face.ProxySurface() is nullptr\n");
      text_log->PopIndent();
    }
    return false;
  }

  if ( m_S[si] != face.ProxySurface() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("brep.m_S[face.m_si=%d] != face.ProxySurface().\n",si);
      text_log->PopIndent();
    }
    return false;
  }

  if ( face.ProxySurfaceIsTransposed() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("face.ProxySurfaceIsTransposed() is true.\n");
      text_log->PopIndent();
    }
    return false;
  }

  return true; 
}

bool
ON_Brep::IsValidEdgeTopology( int edge_index, ON_TextLog* text_log ) const
{
  if ( edge_index < 0 || edge_index >= m_E.Count() )
  {
    if ( text_log )
      text_log->Print("brep edge_index = %d (should be >=0 and <%d=brep.m_E.Count() ).\n",
                      edge_index, m_E.Count());
    return false;
  }
  const ON_BrepEdge& edge = m_E[edge_index];
  if ( edge.m_edge_index != edge_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_edge_index = %d (should be %d).\n",
                       edge.m_edge_index, edge_index );
      text_log->PopIndent();
    }
    return false;
  }
  if ( edge.m_brep != this )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_brep does not point to parent brep\n");
      text_log->PopIndent();
    }
    return false;
  }

  if ( !edge.IsValid(text_log) )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge is not a valid.\n");
      text_log->PopIndent();
    }
    return false;
  }

  // This checks to make sure that m_C3[] and the edge's proxy information is valid.
  // This isn't exactly "topology", but if this stuff isn't set right, then the
  // "geometry" checks might crash.

  const int c3i = edge.m_c3i;
  if ( c3i < 0 || c3i >= m_C3.Count() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_c3i = %d (should be >=0 and <%d=m_C3.Count()\n",
                      edge.m_c3i,m_C3.Count() );
      text_log->PopIndent();
    }
    return false;
  }
  
  if ( 0 == m_C3[c3i] )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_E[%d].m_c3i = %d, but m_C3[%d] is nullptr.\n",edge_index,c3i,c3i);
    return false;
  }

  if ( 0 == edge.ProxyCurve() )
  {
    if ( text_log )
      text_log->Print("brep.m_E[%d].m_c3i = %d, but edge.ProxyCurve() is nullptr.\n",edge_index,c3i);
    return false;
  }

  if ( m_C3[c3i] != edge.ProxyCurve() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("m_E[%d].m_c3i=%d, m_C3[%d] != m_E[%d].ProxyCurve()\n",edge_index,c3i,c3i,edge_index);
      text_log->PopIndent();
    }
    return false;
  }

  ON_Interval proxy_sub_dom = edge.ProxyCurveDomain();
  if ( !proxy_sub_dom.IsIncreasing() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("m_E[%d].ProxyCurveDomain() = (%g,%g) is not increasing\n",edge_index,proxy_sub_dom[0],proxy_sub_dom[1]);
      text_log->PopIndent();
    }
    return false;
  }

  ON_Interval c3_dom = m_C3[c3i]->Domain();
  if ( !c3_dom.Includes(proxy_sub_dom) )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("m_C3[%d].Domain() = (%g,%g) does not inlclude m_E[%d].ProxyCurveDomain() = (%g,%g) is not increasing\n",
                      c3i,c3_dom[0],c3_dom[1],edge_index,proxy_sub_dom[0],proxy_sub_dom[1]);
      text_log->PopIndent();
    }
    return false;
  }

  ON_Interval edge_dom = edge.Domain();
  if ( !edge_dom.IsIncreasing() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] trim is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("m_E[%d].Domain() = (%g,%g) is not increasing\n",edge_index,edge_dom[0],edge_dom[1]);
      text_log->PopIndent();
    }
    return false;
  }



  const int vi0 = edge.m_vi[0];
  const int vi1 = edge.m_vi[1];
  if ( vi0 < 0 || vi0 >= m_V.Count() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_vi[0]=%d (should be >=0 and <%d=m_V.Count()\n",
                       vi0, m_V.Count() );
      text_log->PopIndent();
    }
    return false;
  }
  if ( vi1 < 0 || vi1 >= m_V.Count() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_vi[1]=%d (should be >=0 and <%d=m_V.Count()\n",
                       vi1, m_V.Count() );
      text_log->PopIndent();
    }
    return false;
  }
  int evi;
  for ( evi = 0; evi < 2; evi++ ) 
  {
    const ON_BrepVertex& vertex = m_V[edge.m_vi[evi]];

    if ( edge.m_vi[evi] != vertex.m_vertex_index )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
        text_log->PushIndent();
        text_log->Print("edge.m_vi[%d]=%d is a deleted vertex\n",
                         evi,edge.m_vi[evi] );
        text_log->PopIndent();
      }
      return false;
    }


    const int vertex_edge_count = vertex.m_ei.Count();
    bool bFoundIt = false;
    int vei;
    for ( vei = 0; vei < vertex_edge_count && !bFoundIt; vei++ ) 
    {
      bFoundIt = (vertex.m_ei[vei] == edge_index);
    }
    if ( !bFoundIt )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
        text_log->PushIndent();
        text_log->Print("edge.m_vi[%d]=%d but edge is not referenced in m_V[%d].m_ei[]\n",
                         evi,edge.m_vi[evi],edge.m_vi[evi] );
        text_log->PopIndent();
      }
      return false;
    }
  }

  const int edge_trim_count = edge.m_ti.Count();
  if ( edge_trim_count < 0 )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_ti.Count() < 0\n");
      text_log->PopIndent();
    }
    return false;
  }
  int i, eti, ti;
  for (eti = 0; eti < edge_trim_count; eti++ )
  {
    ti = edge.m_ti[eti];
    if ( ti < 0 || ti >= m_T.Count() )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
        text_log->PushIndent();
        text_log->Print("edge.m_ti[%d]=%d (should be >=0 and <%d=m_T.Count())\n",eti,ti);
        text_log->PopIndent();
      }
      return false;
    }
    if ( m_T[ti].m_trim_index != ti )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
        text_log->PushIndent();
        text_log->Print("edge.m_ti[%d]=%d is a deleted trim\n",eti,ti);
        text_log->PopIndent();
      }
      return false;
    }
    for ( i = 0; i < eti; i++ ) 
    {
      if ( edge.m_ti[i] == ti )
      {
        if ( text_log )
        {
          text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
          text_log->PushIndent();
          text_log->Print("edge.m_ti[%d]=edge.m_ti[%d]=%d (a trim should be referenced once).\n",i,eti,ti);
          text_log->PopIndent();
        }
        return false;
      }
    }
    const ON_BrepTrim& trim = m_T[ti];
    if ( trim.m_ei != edge_index )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
        text_log->PushIndent();
        text_log->Print("edge.m_ti[%d]=%d but brep.m_T[%d].m_ei=%d\n",eti,ti,ti,trim.m_ei);
        text_log->PopIndent();
      }
      return false;
    }
  }

  return true;
}

bool
ON_Brep::IsValidLoopTopology( int loop_index, ON_TextLog* text_log ) const
{
  int lti, ti;

  if ( loop_index < 0 || loop_index >= m_L.Count() )
  {
    if ( text_log )
      text_log->Print("brep loop_index = %d (should be >=0 and <%d=brep.m_L.Count() ).\n",
                      loop_index, m_L.Count());
    return false;
  }
  const ON_BrepLoop& loop = m_L[loop_index];
  if ( loop.m_loop_index != loop_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
      text_log->PushIndent();
      text_log->Print("loop.m_loop_index = %d (should be %d).\n",
                       loop.m_loop_index, loop_index );
      text_log->PopIndent();
    }
    return false;
  }
  if ( loop.m_brep != this )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
      text_log->PushIndent();
      text_log->Print("loop.m_brep does not point to parent brep\n");
      text_log->PopIndent();
    }
    return false;
  }

  if ( loop.m_fi < 0 || loop.m_fi >= m_F.Count() )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_L[%d].m_fi = %d is not invalid.\n",loop_index,loop.m_fi);
    return false;
  }
  if ( m_F[loop.m_fi].m_face_index != loop.m_fi )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_L[%d].m_fi = %d is a deleted face.\n",loop_index,loop.m_fi);
    return false;
  }
  if ( loop.m_ti.Count() < 1 )
  {
      if ( text_log )
        text_log->Print("ON_Brep.m_L[%d].m_ti.Count() = %d  (should be > 0 )\n",loop_index,loop.m_ti.Count());
      return false;
  }

  for ( lti = 0; lti < loop.m_ti.Count(); lti++ )
  {
    ti = loop.m_ti[lti];
    if ( ti < 0 || ti >= m_T.Count() )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_L[%d].m_ti[%d] = %d is not invalid.\n",loop_index,lti,ti);
      return false;
    }
    const ON_BrepTrim& trim = m_T[ti];
    if ( trim.m_trim_index != ti )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_L[%d].m_ti[%d] = %d is a deleted trim.\n",loop_index,lti,ti);
      return false;
    }
    if ( trim.m_li != loop_index )
    {
      if ( text_log )
      {
        text_log->Print("brep loop m_L[%d] or trim m_T[%d] is not valid.\n",loop_index,ti);
        text_log->PushIndent();
        text_log->Print("loop.m_ti[%d] = %d != %d =trim.m_li\n",lti,ti,trim.m_li);
        text_log->PopIndent();
      }
      return false;
    }
  }


  int first_trim_ti = -4;
  int first_trim_vi0 = -3;
  int prev_trim_vi1 = -2;
  int prev_trim_ti=-9;
  for ( lti = 0; lti < loop.m_ti.Count(); lti++ )
  {
    const ON_BrepTrim& trim = m_T[loop.m_ti[lti]];
    if ( 0 == lti )
    {
      first_trim_ti = loop.m_ti[lti];
      first_trim_vi0 = trim.m_vi[0];
    }
    else if ( prev_trim_vi1 != trim.m_vi[0] )
    {
      // 23 May 2003 Dale Lear
      //     Added this test to make sure adjacent trims
      //     in a loop shared vertices.
      if ( text_log )
      {
         text_log->Print("brep loop m_L[%d] is not valid.\n",loop_index);
         text_log->PushIndent();
         text_log->Print("m_T[loop.m_ti[%d]=%d].m_vi[1] = %d != m_T[loop.m_ti[%d]=%d].m_vi[0]=%d.\n",
                          lti-1,prev_trim_ti,prev_trim_vi1,lti,loop.m_ti[lti],trim.m_vi[0]);
         text_log->PopIndent();
      }
      return false;
    }
    prev_trim_ti = loop.m_ti[lti];
    prev_trim_vi1 = trim.m_vi[1];
  }

  if ( first_trim_ti >= 0 && first_trim_vi0 != prev_trim_vi1 )
  {
    // 23 May 2003 Dale Lear
    //     Added this test to make sure adjacent trims
    //     in a loop shared vertices.
    if ( text_log )
    {
       text_log->Print("brep loop m_L[%d] is not valid.\n",loop_index);
       text_log->PushIndent();
       text_log->Print("m_T[loop.m_ti[%d]=%d].m_vi[1] = %d != m_T[loop.m_ti[0]=%d].m_vi[0]=%d.\n",
                        loop.m_ti.Count()-1,prev_trim_ti,prev_trim_vi1,first_trim_ti,first_trim_vi0);
       text_log->PopIndent();
    }
    return false;
  }


  return true;
}

bool
ON_Brep::IsValidTrimTopology( int trim_index, ON_TextLog* text_log ) const
{
  if ( trim_index < 0 || trim_index >= m_T.Count() )
  {
    if ( text_log )
      text_log->Print("brep trim_index = %d (should be >=0 and <%d=brep.m_T.Count() ).\n",
                      trim_index, m_T.Count());
    return false;
  }
  const ON_BrepTrim& trim = m_T[trim_index];
  if ( trim.m_trim_index != trim_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
      text_log->PushIndent();
      text_log->Print("trim.m_trim_index = %d (should be %d).\n",
                       trim.m_trim_index, trim_index );
      text_log->PopIndent();
    }
    return false;
  }
  if ( trim.m_brep != this )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
      text_log->PushIndent();
      text_log->Print("trim.m_brep does not point to parent brep\n");
      text_log->PopIndent();
    }
    return false;
  }

  if ( trim.m_vi[0] < 0 || trim.m_vi[0] >= m_V.Count() )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_T[%d].m_vi[0] = %d is not invalid.\n",trim_index,trim.m_vi[0]);
    return false;
  }
  if ( trim.m_vi[1] < 0 || trim.m_vi[1] >= m_V.Count() )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_T[%d].m_vi[1] = %d is not invalid.\n",trim_index,trim.m_vi[1]);
    return false;
  }

  if ( m_V[trim.m_vi[0]].m_vertex_index != trim.m_vi[0] )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_T[%d].m_vi[0] is deleted.\n",trim_index);
    return false;
  }
  if ( m_V[trim.m_vi[1]].m_vertex_index != trim.m_vi[1] )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_T[%d].m_vi[1] is deleted.\n",trim_index);
    return false;
  }

  if ( trim.m_c2i < 0 || trim.m_c2i >= m_C2.Count() )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_T[%d].m_c2i = %d is not valid.\n",trim_index,trim.m_c2i);
    return false;
  }

  if ( 0 == m_C2[trim.m_c2i] )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_T[%d].m_c2i = %d, but m_C2[%d] is nullptr.\n",trim_index,trim.m_c2i,trim.m_c2i);
    return false;
  }

  if ( 0 == trim.ProxyCurve() )
  {
    if ( text_log )
      text_log->Print("brep.m_T[%d].m_c2i = %d, but trim.ProxyCurve() is nullptr.\n",trim_index,trim.m_c2i);
    return false;
  }

  if ( m_C2[trim.m_c2i] != trim.ProxyCurve() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
      text_log->PushIndent();
      text_log->Print("m_T[%d].m_c2i=%d, m_C2[%d] != m_T[%d].ProxyCurve()\n",
                      trim_index, trim.m_c2i, trim.m_c2i, trim_index);
      text_log->PopIndent();
    }
    return false;
  }

  ON_Interval proxy_sub_dom = trim.ProxyCurveDomain();
  if ( !proxy_sub_dom.IsIncreasing() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
      text_log->PushIndent();
      text_log->Print("m_T[%d].ProxyCurveDomain() = (%g,%g) is not increasing\n",trim_index,proxy_sub_dom[0],proxy_sub_dom[1]);
      text_log->PopIndent();
    }
    return false;
  }

  ON_Interval c2_dom = m_C2[trim.m_c2i]->Domain();
  if ( !c2_dom.Includes(proxy_sub_dom) )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
      text_log->PushIndent();
      text_log->Print("m_C2[%d].Domain() = (%g,%g) does not include m_T[%d].ProxyCurveDomain() = (%g,%g) is not increasing\n",
                      trim.m_c2i,c2_dom[0],c2_dom[1],
                      trim_index,proxy_sub_dom[0],proxy_sub_dom[1]);
      text_log->PopIndent();
    }
    return false;
  }

  ON_Interval trim_dom = trim.Domain();
  if ( !trim_dom.IsIncreasing() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
      text_log->PushIndent();
      text_log->Print("m_T[%d].Domain() = (%g,%g) is not increasing\n",trim_index,trim_dom[0],trim_dom[1]);
      text_log->PopIndent();
    }
    return false;
  }

  if ( trim.m_li < 0 || trim.m_li >= m_L.Count() )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_T[%d].m_li = %d is not valid.\n",trim_index,trim.m_li);
    return false;
  }

  const ON_BrepLoop& loop = m_L[trim.m_li];

  if ( loop.m_loop_index != trim.m_li )
  {
    if ( text_log )
      text_log->Print("ON_Brep.m_T[%d].m_li = %d is a deleted loop.\n",trim_index,trim.m_li);
    return false;
  }

  bool bFoundTrim = false;
  int lti, loop_trim_count = loop.m_ti.Count();
  for ( lti = 0; lti < loop_trim_count && !bFoundTrim; lti++ )
  {
    if ( loop.m_ti[lti] == trim_index )
    {
      bFoundTrim = true;
      break;
    }
  }
  if ( !bFoundTrim )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim or brep.m_L[%d] loop is not valid.\n",trim_index,trim.m_li);
      text_log->PushIndent();
      text_log->Print("trim.m_li = %d but loop.m_ti[] does not contain %d (should appear once in).\n",
                      trim.m_li,trim_index);
      text_log->PopIndent();
    }
    return false;
  }

  if ( trim.m_type == ON_BrepTrim::singular )
  {
    // trim has no 3d edge
    if ( trim.m_ei != -1 )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d].m_type = singular, but m_ei = %d (should be -1).\n",trim_index,trim.m_ei);
      return false;
    }
    if ( trim.m_bRev3d )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d].m_type = singular, but m_bRev3d = %d (should be 0).\n",trim_index,trim.m_bRev3d);
      return false;
    }
    if ( trim.m_vi[0] != trim.m_vi[1] )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d].m_type = singular, but m_vi = (%d,%d) (should be same vertex index).\n",
                        trim_index,trim.m_vi[0],trim.m_vi[1]);
      return false;
    }
  }
  else
  {
    // trim should be connected to a 3d edge
    if ( trim.m_ei < 0 || trim.m_ei >= m_E.Count() )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d].m_ei = %d is not invalid.\n",trim_index,trim.m_ei);
      return false;
    }
  
    const ON_BrepEdge& edge = m_E[trim.m_ei];
    if ( edge.m_edge_index != trim.m_ei )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d].m_ei is deleted.\n",trim_index);
      return false;
    }

    const int evi0 = trim.m_bRev3d ? 1 : 0;
    const int evi1 = trim.m_bRev3d ? 0 : 1;
    if ( trim.m_vi[0] != edge.m_vi[evi0] )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d].m_bRev3d = %d, but m_vi[0] != m_E[m_ei].m_vi[%d].\n",trim_index,trim.m_bRev3d,evi0);
      return false;
    }
    if ( trim.m_vi[1] != edge.m_vi[evi1] )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d].m_bRev3d = %d, but m_vi[0] != m_E[m_ei].m_vi[%d].\n",trim_index,trim.m_bRev3d,evi1);
      return false;
    }
  }

  return true;
}

bool
ON_Brep::IsValidTopology( ON_TextLog* text_log ) const
{
  const int curve2d_count = m_C2.Count();
  const int curve3d_count = m_C3.Count();
  const int surface_count = m_S.Count();
  const int vertex_count  = m_V.Count();
  const int edge_count    = m_E.Count();
  const int trim_count    = m_T.Count();
  const int loop_count    = m_L.Count();
  const int face_count    = m_F.Count();

  int vi, ei, fi, ti, li;

  if ( 0 == face_count && 0 == edge_count && 0 == vertex_count )
  {
    if ( text_log )
      text_log->Print( "ON_Brep has no faces, edges, or vertices\n");
    return false;
  }

  if ( 0 != face_count )
  {
    if ( 0 == edge_count )
    {
      if ( text_log )
        text_log->Print( "ON_Brep has no edges.\n");
      return false;
    }
    if ( 0 == loop_count )
    {
      if ( text_log )
        text_log->Print( "ON_Brep has no loops.\n");
      return false;
    }
    if ( 0 == surface_count )
    {
      if ( text_log )
        text_log->Print( "ON_Brep has no surfaces.\n");
      return false;
    }
    if ( 0 == trim_count )
    {
      if ( text_log )
        text_log->Print( "ON_Brep has no trims.\n");
      return false;
    }
    if ( 0 == curve2d_count )
    {
      if ( text_log )
        text_log->Print( "ON_Brep has no 2d curves.\n");
      return false;
    }
  }

  if ( 0 != edge_count )
  {
    if ( 0 == curve3d_count )
    {
      if ( text_log )
        text_log->Print( "ON_Brep has no 3d curves.\n");
      return false;
    }
    if ( 0 == vertex_count )
    {
      if ( text_log )
        text_log->Print( "ON_Brep has no vertices.\n");
      return false;
    }
  }

  // check element indices match array positions
  for ( vi = 0; vi < vertex_count; vi++ ) 
  {
    if ( m_V[vi].m_vertex_index == -1 )
    {
      const ON_BrepVertex& vertex = m_V[vi];
      if ( vertex.m_ei.Count() > 0 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_V[%d] is deleted (m_vertex_index = -1) but vertex.m_ei.Count() = %d.\n",
                           vi, vertex.m_ei.Count() );
        return false;
      }
    }
    else if ( m_V[vi].m_vertex_index != vi )
    {
      if ( text_log )
        text_log->Print( "ON_Brep.m_V[%d].m_vertex_index = %d (should be %d)\n",
                         vi, m_V[vi].m_vertex_index, vi );
      return false;
    }
  }

  for ( ei = 0; ei < edge_count; ei++ ) 
  {
    if ( m_E[ei].m_edge_index == -1 )
    {
      const ON_BrepEdge& edge = m_E[ei];
      if ( edge.m_ti.Count() > 0 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_ei.Count() = %d.\n",
                           ei, edge.m_ti.Count() );
        return false;
      }
      if ( edge.m_c3i != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_c3i=%d (should be -1).\n",
                           ei, edge.m_c3i );
        return false;
      }
      if ( edge.ProxyCurve() )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_curve is not nullptr.\n",
                           ei );
        return false;
      }
      if ( edge.m_vi[0] != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_vi[0]=%d (should be -1).\n",
                           ei, edge.m_vi[0] );
        return false;
      }
      if ( edge.m_vi[1] != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_vi[1]=%d (should be -1).\n",
                           ei, edge.m_vi[1] );
        return false;
      }
    }
    else if ( m_E[ei].m_edge_index != ei )
    {
      if ( text_log )
        text_log->Print( "ON_Brep.m_E[%d].m_edge_index = %d (should be %d)\n",
                         ei, m_E[ei].m_edge_index, ei );
      return false;
    }
  }

  for ( ti = 0; ti < trim_count; ti++ ) 
  {
    if ( m_T[ti].m_trim_index == -1 )
    {
      const ON_BrepTrim& trim = m_T[ti];
      if ( trim.m_ei != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_ei=%d (should be -1).\n",
                           ti, trim.m_ei );
        return false;
      }
      if ( trim.m_li != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_li=%d (should be -1).\n",
                           ti, trim.m_li );
        return false;
      }
      if ( trim.m_c2i != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_c2i=%d (should be -1).\n",
                           ti, trim.m_c2i );
        return false;
      }
      if ( trim.m_vi[0] != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_vi[0]=%d (should be -1).\n",
                           ti, trim.m_vi[0] );
        return false;
      }
      if ( trim.m_vi[1] != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_vi[1]=%d (should be -1).\n",
                           ti, trim.m_vi[1] );
        return false;
      }
    }
    else if ( m_T[ti].m_trim_index != ti  )
    {
      if ( text_log )
        text_log->Print( "ON_Brep.m_T[%d].m_trim_index = %d (should be %d)\n",
                         ti, m_T[ti].m_trim_index, ti );
      return false;
    }
    else if ( !m_T[ti].IsValid( text_log ) )
    {
      if ( text_log )
        text_log->Print( "ON_Brep.m_T[%d] is not valid\n",ti );
      return false;
    }
  }

  for ( li = 0; li < loop_count; li++ ) 
  {
    if ( m_L[li].m_loop_index == -1 )
    {
      const ON_BrepLoop& loop = m_L[li];
      if ( loop.m_fi != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_L[%d] is deleted (m_loop_index = -1) but loop.m_fi=%d (should be -1).\n",
                           li, loop.m_fi );
        return false;
      }
      if ( loop.m_ti.Count() > 0 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_L[%d] is deleted (m_loop_index = -1) but loop.m_ti.Count()=%d.\n",
                           li, loop.m_ti.Count() );
        return false;
      }
    }
    else if ( m_L[li].m_loop_index != li )
    {
      if ( text_log )
        text_log->Print( "ON_Brep.m_L[%d].m_loop_index = %d (should be %d)\n",
                         li, m_L[li].m_loop_index, li );
      return false;
    }
  }

  for ( fi = 0; fi < face_count; fi++ ) 
  {
    if ( m_F[fi].m_face_index == -1 )
    {
      const ON_BrepFace& face = m_F[fi];
      if ( face.m_si != -1 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.m_si=%d (should be -1).\n",
                           fi, face.m_si );
        return false;
      }
      if ( face.ProxySurface() )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.ProxySurface() is not nullptr.\n",
                           fi );
        return false;
      }
      if ( face.m_li.Count() > 0 )
      {
        if ( text_log )
          text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.m_li.Count()=%d.\n",
                           fi, face.m_li.Count() );
        return false;
      }
    }
    else if ( m_F[fi].m_face_index != fi )
    {
      if ( text_log )
        text_log->Print( "ON_Brep.m_F[%d].m_face_index = %d (should be %d)\n",
                         fi, m_F[fi].m_face_index, fi );
      return false;
    }
  }

  // check vertices
  for ( vi = 0; vi < vertex_count; vi++ ) {
    if ( m_V[vi].m_vertex_index == -1 )
      continue;
    if ( !IsValidVertexTopology( vi, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_V[%d] is invalid.\n",vi);
      return false;
    }
  }

  // check edges
  for ( ei = 0; ei < edge_count; ei++ ) 
  {
    if ( m_E[ei].m_edge_index == -1 )
      continue;
    if ( !IsValidEdgeTopology( ei, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_E[%d] is invalid.\n",ei);
      return false;
    }
  }

  // check faces
  for ( fi = 0; fi < face_count; fi++ ) 
  {
    if ( m_F[fi].m_face_index == -1 )
      continue;
    if ( !IsValidFaceTopology( fi, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_F[%d] is invalid.\n",fi);
      return false;
    }
  }

  // check trims
  for ( ti = 0; ti < trim_count; ti++ )
  {
    const ON_BrepTrim& trim = m_T[ti];
    if ( trim.m_trim_index == -1 )
      continue;
    if ( !IsValidTrimTopology( ti, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d] is invalid.\n",ti);
      return false;
    }
  }

  // check loops
  for ( li = 0; li < loop_count; li++ )
  {
    const ON_BrepLoop& loop = m_L[li];
    if ( loop.m_loop_index == -1 )
      continue;
    if ( !IsValidLoopTopology( li, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_L[%d] is invalid.\n",li);
      return false;
    }
  }

  return true;
}


////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

bool
ON_Brep::IsValidVertexGeometry( int vertex_index, ON_TextLog* text_log ) const
{
  if ( vertex_index < 0 || vertex_index >= m_V.Count() )
  {
    if ( text_log )
      text_log->Print("brep vertex_index = %d (should be >=0 and <%d=brep.m_V.Count() ).\n",
                      vertex_index, m_V.Count());
    return false;
  }
  const ON_BrepVertex& vertex = m_V[vertex_index];
  if ( vertex.m_vertex_index != vertex_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
      text_log->PushIndent();
      text_log->Print("vertex.m_vertex_index = %d (should be %d).\n",
                       vertex.m_vertex_index, vertex_index );
      text_log->PopIndent();
    }
    return false;
  }

  if ( !vertex.point.IsValid() )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_V[%d] vertex geometry is not valid.\n",vertex_index);
      text_log->PushIndent();
      text_log->Print("vertex.point = (%g,%g,%g) is not valid.\n",vertex.point.x,vertex.point.y,vertex.point.z );
      text_log->PopIndent();
    }
    return false;
  }
  return true;
}

bool
ON_Brep::IsValidEdgeGeometry( int edge_index, ON_TextLog* text_log ) const
{
  if ( edge_index < 0 || edge_index >= m_E.Count() )
  {
    if ( text_log )
      text_log->Print("brep edge_index = %d (should be >=0 and <%d=brep.m_E.Count() ).\n",
                      edge_index, m_E.Count());
    return false;
  }
  const ON_BrepEdge& edge = m_E[edge_index];
  if ( edge.m_edge_index != edge_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_edge_index = %d (should be %d).\n",
                       edge.m_edge_index, edge_index );
      text_log->PopIndent();
    }
    return false;
  }

  int vi0 = edge.m_vi[0];
  int vi1 = edge.m_vi[1];
  if ( edge.IsClosed() ) 
  {
    if ( vi0 != vi1 )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
        text_log->PushIndent();
        text_log->Print("edge.m_vi[]=(%d,%d) but edge.IsClosed() is true\n",
                         vi0,vi1);
        text_log->PopIndent();
      }
      return false;
    }
  }
  else {
    if ( vi0 == vi1 )
    {
      if ( text_log )
      {
        text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
        text_log->PushIndent();
        text_log->Print("edge.m_vi[0]=edge.m_vi[1]=%d but edge.IsClosed() is false.\n",
                         vi0);
        text_log->PopIndent();
      }
      return false;
    }
  }

  return true;
}

bool
ON_Brep::IsValidFaceGeometry( int face_index, ON_TextLog* text_log ) const
{
  if ( face_index < 0 || face_index >= m_F.Count() )
  {
    if ( text_log )
      text_log->Print("brep face_index = %d (should be >=0 and <%d=brep.m_F.Count() ).\n",
                      face_index, m_F.Count());
    return false;
  }
  const ON_BrepFace& face = m_F[face_index];
  if ( face.m_face_index != face_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("face.m_face_index = %d (should be %d).\n",
                       face.m_face_index, face_index );
      text_log->PopIndent();
    }
    return false;
  }
  return true;
}

bool
ON_Brep::IsValidLoopGeometry( int loop_index, ON_TextLog* text_log ) const
{
  if ( loop_index < 0 || loop_index >= m_L.Count() )
  {
    if ( text_log )
      text_log->Print("brep loop_index = %d (should be >=0 and <%d=brep.m_L.Count() ).\n",
                      loop_index, m_L.Count());
    return false;
  }
  const ON_BrepLoop& loop = m_L[loop_index];
  if ( loop.m_loop_index != loop_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
      text_log->PushIndent();
      text_log->Print("loop.m_loop_index = %d (should be %d).\n",
                       loop.m_loop_index, loop_index );
      text_log->PopIndent();
    }
    return false;
  }
  return true;
}

bool
ON_Brep::IsValidTrimGeometry( int trim_index, ON_TextLog* text_log ) const
{
  if ( trim_index < 0 || trim_index >= m_T.Count() )
  {
    if ( text_log )
      text_log->Print("brep trim_index = %d (should be >=0 and <%d=brep.m_T.Count() ).\n",
                      trim_index, m_T.Count());
    return false;
  }
  const ON_BrepTrim& trim = m_T[trim_index];
  if ( trim.m_trim_index != trim_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
      text_log->PushIndent();
      text_log->Print("trim.m_trim_index = %d (should be %d).\n",
                       trim.m_trim_index, trim_index );
      text_log->PopIndent();
    }
    return false;
  }
  return true;
}

bool
ON_Brep::IsValidGeometry( ON_TextLog* text_log ) const
{
  const int curve2d_count = m_C2.Count();
  const int curve3d_count = m_C3.Count();
  const int surface_count = m_S.Count();
  const int vertex_count  = m_V.Count();
  const int edge_count    = m_E.Count();
  const int trim_count    = m_T.Count();
  const int loop_count    = m_L.Count();
  const int face_count    = m_F.Count();

  int c2i, c3i, si, vi, ei, fi, ti, li;

  // check 2d curve geometry
  for ( c2i = 0; c2i < curve2d_count; c2i++ ) {
    if ( !m_C2[c2i] )
    {
      continue;
      // nullptr 2d curves are ok if they are not referenced
    }
    if ( !m_C2[c2i]->IsValid(text_log) )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_C2[%d] is invalid.\n",c2i);
      return false;
    }
    int c2_dim = m_C2[c2i]->Dimension();
    if ( c2_dim != 2 )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_C2[%d]->Dimension() = %d (should be 2).\n", c2i, c2_dim );
      return false;
    }
  }

  // check 3d curve geometry
  for ( c3i = 0; c3i < curve3d_count; c3i++ ) {
    if ( !m_C3[c3i] )
    {
      continue;
      // nullptr 3d curves are ok if they are not referenced
    }
    if ( !m_C3[c3i]->IsValid(text_log) )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_C3[%d] is invalid.\n",c3i);
      return false;
    }
    int c3_dim = m_C3[c3i]->Dimension();
    if ( c3_dim != 3 )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_C3[%d]->Dimension() = %d (should be 3).\n", c3i, c3_dim );
      return false;
    }
  }

  // check 3d surface geometry
  for ( si = 0; si < surface_count; si++ ) {
    if ( !m_S[si] )
    {
      continue;
      // nullptr 3d surfaces are ok if they are not referenced
    }
    if ( !m_S[si]->IsValid(text_log) )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_S[%d] is invalid.\n",si);
      return false;
    }
    int dim = m_S[si]->Dimension();
    if ( dim != 3 )
    {
      if ( text_log )
        text_log->Print("ON_Brep.m_S[%d]->Dimension() = %d (should be 3).\n", si, dim );
      return false;
    }
  }

  // check vertices
  for ( vi = 0; vi < vertex_count; vi++ ) {
    if ( m_V[vi].m_vertex_index == -1 )
      continue;
    if ( !IsValidVertexGeometry( vi, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_V[%d] is invalid.\n",vi);
      return false;
    }
  }

  // check edges
  for ( ei = 0; ei < edge_count; ei++ ) 
  {
    if ( m_E[ei].m_edge_index == -1 )
      continue;
    if ( !IsValidEdgeGeometry( ei, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_E[%d] is invalid.\n",ei);
      return false;
    }
  }

  // check faces
  for ( fi = 0; fi < face_count; fi++ ) 
  {
    if ( m_F[fi].m_face_index == -1 )
      continue;
    if ( !IsValidFaceGeometry( fi, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_F[%d] is invalid.\n",fi);
      return false;
    }
  }

  // check trims
  for ( ti = 0; ti < trim_count; ti++ )
  {
    if ( m_T[ti].m_trim_index == -1 )
      continue;
    if ( !IsValidTrimGeometry( ti, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d] is invalid.\n",ti);
      return false;
    }
  }

  // check loops
  for ( li = 0; li < loop_count; li++ )
  {
    if ( m_L[li].m_loop_index == -1 )
      continue;
    if ( !IsValidLoopGeometry( li, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_L[%d] is invalid.\n",li);
      return false;
    }
  }

  return true;
}

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

bool
ON_Brep::IsValidVertexTolerancesAndFlags( int vertex_index, ON_TextLog* text_log ) const
{
  if ( vertex_index < 0 || vertex_index >= m_V.Count() )
  {
    if ( text_log )
      text_log->Print("brep vertex_index = %d (should be >=0 and <%d=brep.m_V.Count() ).\n",
                      vertex_index, m_V.Count());
    return false;
  }
  const ON_BrepVertex& vertex = m_V[vertex_index];
  if ( vertex.m_vertex_index != vertex_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
      text_log->PushIndent();
      text_log->Print("vertex.m_vertex_index = %d (should be %d).\n",
                       vertex.m_vertex_index, vertex_index );
      text_log->PopIndent();
    }
    return false;
  }

  if ( vertex.m_tolerance < 0.0 )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
      text_log->PushIndent();
      text_log->Print("vertex.m_tolerace = %g (should be >= 0.0)\n",vertex.m_tolerance);
      text_log->PopIndent();
    }
    return false;
  }

  return true;
}

bool
ON_Brep::IsValidEdgeTolerancesAndFlags( int edge_index, ON_TextLog* text_log ) const
{
  if ( edge_index < 0 || edge_index >= m_E.Count() )
  {
    if ( text_log )
      text_log->Print("brep edge_index = %d (should be >=0 and <%d=brep.m_E.Count() ).\n",
                      edge_index, m_E.Count());
    return false;
  }
  const ON_BrepEdge& edge = m_E[edge_index];
  if ( edge.m_edge_index != edge_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_edge_index = %d (should be %d).\n",
                       edge.m_edge_index, edge_index );
      text_log->PopIndent();
    }
    return false;
  }

  if ( edge.m_tolerance < 0.0 )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
      text_log->PushIndent();
      text_log->Print("edge.m_tolerance=%g (should be >= 0.0)\n",edge.m_tolerance);
      text_log->PopIndent();
    }
    return false;
  }

  return true;
}

bool
ON_Brep::IsValidFaceTolerancesAndFlags( int face_index, ON_TextLog* text_log ) const
{
  if ( face_index < 0 || face_index >= m_F.Count() )
  {
    if ( text_log )
      text_log->Print("brep face_index = %d (should be >=0 and <%d=brep.m_F.Count() ).\n",
                      face_index, m_F.Count());
    return false;
  }
  const ON_BrepFace& face = m_F[face_index];
  if ( face.m_face_index != face_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
      text_log->PushIndent();
      text_log->Print("face.m_face_index = %d (should be %d).\n",
                       face.m_face_index, face_index );
      text_log->PopIndent();
    }
    return false;
  }
  return true;
}

bool
ON_Brep::IsValidLoopTolerancesAndFlags( int loop_index, ON_TextLog* text_log ) const
{
  if ( loop_index < 0 || loop_index >= m_L.Count() )
  {
    if ( text_log )
      text_log->Print("brep loop_index = %d (should be >=0 and <%d=brep.m_L.Count() ).\n",
                      loop_index, m_L.Count());
    return false;
  }
  const ON_BrepLoop& loop = m_L[loop_index];
  if ( loop.m_loop_index != loop_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
      text_log->PushIndent();
      text_log->Print("loop.m_loop_index = %d (should be %d).\n",
                       loop.m_loop_index, loop_index );
      text_log->PopIndent();
    }
    return false;
  }
  return true;
}

bool
ON_Brep::IsValidTrimTolerancesAndFlags( int trim_index, ON_TextLog* text_log ) const
{
  if ( trim_index < 0 || trim_index >= m_T.Count() )
  {
    if ( text_log )
      text_log->Print("brep trim_index = %d (should be >=0 and <%d=brep.m_T.Count() ).\n",
                      trim_index, m_T.Count());
    return false;
  }
  const ON_BrepTrim& trim = m_T[trim_index];
  if ( trim.m_trim_index != trim_index )
  {
    if ( text_log )
    {
      text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
      text_log->PushIndent();
      text_log->Print("trim.m_trim_index = %d (should be %d).\n",
                       trim.m_trim_index, trim_index );
      text_log->PopIndent();
    }
    return false;
  }
  return true;
}

bool
ON_Brep::IsValidTolerancesAndFlags( ON_TextLog* text_log ) const
{
  const int vertex_count  = m_V.Count();
  const int edge_count    = m_E.Count();
  const int trim_count    = m_T.Count();
  const int loop_count    = m_L.Count();
  const int face_count    = m_F.Count();

  int vi, ei, fi, ti, li;

  // check vertices
  for ( vi = 0; vi < vertex_count; vi++ ) {
    if ( m_V[vi].m_vertex_index == -1 )
      continue;
    if ( !IsValidVertexTolerancesAndFlags( vi, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_V[%d] is invalid.\n",vi);
      return false;
    }
  }

  // check edges
  for ( ei = 0; ei < edge_count; ei++ ) 
  {
    if ( m_E[ei].m_edge_index == -1 )
      continue;
    if ( !IsValidEdgeTolerancesAndFlags( ei, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_E[%d] is invalid.\n",ei);
      return false;
    }
  }

  // check faces
  for ( fi = 0; fi < face_count; fi++ ) 
  {
    if ( m_F[fi].m_face_index == -1 )
      continue;
    if ( !IsValidFaceTolerancesAndFlags( fi, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_F[%d] is invalid.\n",fi);
      return false;
    }
  }

  // check trims
  for ( ti = 0; ti < trim_count; ti++ )
  {
    if ( m_T[ti].m_trim_index == -1 )
      continue;
    if ( !IsValidTrimTolerancesAndFlags( ti, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_T[%d] is invalid.\n",ti);
      return false;
    }
  }

  // check loops
  for ( li = 0; li < loop_count; li++ )
  {
    if ( m_L[li].m_loop_index == -1 )
      continue;
    if ( !IsValidLoopTolerancesAndFlags( li, text_log ) ) {
      if ( text_log )
        text_log->Print("ON_Brep.m_L[%d] is invalid.\n",li);
      return false;
    }
  }

  return true;
}

