/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkRectilinearGridConnectivity.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkRectilinearGridConnectivity.h"

#include "vtkPolyData.h"
#include "vtkCellData.h"
#include "vtkPointData.h"
#include "vtkCellArray.h"
#include "vtkIdTypeArray.h"
#include "vtkDoubleArray.h"
#include "vtkEquivalenceSet.h"
#include "vtkRectilinearGrid.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkIncrementalOctreePointLocator.h"

#include "vtkInformation.h"
#include "vtkObjectFactory.h"
#include "vtkInformationVector.h"
#include "vtkCompositeDataIterator.h"
#include "vtkCompositeDataPipeline.h"
#include "vtkMultiProcessController.h"

#include <map>
#include <vector>
#include <string>


vtkStandardNewMacro( vtkRectilinearGridConnectivity );

// An extended marching cubes case table for generating cube faces (either
// truncated by iso-lines or not) in addition to iso-triangles. These two
// kinds of polygons in combination represent the surface(s) of the greater-
// than-isovalue sub-volume(s) extracted in a cube. An index in each entry
// may refer to an Interpolated Iso-Value Point (IIVP, i.e., the associated
// edge, 0 ~ 11) or one of the eight Vertices Of the Cube (VOC: 12 ~ 19).
// The constituent polygons (triangles, quads, or pentagons) of a sub-volume
// are listed successively, ending with flag -1 in sepration from another sub-
// volume that may follows. Each polygon begins with the number of the points
// followed by the specific IIVP-Ids and VOC-Ids. Flag -2 terminates the list
// of all sub-volumes, if any. The two integers in the annotation section of
// each entry indicate the case number (0 ~ 255) and the base case number (0
// ~ 15), respectively.

struct  vtkRectilinearGridConnectivityMarchingCubesVolumeCases
{
  int    PointIds[68];
  static int GetEntrySize() { return 68; }
  static vtkRectilinearGridConnectivityMarchingCubesVolumeCases * GetCases();
};


// ============================================================================
// ======================== Supporting Classes (begin) ========================
// ============================================================================


#define VTK_MAX_FACES_PER_CELL 12


class vtkRectilinearGridConnectivityInternal
{
public:
  int                              ComponentNumbersObtained;
  int                              NumberIntegralComponents;
  int                              VolumeFractionArraysType;
  double                           VolumeFractionValueScale;
  std::vector<       int      > ComponentNumbersPerArray;
  std::vector< std::string > VolumeFractionArrayNames;
  std::vector< std::string > VolumeDataAttributeNames;
  std::vector< std::string > IntegrableAttributeNames;

  int IntegrablePointDataArraysAvailable( vtkRectilinearGrid * rectGrid )
    {
    int  numArays = static_cast<int> ( this->IntegrableAttributeNames.size() );
    int  allExist = numArays;

    for ( int i = 0; i < numArays && allExist; i ++ )
      {
      allExist = vtkDoubleArray::SafeDownCast
                 (  rectGrid->GetPointData()->GetArray
                    ( this->IntegrableAttributeNames[i].c_str() )
                 ) ? allExist : 0;
      }

    return allExist;
    }

  int IntegrableCellDataArraysAvailable( vtkPolyData * polyData )
    {
    int  numArays = static_cast<int> ( this->IntegrableAttributeNames.size() );
    int  allExist = numArays;

    for ( int i = 0; i < numArays && allExist; i ++ )
      {
      allExist = vtkDoubleArray::SafeDownCast
                 (  polyData->GetCellData()->GetArray
                    ( this->IntegrableAttributeNames[i].c_str() )
                 ) ? allExist : 0;
      }

    return allExist;
    }
};


//============================================================================


class vtkRectilinearGridConnectivityFace
{
public:

  // It is currently assumed that there are not so many blocks, fragments, and
  // processes. In case this assumption does not hold, 'short' needs to be
  // changed to 'int'
  short      BlockId;    // for intra-process inter-block fragments extraction
  short      FragmentId; // for any level fragments extraction
  short      ProcessId;  // for inter-process fragments extraction
  vtkIdType  PolygonId;  // for any level fragments extraction
                         // global index of the face / polygon in a vtkPolyData

  // three smallest global point-Ids are used for a unique representation of a
  // polygon (triangle, quad, or pentagon) and the first one is actually the
  // index of the entry in the face hash
  vtkIdType  PointId2;
  vtkIdType  PointId3;

  vtkRectilinearGridConnectivityFace * NextFace; // next face in a linked list
};


//============================================================================


class vtkRectilinearGridConnectivityFaceHeap
{
public:
  vtkRectilinearGridConnectivityFaceHeap();
  ~vtkRectilinearGridConnectivityFaceHeap();

  vtkRectilinearGridConnectivityFace * NewFace();

  // The face is valid until NewFace is called or the heap destructs.
  void RecycleFace( vtkRectilinearGridConnectivityFace * face);

private:

  int  HeapLength;
  int  NextFaceIndex; // Which heap face is next in line.

  // Hard code the allocation size.
  // We could do fancy doubling, but why bother.
  // The cost of allocating another chink is small because
  // we do not copy.
  int  NumberOfFacesPerAllocation;

  // Ivars that allow fast allocation of face objects.
  // They are allocated a few hundred at a time, and reused.
  vtkRectilinearGridConnectivityFace * Heap;
  vtkRectilinearGridConnectivityFace * RecycleBin;

  // The faces are allocated in arrays/heaps, so they need to be
  // deleted in arrays.  The first face from every array is reserved
  // and saved in a linked list so we can delete the arrays later.
  vtkRectilinearGridConnectivityFace * Heaps;

  void Allocate();
};

vtkRectilinearGridConnectivityFaceHeap::vtkRectilinearGridConnectivityFaceHeap()
{
  this->Heap = 0;
  this->Heaps = 0;
  this->HeapLength = 0;
  this->RecycleBin = 0;
  this->NextFaceIndex = 0;
  this->NumberOfFacesPerAllocation = 1000;
}

vtkRectilinearGridConnectivityFaceHeap::~vtkRectilinearGridConnectivityFaceHeap()
{
  this->RecycleBin = 0;
  this->NumberOfFacesPerAllocation = 0;

  while ( this->Heaps )
    {
    vtkRectilinearGridConnectivityFace * next = this->Heaps->NextFace;
    delete [] this->Heaps;
    this->Heaps = next;
    }
}

void vtkRectilinearGridConnectivityFaceHeap::Allocate()
{
  vtkRectilinearGridConnectivityFace * newHeap = new
  vtkRectilinearGridConnectivityFace[ this->NumberOfFacesPerAllocation ];

  // use the first element to construct a linked list of arrays/heaps
  newHeap[0].NextFace = this->Heaps;
  this->Heaps         = newHeap;

  this->HeapLength    = this->NumberOfFacesPerAllocation;
  this->NextFaceIndex = 1; // skip the first (used for the Heaps linked list)
  this->Heap          = newHeap;
}

// The face will be valid until NewFace is called or the heap destructs.
void vtkRectilinearGridConnectivityFaceHeap::RecycleFace
  ( vtkRectilinearGridConnectivityFace * face )
{
  face->NextFace   = this->RecycleBin;
  this->RecycleBin = face;
}

vtkRectilinearGridConnectivityFace * vtkRectilinearGridConnectivityFaceHeap
  ::NewFace()
{
  vtkRectilinearGridConnectivityFace * face = NULL;

  // look for faces to use in the recycle bin
  if (this->RecycleBin)
    {
    face = this->RecycleBin;
    this->RecycleBin = face->NextFace;
    face->NextFace = 0;
    }
  else
    {
    // nothing to recycle --> get a face from the heap
    if ( this->NextFaceIndex >= this->HeapLength )
      {
      // we need to allocate another heap
      this->Allocate();
      }

    face = this->Heap + this->NextFaceIndex ++;
    }

  face->PointId2   = 0;
  face->PointId3   = 0;

  // we only need one cell id, because we delete from the hash
  // any face with two cells because it is internal
  face->BlockId    = 0;
  face->NextFace   = 0;
  face->PolygonId  = 0;
  face->FragmentId = 0;

  return face;
}


//============================================================================


class vtkRectilinearGridConnectivityFaceHash
{
public:
  vtkRectilinearGridConnectivityFaceHash();
  ~vtkRectilinearGridConnectivityFaceHash();

  // Returns the number of faces in the hash (faces returned by iteration).
  vtkIdType GetNumberOfFaces() { return this->NumberOfFaces; }

  // Creates a hash that expects at most "numberOfPoint" points as indexes
  void Initialize( vtkIdType numberOfPoints );

  // These methods ass a face to the hash.  The four point method is for
  // convenience. The points do not need to be sorted. If this is a new face,
  // this method constructs the object and adds it to the hash. The cell id is
  // set and the face is returned. If this face is already in the hash, the
  // object is removed from the hash and is returned.  The face is automatically
  // recycled, but is valid until this method is called again.
  // Note: I will assume that the ptIds are unique (i.e. pt1 != pt2 ...)
  vtkRectilinearGridConnectivityFace * AddFace
                               ( vtkIdType pt1, vtkIdType pt2, vtkIdType pt3 );
  vtkRectilinearGridConnectivityFace * AddFace( vtkIdType pt1, vtkIdType pt2,
                                                vtkIdType pt3, vtkIdType pt4);
  vtkRectilinearGridConnectivityFace * AddFace( vtkIdType pt1, vtkIdType pt2,
                                 vtkIdType pt3, vtkIdType pt4, vtkIdType pt5 );

  // A way to iterate over the faces in the hash.
  void InitTraversal();
  vtkRectilinearGridConnectivityFace * GetNextFace();

  // Since the face does not store the first point id explicitley,
  // this returns the id from the iterator state.
  vtkIdType GetFirstPointIndex() { return this->IteratorIndex; }

private:

  // Keep track of the number of faces in the hash for convenience.
  // The user does not need to iterate over all faces to count them.
  vtkIdType  NumberOfFaces;

  // Array indexed by faces smallest corner id.
  // Each element is a linked list of faces that share the point.
  vtkIdType  NumberOfPoints;
  vtkRectilinearGridConnectivityFace ** Hash;

  // Allocates faces efficiently.
  vtkRectilinearGridConnectivityFaceHeap * Heap;

  // This class is too simple and internal for a separate iterator object.
  vtkIdType  IteratorIndex;
  vtkRectilinearGridConnectivityFace * IteratorCurrent;
};

vtkRectilinearGridConnectivityFaceHash::vtkRectilinearGridConnectivityFaceHash()
{
  this->Hash = 0;
  this->Heap = new vtkRectilinearGridConnectivityFaceHeap;

  this->NumberOfPoints  = 0;
  this->NumberOfFaces   = 0;
  this->IteratorIndex   = -1;
  this->IteratorCurrent = 0;
}

vtkRectilinearGridConnectivityFaceHash::~vtkRectilinearGridConnectivityFaceHash()
{
  if ( this->Hash )
    {
    delete [] this->Hash;
    this->Hash = 0;
    }

  delete this->Heap;
  this->Heap = 0;

  this->IteratorIndex   = 0;
  this->IteratorCurrent = 0;
  this->NumberOfFaces   = 0;
}

void vtkRectilinearGridConnectivityFaceHash::InitTraversal()
{
  // I had a decision: current points to the last or next face.
  // I chose last because it simplifies InitTraveral.
  // I just initialize the IteratorIndex as -1 (special value).
  // This assume that vtkIdType is signed!
  this->IteratorIndex   = -1;
  this->IteratorCurrent = 0;
}

vtkRectilinearGridConnectivityFace * vtkRectilinearGridConnectivityFaceHash
  ::GetNextFace()
{
  if ( this->IteratorIndex >= this->NumberOfPoints )
    {
    // past the end of the hash --- it has not been initialized.
    return 0;
    }

  // traverse the linked list.
  if ( this->IteratorCurrent )
    {
    this->IteratorCurrent = this->IteratorCurrent->NextFace;
    }

  // move through heap to find the next linked list if we hit the end of
  // the current linked list,
  while ( this->IteratorCurrent == 0 )
    {
    this->IteratorIndex ++;
    if ( this->IteratorIndex >= this->NumberOfPoints )
      {
      return 0;
      }

    this->IteratorCurrent = this->Hash[ this->IteratorIndex ];
    }

  return this->IteratorCurrent;
}

void vtkRectilinearGridConnectivityFaceHash::Initialize
  ( vtkIdType numberOfPoints )
{
  if ( this->Hash )
    {
    vtkGenericWarningMacro( "You can only initialize once.\n" );
    return;
    }

  this->Hash = new vtkRectilinearGridConnectivityFace * [ numberOfPoints ];
  this->NumberOfPoints = numberOfPoints;

  memset(  this->Hash,  0,  sizeof( vtkRectilinearGridConnectivityFace * )
                          * numberOfPoints  );
}

vtkRectilinearGridConnectivityFace * vtkRectilinearGridConnectivityFaceHash
  ::AddFace( vtkIdType pt1, vtkIdType pt2, vtkIdType pt3 )
{
  // Sort the three ids. Assume the three ids are unique (actually not)
  vtkIdType tmp;
  if ( pt2 < pt1 )
    {
    tmp = pt1;
    pt1 = pt2;
    pt2 = tmp;
    }

  if ( pt3 < pt1 )
    {
    tmp = pt1;
    pt1 = pt3;
    pt3 = tmp;
    }

  if ( pt3 < pt2 )
    {
    tmp = pt2;
    pt2 = pt3;
    pt3 = tmp;
    }

  // Note: we do not check if the point index is out of bounds.

  // look for the face in the hash and keep old reference for editing
  vtkRectilinearGridConnectivityFace ** ref = this->Hash + pt1;
  vtkRectilinearGridConnectivityFace *  face = *ref;
  while ( face )
    {
    if ( face->PointId2 == pt2 && face->PointId3 == pt3 )
      {
      // find the face and remove it from the hash
      *ref = face->NextFace;
      face->NextFace = 0;
      this->Heap->RecycleFace( face );
      this->NumberOfFaces --;

      // the face will still be valid until the next cycle.
      return face;
      }
    ref  = &face->NextFace;
    face =  face->NextFace;
    }
  // this is a new face.
  face = this->Heap->NewFace();
  face->PointId2 = pt2;
  face->PointId3 = pt3;

  // add the face to the hash.
  *ref = face;
  this->NumberOfFaces ++;

  return face;
}

vtkRectilinearGridConnectivityFace * vtkRectilinearGridConnectivityFaceHash
  ::AddFace( vtkIdType pt1, vtkIdType pt2, vtkIdType pt3, vtkIdType pt4 )
{
  // we just need to find the three smallest point ids
  if ( pt1 > pt2 && pt1 > pt3 && pt1 > pt4 )
    {
    return this->AddFace( pt2, pt3, pt4 );
    }

  if ( pt2 > pt3 && pt2 > pt4 )
    {
    return this->AddFace( pt1, pt3, pt4 );
    }

  if ( pt3 > pt4 )
    {
    return this->AddFace( pt1, pt2, pt4 );
    }

  return this->AddFace( pt1, pt2, pt3 );
}

vtkRectilinearGridConnectivityFace * vtkRectilinearGridConnectivityFaceHash
  ::AddFace( vtkIdType pt1, vtkIdType pt2,
             vtkIdType pt3, vtkIdType pt4, vtkIdType pt5 )
{
  vtkIdType tempIndx;
  vtkIdType pntIndxs[5] = { pt1, pt2, pt3, pt4, pt5 };

  // determine the two largest point Ids
  for ( int j = 0;     j < 2; j ++ )
  for ( int i = j + 1; i < 5; i ++ )
    {
    if ( pntIndxs[i] > pntIndxs[j] )
      {
      tempIndx    = pntIndxs[i];
      pntIndxs[i] = pntIndxs[j];
      pntIndxs[j] = tempIndx;
      }
    }

  // but submit the three smallest point Ids
  return this->AddFace( pntIndxs[2], pntIndxs[3], pntIndxs[4] );
}


// ============================================================================
// ======================== Supporting Classes ( end ) ========================
// ============================================================================


//-----------------------------------------------------------------------------
vtkRectilinearGridConnectivity::vtkRectilinearGridConnectivity()
{
  this->FaceHash          = NULL;
  this->DualGridBlocks    = NULL;
  this->NumberOfBlocks    = 0;
  this->DualGridsReady    = 0;
  this->DataBlocksTime    = -1.0;
  this->DualGridBounds[0] =
  this->DualGridBounds[2] =
  this->DualGridBounds[4] = VTK_DOUBLE_MAX;
  this->DualGridBounds[1] =
  this->DualGridBounds[3] =
  this->DualGridBounds[5] = VTK_DOUBLE_MIN;
  this->EquivalenceSet    = NULL;
  this->FragmentValues    = NULL;

  this->Controller = vtkMultiProcessController::GetGlobalController();

  this->Internal   = new vtkRectilinearGridConnectivityInternal;
  this->Internal->ComponentNumbersObtained = 0;
  this->Internal->NumberIntegralComponents = 0;
  this->Internal->VolumeFractionArraysType = 0;
  this->Internal->ComponentNumbersPerArray.clear();
  this->Internal->VolumeFractionArrayNames.clear();
  this->Internal->VolumeDataAttributeNames.clear();
  this->Internal->IntegrableAttributeNames.clear();
  this->Internal->VolumeFractionValueScale = 255.0;

  this->VolumeFractionSurfaceValue = 128.0 / 255.0;
}

//-----------------------------------------------------------------------------
vtkRectilinearGridConnectivity::~vtkRectilinearGridConnectivity()
{
  this->Controller = NULL;

  if ( this->Internal )
    {
    this->Internal->ComponentNumbersPerArray.clear();
    this->Internal->VolumeFractionArrayNames.clear();
    this->Internal->VolumeDataAttributeNames.clear();
    this->Internal->IntegrableAttributeNames.clear();
    delete this->Internal;
    this->Internal = NULL;
    }

  if ( this->FaceHash )
    {
    delete this->FaceHash;
    this->FaceHash = NULL;
    }

  if ( this->EquivalenceSet )
    {
    this->EquivalenceSet->Delete();
    this->EquivalenceSet = NULL;
    }

  if ( this->FragmentValues )
    {
    this->FragmentValues->Delete();
    this->FragmentValues = NULL;
    }

  if ( this->DualGridBlocks && this->NumberOfBlocks )
    {
    for ( int i = 0; i < this->NumberOfBlocks; i ++ )
      {
      this->DualGridBlocks[i]->Delete();
      this->DualGridBlocks[i] = NULL;
      }
    delete [] this->DualGridBlocks;
    this->DualGridBlocks = NULL;
    }
}

//-----------------------------------------------------------------------------
vtkExecutive * vtkRectilinearGridConnectivity::CreateDefaultExecutive()
{
  return vtkCompositeDataPipeline::New();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::RemoveAllVolumeArrayNames()
{
  this->Internal->VolumeFractionArrayNames.erase
                  ( this->Internal->VolumeFractionArrayNames.begin(),
                    this->Internal->VolumeFractionArrayNames.end() );
  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::RemoveDoubleVolumeArrayNames()
{
  if ( this->Internal->VolumeFractionArraysType != VTK_DOUBLE )
    {
    return;
    }

  this->Internal->VolumeFractionArrayNames.erase
                  ( this->Internal->VolumeFractionArrayNames.begin(),
                    this->Internal->VolumeFractionArrayNames.end() );
  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::RemoveFloatVolumeArrayNames()
{
  if ( this->Internal->VolumeFractionArraysType != VTK_FLOAT )
    {
    return;
    }

  this->Internal->VolumeFractionArrayNames.erase
                  ( this->Internal->VolumeFractionArrayNames.begin(),
                    this->Internal->VolumeFractionArrayNames.end() );
  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::RemoveUnsignedCharVolumeArrayNames()
{
  if ( this->Internal->VolumeFractionArraysType != VTK_UNSIGNED_CHAR )
    {
    return;
    }

  this->Internal->VolumeFractionArrayNames.erase
                  ( this->Internal->VolumeFractionArrayNames.begin(),
                    this->Internal->VolumeFractionArrayNames.end() );
  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::AddVolumeArrayName( char * arayName )
{
  if( arayName == 0 )
    {
    return;
    }

  this->Internal->VolumeFractionArraysType = 0;
  this->Internal->VolumeFractionArrayNames.push_back( arayName );
  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::AddDoubleVolumeArrayName( char * arayName )
{
  if( arayName == 0 )
    {
    return;
    }

  if ( this->Internal->VolumeFractionArraysType != VTK_DOUBLE )
    {
    this->RemoveAllVolumeArrayNames();
    this->Internal->VolumeFractionArraysType = VTK_DOUBLE;
    }

  this->Internal->VolumeFractionArrayNames.push_back( arayName );
  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::AddFloatVolumeArrayName( char * arayName )
{
  if( arayName == 0 )
    {
    return;
    }

  if ( this->Internal->VolumeFractionArraysType != VTK_FLOAT )
    {
    this->RemoveAllVolumeArrayNames();
    this->Internal->VolumeFractionArraysType = VTK_FLOAT;
    }

  this->Internal->VolumeFractionArrayNames.push_back( arayName );
  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::AddUnsignedCharVolumeArrayName
  ( char * arayName )
{
  if( arayName == 0 )
    {
    return;
    }

  if ( this->Internal->VolumeFractionArraysType != VTK_UNSIGNED_CHAR )
    {
    this->RemoveAllVolumeArrayNames();
    this->Internal->VolumeFractionArraysType = VTK_UNSIGNED_CHAR;
    }

  this->Internal->VolumeFractionArrayNames.push_back( arayName );
  this->Modified();
}

//-----------------------------------------------------------------------------
int vtkRectilinearGridConnectivity::GetNumberOfVolumeArrays()
{
  return static_cast<int>( this->Internal->VolumeDataAttributeNames.size() );
}

//-----------------------------------------------------------------------------
int vtkRectilinearGridConnectivity::GetNumberOfVolumeFractionArrays()
{
  return static_cast<int>( this->Internal->VolumeFractionArrayNames.size() );
}

//-----------------------------------------------------------------------------
const char* vtkRectilinearGridConnectivity::GetVolumeFractionArrayName
  ( int arrayIdx )
{
  if (  arrayIdx < 0 || arrayIdx >= static_cast<int>
                        ( this->Internal->VolumeFractionArrayNames.size() )
     )
    {
    return 0;
    }
  return this->Internal->VolumeFractionArrayNames[ arrayIdx ].c_str();
}

//-----------------------------------------------------------------------------
bool vtkRectilinearGridConnectivity::IsVolumeArray( const char * arayName )
{
  int   i;
  int   numArrays = static_cast<int>
                    ( this->Internal->VolumeDataAttributeNames.size() );

  for ( i = 0; i < numArrays; i ++ )
    {
    if (  !strcmp( arayName,
                   this->Internal->VolumeDataAttributeNames[i].c_str() )  )
      {
      return true;
      }
    }

  return false;
}

//-----------------------------------------------------------------------------
bool   vtkRectilinearGridConnectivity::IsVolumeFractionArray( const char * arayName )
{
  int   i;
  int   numArrays = static_cast<int>
                    ( this->Internal->VolumeFractionArrayNames.size() );

  for ( i = 0; i < numArrays; i ++ )
    {
    if (  !strcmp( arayName,
                   this->Internal->VolumeFractionArrayNames[i].c_str() )  )
      {
      return true;
      }
    }

  return false;
}

//-----------------------------------------------------------------------------
int vtkRectilinearGridConnectivity::FillInputPortInformation
  ( int port, vtkInformation * info )
{
  if(  !this->Superclass::FillInputPortInformation( port, info )  )
    {
    return 0;
    }

  info->Set( vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject" );

  return  1;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::PrintSelf( ostream & os, vtkIndent indent )
{
  this->Superclass::PrintSelf( os, indent );

  os << indent << "Volume Fraction Surface Value: "
               << this->VolumeFractionSurfaceValue << "\n";
  os << indent << "Dual Grids Ready: " << this->DualGridsReady    << "\n";
  os << indent << "Number of Blocks: " << this->NumberOfBlocks    << "\n";
  os << indent << "Data Blocks Time: " << this->DataBlocksTime    << "\n";
  os << indent << "Dual Grid Bounds: " << this->DualGridBounds[0] << ", "
                                       << this->DualGridBounds[1] << "; "
                                       << this->DualGridBounds[2] << ", "
                                       << this->DualGridBounds[3] << "; "
                                       << this->DualGridBounds[4] << ", "
                                       << this->DualGridBounds[5] << ".\n";
}

//-----------------------------------------------------------------------------
int vtkRectilinearGridConnectivity::CheckVolumeDataArrays
  ( vtkRectilinearGrid ** recGrids, int numGrids )
{
  if ( !recGrids || numGrids <= 0 )
    {
    vtkErrorMacro( << "vtkRectilinearGrid array NULL or numGrids <= 0 " << endl );
    return 0;
    }

  int            i, j;
  int            arayType =-1;
  int            tempType = 0;
  int            beNormal = 1;
  int            numFracs = 0;
  int            numArays = 0;
  const char  ** aryNames = NULL;
  const char   * arayName = NULL;
  vtkDataArray * cellAray = NULL;


  // check the number of arrays and specific array names

  if (  ( numArays = recGrids[0]->GetCellData()->GetNumberOfArrays() ) <
        ( numFracs = this->GetNumberOfVolumeFractionArrays() )
     )
    {
    vtkErrorMacro( << "Insufficient number of cell data arrays" << endl );
    return 0;
    }

  for ( i = 0; i < numFracs; i ++ )
    {
    arayName = this->GetVolumeFractionArrayName( i );
    if (  recGrids[0]->GetCellData()->GetArray( arayName )  ==  NULL  )
      {
      arayName = NULL;
      vtkErrorMacro( << "Cell data array " << arayName << " not found." << endl );
      return 0;
      }
    arayName = NULL;
    }

  aryNames = new const char * [ numArays ];
  for ( i = 0; i < numArays; i ++ )
    {
    aryNames[i] = recGrids[0]->GetCellData()->GetArrayName( i );
    }

  for ( j = 1; j < numGrids && beNormal; j ++ )
    {
    beNormal = !( recGrids[j]->GetCellData()->GetNumberOfArrays() - numArays );

    for ( i = 0; i < numArays && beNormal; i ++ )
      {
      beNormal = !strcmp(  aryNames[i],
                           recGrids[j]->GetCellData()->GetArrayName( i )  );
      }
    }

  if ( !beNormal )
    {
    for ( i = 0; i < numArays; i ++ )
      {
      aryNames[i] = NULL;
      }
    delete [] aryNames;
    aryNames = NULL;

    vtkErrorMacro( << "Blocks inconsistent in the number of arrays "
                   << "or array names." << endl );
    return 0;
    }


  // check the volume fraction array(s)

  for ( j = 0; j < numFracs && beNormal ; j ++ )
    {
    arayName = this->GetVolumeFractionArrayName( j );
    for ( i = 0; i < numGrids; i ++ )
      {
      cellAray = recGrids[i]->GetCellData()->GetArray( arayName );
      tempType = cellAray->GetDataType();
      if ( tempType != VTK_FLOAT  &&
           tempType != VTK_DOUBLE &&
           tempType != VTK_UNSIGNED_CHAR
         )
        {
        beNormal = 0;
        vtkErrorMacro( << "Data type expected to be VTK_DOUBLE, VTK_FLOAT "
                       << "or VTK_UNSIGNED_CHAR." << endl );
        break;
        }

      if ( arayType < 0 )
        {
        arayType = tempType;
        this->Internal->VolumeFractionValueScale =
                        ( tempType == VTK_UNSIGNED_CHAR ) ? 255.0 : 1.0;
        }

      if ( arayType >= 0 && arayType != tempType )
        {
        beNormal = 0;
        vtkErrorMacro( << "Volume fraction arrays inconsistent in the "
                       << "data type" << endl );
        break;
        }
      cellAray = NULL;
      }
    arayName = NULL;
    }

  if ( beNormal && this->Internal->VolumeDataAttributeNames.empty() )
    {
    for ( i = 0; i < numArays; i ++ )
      {
      if (  strcmp( aryNames[i], "vtkGhostLevels" )  )
        {
        // note that 'vtkGhostLevels' is a hidden data array
        this->Internal->VolumeDataAttributeNames.push_back( aryNames[i] );

        if (  !strstr( aryNames[i], "raction" ) &&
              !this->IsVolumeFractionArray( aryNames[i] )
           )
          {
          this->Internal->IntegrableAttributeNames.push_back( aryNames[i] );
          }
        }
      }
    }

  for ( i = 0; i < numArays; i ++ )
    {
    aryNames[i] = NULL;
    }
  delete [] aryNames;
  aryNames = NULL;

  return beNormal;
}

//-----------------------------------------------------------------------------
int vtkRectilinearGridConnectivity::RequestData( vtkInformation *,
  vtkInformationVector ** inputVector, vtkInformationVector * outputVector )
{
  // check the number of volume fraction arrays
  if ( this->GetNumberOfVolumeFractionArrays() == 0 )
    {
    vtkWarningMacro( << "At least one volume fraction array expected for "
                     << "extracting fragments." << endl );
    return 0;
    }


  // check the output
  vtkInformation       * outInfor = outputVector->GetInformationObject( 0 );
  vtkMultiBlockDataSet * outputMB = vtkMultiBlockDataSet::SafeDownCast
                         (  outInfor->Get( vtkDataObject::DATA_OBJECT() )  );
  if ( !outputMB )
    {
    outInfor = NULL;
    vtkErrorMacro( << "Output vtkMultiBlockDataSet NULL." << endl );
    return 0;
    }


  // check the input
  int                   inputIdx = 0;
  int                   numBlcks = 0;
  vtkRectilinearGrid ** recGrids = NULL;
  vtkInformation      * inputInf = inputVector[0]->GetInformationObject( 0 );
  vtkDataObject       * pDataObj = inputInf->Get( vtkDataObject::DATA_OBJECT() );
  vtkCompositeDataSet * cdsInput = vtkCompositeDataSet::SafeDownCast( pDataObj );
  vtkRectilinearGrid  * recInput = vtkRectilinearGrid::SafeDownCast ( pDataObj );
  vtkCompositeDataIterator * cdIterat = NULL;

  if ( recInput )
    {
    // a vtkRectilinearGrid dataset
    numBlcks    = 1;
    recGrids    = new vtkRectilinearGrid * [1];
    recGrids[0] = recInput;
    }
  else
  if ( cdsInput )
    {
    // a vtkComposisteDataset that may contain some vtkRectilinearGrid blocks

    // obtain the number of vtkRectilinearGrid blocks
    numBlcks = 0;
    cdIterat = cdsInput->NewIterator();
    cdIterat->GoToFirstItem();
    while ( !cdIterat->IsDoneWithTraversal() )
      {
      if (  vtkRectilinearGrid::SafeDownCast
            ( cdIterat->GetCurrentDataObject() )  )
        {
        numBlcks ++;
        }
      cdIterat->GoToNextItem();
      }

    // allocate an array to store these vtkRectilinearGrid blocks
    recGrids = new vtkRectilinearGrid * [ numBlcks ];
    cdIterat->GoToFirstItem();
    while ( !cdIterat->IsDoneWithTraversal() )
      {
      pDataObj = cdIterat->GetCurrentDataObject();
      recInput = vtkRectilinearGrid::SafeDownCast( pDataObj );
      if ( recInput )
        {
        recGrids[ inputIdx ++ ] = recInput;
        }
      else
      if ( pDataObj )
        {
        vtkWarningMacro( << "Filed to handle block of type "
                         << pDataObj->GetClassName()
                         << " --- block skipped." << endl );
        }

      cdIterat->GoToNextItem();
      }

    cdIterat->Delete();
    cdIterat = NULL;
    }
  else
  if ( pDataObj )
    {
    // the input dataset is neither vtkCompositeDataSet nor vtkRectilinearGrid
    inputInf = NULL;
    outInfor = NULL;
    pDataObj = NULL;
    cdsInput = NULL;
    recInput = NULL;
    vtkErrorMacro( << "Failed to handle dataset of type "
                   << pDataObj->GetClassName() << endl );
    return 0;
    }


  // obtain the current time step to determine if the dual grids are ready
  double     timeStep = 0.0;
  inputInf = pDataObj->GetInformation();
  if (  inputInf  &&  inputInf->Has( vtkDataObject::DATA_TIME_STEP() )  )
    {
    timeStep = inputInf->Get( vtkDataObject::DATA_TIME_STEP() );
    }
  else
  if ( outInfor &&
       outInfor->Has( vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP() )
     )
    {
    timeStep = outInfor->
             Get( vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP() );
    }

  if ( timeStep == this->DataBlocksTime )
    {
    this->DualGridsReady = 1;
    }
  else
    {
    this->DualGridsReady = 0;
    this->DataBlocksTime = timeStep;
    }

  inputInf = NULL;
  outInfor = NULL;
  pDataObj = NULL;
  cdsInput = NULL;
  recInput = NULL;


  // create vtkPolyData objects to be attached to the output
  int            i;
  int            numParts = this->GetNumberOfVolumeFractionArrays();
  vtkPolyData ** theParts = new vtkPolyData * [ numParts ];
  outputMB->SetNumberOfBlocks( numParts );
  for ( i = 0; i < numParts; i ++ )
    {
    theParts[i] = vtkPolyData::New();
    outputMB->SetBlock( i, theParts[i] );
    }
  outputMB = NULL;


  // note that some processes may not be assigned with any block if there are
  // more processes than the blocks and in this case nothing can be  done
  // except for asking the (remote) process to send an empty vtkPolyData to
  // the root process which is always waiting for a fixed number of results
  // from the remote processes
  if ( !recGrids || !numBlcks )
    {
    // recGrids might be NULL and numBlcks might be zero in multi-process mode

    if ( this->Controller->GetLocalProcessId() &&     // a root process
         this->Controller->GetNumberOfProcesses() > 1 // multi-process
      )
      {
      for ( i = 0; i < numParts; i ++ )
        {
        this->Controller->Send( theParts[i], 0, 9890831 + i );
        }
      }

    for ( i = 0; i < numParts; i ++ )
      {
      theParts[i]->Delete();
      theParts[i] = NULL;
      }
    delete [] theParts;
    theParts = NULL;

    return 1;
    }


  // If the time step has been updated (new data blocks have been loaded, which
  // invalidates the existing dual grids), we need to check the volume arrays,
  // update the dual grids, and obtain the bounding box.
  if ( !this->DualGridsReady )
    {
    // verify the consistent volume data arrays contained in all blocks and
    // obtain the data type of the volume fraction arrays

    if (  this->CheckVolumeDataArrays( recGrids, numBlcks ) == 0  )
      {
      for ( inputIdx = 0; inputIdx < numBlcks; inputIdx ++ )
        {
        recGrids[ inputIdx ] = NULL;
        }
      delete [] recGrids;
      recGrids = NULL;

      for ( i = 0; i < numParts; i ++ )
        {
        theParts[i]->Delete();
        theParts[i] = NULL;
        }
      delete [] theParts;
      theParts = NULL;

      vtkErrorMacro( << "Error with volume data arrays --- Fragments extraction "
                     << "cancelled." << endl );
      return 0;
      }

    // update the dual-grid block(s) and obtain the bounding box, if necessary

    // destroy the obsolete dual grid block(s)
    if ( this->DualGridBlocks && this->NumberOfBlocks )
      {
      for ( i = 0; i < this->NumberOfBlocks; i ++ )
        {
        this->DualGridBlocks[i]->Delete();
        this->DualGridBlocks[i] = NULL;
        }
      delete [] this->DualGridBlocks;
      this->DualGridBlocks = NULL;
      }

    // allocate a new array of dual grids
    this->DualGridsReady    = 1;
    this->NumberOfBlocks    = numBlcks;
    this->DualGridBlocks    = new vtkRectilinearGrid * [ numBlcks ];

    // clear the bounding box
    double       * rcBounds = NULL;
    this->DualGridBounds[0] =
    this->DualGridBounds[2] =
    this->DualGridBounds[4] = VTK_DOUBLE_MAX;
    this->DualGridBounds[1] =
    this->DualGridBounds[3] =
    this->DualGridBounds[5] = VTK_DOUBLE_MIN;

    // compute the bounidng box and create the dual grids
    for ( i = 0; i < numBlcks; i ++ )
      {
      rcBounds = recGrids[i]->GetBounds();
      this->DualGridBounds[0] = ( rcBounds[0] < this->DualGridBounds[0] )
                                ? rcBounds[0] : this->DualGridBounds[0];
      this->DualGridBounds[2] = ( rcBounds[2] < this->DualGridBounds[2] )
                                ? rcBounds[2] : this->DualGridBounds[2];
      this->DualGridBounds[4] = ( rcBounds[4] < this->DualGridBounds[4] )
                                ? rcBounds[4] : this->DualGridBounds[4];
      this->DualGridBounds[1] = ( rcBounds[1] > this->DualGridBounds[1] )
                                ? rcBounds[1] : this->DualGridBounds[1];
      this->DualGridBounds[3] = ( rcBounds[3] > this->DualGridBounds[3] )
                                ? rcBounds[3] : this->DualGridBounds[3];
      this->DualGridBounds[5] = ( rcBounds[5] > this->DualGridBounds[5] )
                                ? rcBounds[5] : this->DualGridBounds[5];
      rcBounds = NULL;

      this->DualGridBlocks[i] = vtkRectilinearGrid::New();
      this->CreateDualRectilinearGrid( recGrids[i], this->DualGridBlocks[i] );
      }
    }


  // deallocate the pointers to the original grid blocks (with cell data)
  for ( i = 0; i < numBlcks; i ++ )
    {
    recGrids[i] = NULL;
    }
  delete [] recGrids;
  recGrids = NULL;


  // extract fragments based on the volume fraction array(s)
  for ( i = 0; i < numParts; i ++ )
    {
    this->ExtractFragments( this->DualGridBlocks, numBlcks,
                            this->DualGridBounds, i, theParts[i] );
    theParts[i]->Delete();
    theParts[i] = NULL;
    }
  delete [] theParts;
  theParts = NULL;

  return 1;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::ExtractFragments
  ( vtkRectilinearGrid ** dualGrds, int numBlcks, double boundBox[6],
    unsigned char partIndx, vtkPolyData * polyData )
{
  if ( !dualGrds || numBlcks <= 0 || !polyData ||
       !this->GetVolumeFractionArrayName( partIndx )
     )
    {
    vtkErrorMacro( << "Input vtkRectilinearGrid array (dualGrds) or output "
                   << "vtkPolyData (polyData) NULL, invalid number of blocks "
                   << "or invalid volume fraction array name." << endl );
    return;
    }


  int            i;
  int          * maxFsize = NULL;
  vtkPolyData ** surfaces = NULL;
  vtkPolyData  * plyHedra = NULL;
  vtkPoints    * mbPoints = NULL;
  vtkIncrementalOctreePointLocator * mbPntLoc = NULL;

  mbPoints = vtkPoints::New();
  mbPntLoc = vtkIncrementalOctreePointLocator::New();
  mbPntLoc->SetTolerance( 0.0001 );
  mbPntLoc->InitPointInsertion( mbPoints, boundBox, 20000 );


  // Process each vtkRectilinearGrid dataset and extract individual greater-
  // than-isovalue sub-volumes (polyhedra in the form of vtkPolyData) on a
  // per-cube basis and write the result to the corresponding vtkPolyData.


  maxFsize = new int[ numBlcks ];
  surfaces = new vtkPolyData * [ numBlcks ];
  for ( i = 0; i < numBlcks; i ++ )
    {
    plyHedra    = vtkPolyData::New();
    surfaces[i] = vtkPolyData::New();

    // perform marching cubes on the dual grid to obtain the greater-than-
    // isovalue polyhedra, of which each 2D polygon is assigned with a global
    // volume Id
    this->ExtractFragmentPolyhedra( dualGrds[i],
          this->GetVolumeFractionArrayName( partIndx ),
          this->VolumeFractionSurfaceValue *
          this->Internal->VolumeFractionValueScale, plyHedra );

    // # clear and re-init EquivalenceSet
    // # clear and re-init the face hash with the number of points contained
    //   in the polyhedra
    // # add each face of the polyhedra to the face hash, with the block-based
    //   local point Id as the face hash entry / index, assign it with the face
    //   index (in the polyhedra, via PolygonId) for late access to the original
    //   2D polygon in the polyhedra, and assign it with the volume index (in
    //   the polyhedra, via VolumeId)
    // # resolve the polygons of the polyhedra in the face hash
    // # obtain the remaining / exterior faces from the face hash and group them
    //   based on the local (block-based) fragment Id
    // # Given each exterior face extracted from the face hash, gain access to
    //   the original 2D polygon in the polyhedra, insert it to the output
    //   vtkPolyData. The points are also inserted to the output polygon and
    //   a global Id is assigned to each point as the point data attribute
    this->ExtractFragmentPolygons
          ( i, maxFsize[i], plyHedra, surfaces[i], mbPntLoc );

    plyHedra->Delete();
    plyHedra = NULL;
    }


  // The equivalenceSet keeps track of fragment ids and determines which
  // fragment ids need to be combined into a single fragment.
  if ( this->EquivalenceSet )
    {
    this->EquivalenceSet->Delete();
    this->EquivalenceSet = NULL;
    }
  this->EquivalenceSet = vtkEquivalenceSet::New();

  // Allocate a vtkDoubleArray to maintain the attributes of each fragment
  if ( this->FragmentValues )
    {
    this->FragmentValues->Delete();
    this->FragmentValues = NULL;
    }
  this->FragmentValues = vtkDoubleArray::New();
  this->FragmentValues->SetNumberOfComponents
        ( this->Internal->NumberIntegralComponents + 1 );// material volume

  this->InitializeFaceHash( surfaces, numBlcks );
  this->AddPolygonsToFaceHash( surfaces, maxFsize, numBlcks );
  this->ResolveEquivalentFragments();
  this->GenerateOutputFromSingleProcess
        ( surfaces, numBlcks, partIndx, polyData );

  // memory deallocation
  mbPntLoc->Delete();
  mbPoints->Delete();
  mbPntLoc = NULL;
  mbPoints = NULL;

  delete [] maxFsize;
  maxFsize = NULL;

  for ( i = 0; i < numBlcks; i ++ )
    {
    surfaces[i]->Delete();
    surfaces[i] = NULL;
    }
  delete [] surfaces;
  surfaces = NULL;


  // So far each process (either a remote process or the root process) has
  // processed the block(s), if any (some processes may not be assigned with
  // any blocks at all), that are assigned to it. Exterior surfaces of the
  // extracted fragments, if any, are stored in 'polyData' (possibly empty).


  // ------------------------------------------------------------ //
  // -------- Let's consinder inter-process issues below -------- //
  // ------------------------------------------------------------ //


  int  procIndx = 0;
  int  numProcs = this->Controller->GetNumberOfProcesses();
  if ( numProcs > 1 )
    {
    if ( this->Controller->GetLocalProcessId() != 0 )
      {
      // this is a remote process
      this->Controller->Send( polyData, 0, 9890831 + partIndx );
      polyData->Initialize();
      }
    else
      {
      // this is the root process

      // NOTE: Since this is the root process collecting the extraction results
      // from remote processes, argument 'boundBox' (only specific to the root
      // process) is not valid any more for combining these multiple results.
      // we need to compute the global bounding box covering all the extraction
      // results. An invalid bounding box would crash the point locator.

      // allocate an array of vtkPolyData objects (tempPlys for computing the
      // global bounding box, otherwise it would not be allocated as an array)
      maxFsize = new int[ numProcs ]; // max fragment size
      vtkPolyData   ** tempPlys = new vtkPolyData * [ numProcs ];
      vtkPolyData   ** procPlys = new vtkPolyData * [ numProcs ];
      for ( i = 0; i < numProcs; i ++ )
        {
        tempPlys[i] = vtkPolyData::New();
        procPlys[i] = vtkPolyData::New();
        }

      // collect the extraction results from the remote processes
      tempPlys[0]->DeepCopy( polyData );
      polyData->Initialize();
      for ( procIndx = 1; procIndx < numProcs; procIndx ++ )
        {
        this->Controller->Receive
                          ( tempPlys[procIndx], procIndx, 9890831 + partIndx );
        }

      // obtain the global bounding box (note that vtkPolyData objects provided
      // by some processes including the root process might be just empty)
      double  * localBox    = NULL;
      double    globalBB[6] = { VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
                                VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
                                VTK_DOUBLE_MAX, VTK_DOUBLE_MIN
                              };
      for ( i = 0; i < numProcs; i ++ )
        {
        if ( tempPlys[i]->GetNumberOfPoints() )
          {
          localBox    = tempPlys[i]->GetBounds();
          globalBB[0] = ( localBox[0] < globalBB[0] ) ? localBox[0] : globalBB[0];
          globalBB[2] = ( localBox[2] < globalBB[2] ) ? localBox[2] : globalBB[2];
          globalBB[4] = ( localBox[4] < globalBB[4] ) ? localBox[4] : globalBB[4];
          globalBB[1] = ( localBox[1] > globalBB[1] ) ? localBox[1] : globalBB[1];
          globalBB[3] = ( localBox[3] > globalBB[3] ) ? localBox[3] : globalBB[3];
          globalBB[5] = ( localBox[5] > globalBB[5] ) ? localBox[5] : globalBB[5];
          localBox    = NULL;
          }
        }

      // create a global point locator used to assign unique point Ids for
      // combining fragments extracted from multiple processes
      mbPoints = vtkPoints::New();
      mbPntLoc = vtkIncrementalOctreePointLocator::New();
      mbPntLoc->SetTolerance( 0.0001 );
      mbPntLoc->InitPointInsertion( mbPoints, globalBB );

      // generate inter-process vtkPolyData objects for merging fragments
      for ( procIndx = 0; procIndx < numProcs; procIndx ++ )
        {
        this->CreateInterProcessPolygons( tempPlys[procIndx], procPlys[procIndx],
                                          mbPntLoc,           maxFsize[procIndx] );
        tempPlys[procIndx]->Delete();
        tempPlys[procIndx] = NULL;
        }
      delete  [] tempPlys;
      tempPlys = NULL;

      // create an equivalence set for removing multiple fragments
      if ( this->EquivalenceSet )
        {
        this->EquivalenceSet->Delete();
        this->EquivalenceSet = NULL;
        }
      this->EquivalenceSet = vtkEquivalenceSet::New();

      // allocate a vtkDoubleArray to maintain the attributes of each fragment
      if ( this->FragmentValues )
        {
        this->FragmentValues->Delete();
        this->FragmentValues = NULL;
        }
      this->FragmentValues = vtkDoubleArray::New();
      this->FragmentValues->SetNumberOfComponents
            ( this->Internal->NumberIntegralComponents + 1 );// material volume

      // execute the pipeline of inter-process faces resolution
      this->InitializeFaceHash( procPlys, numProcs );
      this->AddInterProcessPolygonsToFaceHash( procPlys, maxFsize, numProcs );
      this->ResolveEquivalentFragments();
      this->GenerateOutputFromMultiProcesses
            ( procPlys, numProcs, partIndx, polyData );

      // memory deallocation specific to the inter-process module
      mbPntLoc->Delete();
      mbPoints->Delete();
      mbPntLoc = NULL;
      mbPoints = NULL;

      for ( i = 0; i < numProcs; i ++ )
        {
        procPlys[i]->Delete();
        procPlys[i] = NULL;
        }
      delete [] procPlys;
      delete [] maxFsize;
      procPlys = NULL;
      maxFsize = NULL;
      }
    }


  // memory deallocation
  if ( this->FaceHash )
    {
    delete this->FaceHash;
    this->FaceHash = NULL;
    }

  if ( this->EquivalenceSet )
    {
    this->EquivalenceSet->Delete();
    this->EquivalenceSet = NULL;
    }

  if ( this->FragmentValues )
    {
    this->FragmentValues->Delete();
    this->FragmentValues = NULL;
    }
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::CreateDualRectilinearGrid
  ( vtkRectilinearGrid * rectGrid, vtkRectilinearGrid * dualGrid )
{
  if ( !rectGrid || !dualGrid )
    {
    vtkErrorMacro( << "Input rectGrid or output dualGrid NULL." << endl );
    return;
    }

  int               i, j, k, m, n;
  int               numArays;
  int               jCellInc;
  int               kCellInc;
  int               rcShiftJ;
  int               rcShiftK;
  int               rCellIdx;  // Rectilinear CELL InDeX
  int               dPntIndx;  // Dual grid PoiNT INDeX
  int            *  numComps;
  int               rectDims[3];
  int               dualDims[3];
  double            theCords[2];
  double            tempCord;
  double         *  xSpacing = NULL;
  double         *  ySpacing = NULL;
  double         *  zSpacing = NULL;
  vtkDataArray   *  rXcoords = NULL;
  vtkDataArray   *  rYcoords = NULL;
  vtkDataArray   *  rZcoords = NULL;
  vtkDataArray   ** rcArrays = NULL;
  vtkDoubleArray *  dXcoords = NULL;
  vtkDoubleArray *  dYcoords = NULL;
  vtkDoubleArray *  dZcoords = NULL;
  vtkDoubleArray ** dpArrays = NULL;
  vtkDoubleArray *  dVolumes = NULL;

  // get the input grid
  rectGrid->GetDimensions( rectDims );
  rXcoords = rectGrid->GetXCoordinates();
  rYcoords = rectGrid->GetYCoordinates();
  rZcoords = rectGrid->GetZCoordinates();

  // For dual vtkRectilinearGrids, the cells between grid line (gridDim
  // - 3) and grid line (gridDim - 2) of the former half and the cells
  // between grid line 0 and grid line 1 of the latter half are at the
  // ghost level. In other words, there are two grid lines or one row of
  // cells at the ghost level. Without skipping these cells, the polygons
  // resulting from marching cubes over them for the former and the latter
  // would be sent to the face hash twice and then be mistakenly removed
  // as internal faces to leave seams. To address this issue, the size of
  // the dual grid needs to be gridDim - 2.

  // create the output grid
  dualDims[0] = rectDims[0] - 2;
  dualDims[1] = rectDims[1] - 2;
  dualDims[2] = rectDims[2] - 2;
  dXcoords    = vtkDoubleArray::New();
  dYcoords    = vtkDoubleArray::New();
  dZcoords    = vtkDoubleArray::New();
  dVolumes    = vtkDoubleArray::New();
  xSpacing    = new double [ dualDims[0] ];
  ySpacing    = new double [ dualDims[1] ];
  zSpacing    = new double [ dualDims[2] ];

  // array of x coordinates
  dXcoords->SetNumberOfComponents( 1 );
  dXcoords->SetNumberOfTuples( dualDims[0] );
  tempCord = rXcoords->GetComponent( 0, 0 );
  for ( i = 0; i < dualDims[0]; i ++ )
    {
    theCords[0] = tempCord;
    theCords[1] = tempCord = rXcoords->GetComponent( i + 1, 0 );
    xSpacing[i] = theCords[1] - theCords[0];
    dXcoords->SetComponent(  i,  0,  ( theCords[0] + theCords[1] ) * 0.5  );
    }

  // array of y coordinates
  dYcoords->SetNumberOfComponents( 1 );
  dYcoords->SetNumberOfTuples( dualDims[1] );
  tempCord = rYcoords->GetComponent( 0, 0 );
  for ( i = 0; i < dualDims[1]; i ++ )
    {
    theCords[0] = tempCord;
    theCords[1] = tempCord = rYcoords->GetComponent( i + 1, 0 );
    ySpacing[i] = theCords[1] - theCords[0];
    dYcoords->SetComponent(  i,  0,  ( theCords[0] + theCords[1] ) * 0.5  );
    }

  // array of z coordinates
  dZcoords->SetNumberOfComponents( 1 );
  dZcoords->SetNumberOfTuples( dualDims[2] );
  tempCord = rZcoords->GetComponent( 0, 0 );
  for ( i = 0; i < dualDims[2]; i ++ )
    {
    theCords[0] = tempCord;
    theCords[1] = tempCord = rZcoords->GetComponent( i + 1, 0 );
    zSpacing[i] = theCords[1] - theCords[0];
    dZcoords->SetComponent(  i,  0,  ( theCords[0] + theCords[1] ) * 0.5  );
    }

  // gain access to the cell data arrays of the original grid and use them to
  // create point data arrays attached to the dual grid
  numArays = rectGrid->GetCellData()->GetNumberOfArrays();
  numComps = new int [ numArays ];
  rcArrays = new vtkDataArray   * [ numArays ];
  dpArrays = new vtkDoubleArray * [ numArays ];
  for ( i = 0; i < numArays; i ++ )
    {
    rcArrays[i] = rectGrid->GetCellData()->GetArray( i );
    numComps[i] = rcArrays[i]->GetNumberOfComponents();
    dpArrays[i] = vtkDoubleArray::New();
    dpArrays[i]->SetName( rcArrays[i]->GetName() );
    dpArrays[i]->SetNumberOfComponents( numComps[i] );
    dpArrays[i]->SetNumberOfTuples( dualDims[0] * dualDims[1] * dualDims[2] );
    }

  // create an array of geomtric volumes (of the cells of the original grid)
  // as the point data of the dual grid
  dVolumes->SetName( "GeometricVolume" );
  dVolumes->SetNumberOfComponents( 1 );
  dVolumes->SetNumberOfTuples( dualDims[0] * dualDims[1] * dualDims[2] );

  dPntIndx = 0;
  rCellIdx = 0;
  rcShiftJ = 0;
  rcShiftK = 0;
  jCellInc = ( rectDims[0] - 1 );
  kCellInc = ( rectDims[0] - 1 ) * ( rectDims[1] - 1 );
  for ( k = 0, rCellIdx = 0, dPntIndx = 0; k < dualDims[2];
        k  ++, rcShiftK+= kCellInc )
  for ( j = 0, rcShiftJ = 0; j < dualDims[1]; j ++, rcShiftJ += jCellInc )
  for ( i = 0; i < dualDims[0]; i ++, dPntIndx ++ )
    {
    rCellIdx = rcShiftK + rcShiftJ + i;
    dVolumes->SetComponent( dPntIndx,  0,  xSpacing[i]
                          * ySpacing[j] *  zSpacing[k] );

    for ( m = 0; m < numArays;    m ++ )
    for ( n = 0; n < numComps[m]; n ++ )
      {
      dpArrays[m]->SetComponent(  dPntIndx,  n,
                                  rcArrays[m]->GetComponent( rCellIdx, n )  );
      }
    }

  // set the dual grid
  dualGrid->SetDimensions( dualDims );
  dualGrid->SetXCoordinates( dXcoords );
  dualGrid->SetYCoordinates( dYcoords );
  dualGrid->SetZCoordinates( dZcoords );
  dualGrid->GetPointData()->AddArray( dVolumes );
  for ( i = 0; i < numArays; i ++ )
    {
    dualGrid->GetPointData()->AddArray( dpArrays[i] );
    dpArrays[i]->Delete();
    dpArrays[i] = NULL;
    rcArrays[i] = NULL;
    }
  delete [] dpArrays;
  delete [] rcArrays;
  delete [] numComps;
  dpArrays = NULL;
  rcArrays = NULL;
  numComps = NULL;

  // memory de-allocation
  dXcoords->Delete();
  dYcoords->Delete();
  dZcoords->Delete();
  dVolumes->Delete();
  delete [] xSpacing;
  delete [] ySpacing;
  delete [] zSpacing;
  dXcoords = NULL;
  dYcoords = NULL;
  dZcoords = NULL;
  dVolumes = NULL;
  xSpacing = NULL;
  ySpacing = NULL;
  zSpacing = NULL;
  rXcoords = NULL;
  rYcoords = NULL;
  rZcoords = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::ExtractFragmentPolyhedra
  ( vtkRectilinearGrid * rectGrid, const char * fracName,
    double isoValue, vtkPolyData * plyHedra )
{
  if (   !rectGrid || !plyHedra ||
         !this->Internal->IntegrablePointDataArraysAvailable( rectGrid ) ||
          vtkDoubleArray::SafeDownCast
          (  rectGrid->GetPointData()->GetArray( fracName )  ) == NULL
       || vtkDoubleArray::SafeDownCast
          (  rectGrid->GetPointData()->GetArray( "GeometricVolume" )  ) == NULL
     )
    {
    vtkErrorMacro( << "Input vtkRectilinearGrid, point data GeometricVolume, "
                   << "integrable point data arrays, or output vtkPolyData "
                   << "NULL." << endl );
    return;
    }


  // IIP: Interpolated Iso-value Point --- the on-edge iso-value point obtained
  //      via interpolation. Each IIP is indicated by the associated edge index
  //      (0 ~ 11) in the LUT. In contrast, each vertex of the cube is referred
  //      to, in the LUT, by an index (12 ~ 19) that is a translated version of
  //      the original index (0 ~ 7, in the cube).

  int               i, j, k, m, n, a, c;
  int               numArays;
  int               numCells;    // number of polygons of an MC sub-volume
  int               vtxIndex;    // vertices referenced by an MC sub-volume
  int               tmpPtIds[3]; // three point-Ids of a quad
  int               valuAdrs[4]; // address of a value in an array
  int               dataDims[3];
  int               numbHits[8]; // number of hits of a vertex on a sub-volume
  int               vtxIndxs[2];
  int               pntReady[20];    // point coordinates ready?
  int               lutPtIdx = 0;
  int               nPlyPnts = 0;    // number of points forming a polygon
  int               estiSize = 0;
  int               caseIndx = 0;
  int               sliceSiz = 0;
  int               pntKindx = 0;
  int               pntJindx = 0;
  int               pntIndex = 0;
  int               volIndex = 0;
  int             * numComps = NULL; // number of components
  int             * volPtIds = NULL;
  double          * volFracs = NULL;
  double          * geomVols = NULL; // geometric volumes of the original cells
  double         ** ptValues = NULL; // arrays / pointer
  double        *** lastVals = NULL; // arrays / components / 8 nodes
  double        *** nodeVals = NULL; // arrays / components / 8 nodes
  double        *** acumVals = NULL; // accumulated values a vertex scatters
  double         ** integVal = NULL; // integrated attributes of a sub-volume
  double            integVol = 0.0;  // integrated attribute of a sub-volume
  double            interplt = 0.0;  // fraction value used for interpolation
  double            dataBbox[6];
  double            acumVols[8];     // accumulated volume a vertex scatters
  double            lastFrcs[8];     // to reuse quad scalars (1, 2, 5, 6 only)
  double            nodeFrcs[8];     // fractions of the ORIGINAL hexas (noe nodes)
  double            lastVols[8];     // to reuse --- similar to lastFrcs
  double            nodeVols[8];     // volumes of the ORIGINAL hexas (now nodes)
  double            lastCord[3];
  double            vtxCords[8][3];  // VerTeX (0 ~ 7)
  double            pntCords[20][3]; // IIPs (0 ~ 11) and vertices (12 ~ 19)
  vtkIdType         cellIdxs[12];    // polygon-/face-indices of a sub-volume
  vtkIdType         plyPtIds[5];     // PointT IDs of a PoLYgon
  vtkIdType         cellIndx = 0;
  vtkPoints       * surfPnts = NULL;
  vtkDataArray    * pXcoords = NULL;
  vtkDataArray    * pYcoords = NULL;
  vtkDataArray    * pZcoords = NULL;
  vtkCellArray    * surfaces = NULL;
  vtkIdTypeArray  * uniVIdxs = NULL;
  vtkDoubleArray  * mVolumes = NULL; // material volumes: SIGMA(fraction * volume)
  vtkDoubleArray ** volArays = NULL; // arrays / pointer
  vtkIncrementalOctreePointLocator * pntAdder = NULL;

  static  int      EDGEVTXS[12][2] = { { 0, 1 }, { 1, 2 }, { 3, 2}, { 0, 3 },
                                       { 4, 5 }, { 5, 6 }, { 7, 6}, { 4, 7 },
                                       { 0, 4 }, { 1, 5 }, { 3, 7}, { 2, 6 }
                                     }; // two VerTeXS (vertices) of an EDGE

  static  int      CASEMASK[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
  vtkRectilinearGridConnectivityMarchingCubesVolumeCases * volCases =
  vtkRectilinearGridConnectivityMarchingCubesVolumeCases::GetCases();


  // gain access to arrays of the 3D coordinates and point data arrays (material
  // volume fraction, non-fraction attributes, and geometric volume) and create
  // a set of vtkDoubleArray objects for integration of all data attributes, of
  // which each might have multiple components
  pXcoords = rectGrid->GetXCoordinates();
  pYcoords = rectGrid->GetYCoordinates();
  pZcoords = rectGrid->GetZCoordinates();
  volFracs = vtkDoubleArray::SafeDownCast
             (  rectGrid->GetPointData()->GetArray( fracName )  )->GetPointer(0);
  geomVols = vtkDoubleArray::SafeDownCast
             (  rectGrid->GetPointData()->GetArray( "GeometricVolume" )  )
              ->GetPointer(0);

  numArays = int( this->Internal->IntegrableAttributeNames.size() );
  numComps = new int       [ numArays ];
  ptValues = new double  * [ numArays ];
  integVal = new double  * [ numArays ];
  acumVals = new double ** [ numArays ];
  lastVals = new double ** [ numArays ];
  nodeVals = new double ** [ numArays ];
  for ( a = 0; a < numArays; a ++ )
    {
    vtkDoubleArray  *  tempAray =
    vtkDoubleArray::SafeDownCast
                    (  rectGrid->GetPointData()->GetArray
                       ( this->Internal->IntegrableAttributeNames[a].c_str() )
                    );
    ptValues[a] = tempAray->GetPointer( 0 );
    numComps[a] = tempAray->GetNumberOfComponents();
    integVal[a] = new double   [ numComps[a] ];
    acumVals[a] = new double * [ numComps[a] ];
    lastVals[a] = new double * [ numComps[a] ];
    nodeVals[a] = new double * [ numComps[a] ];
    for ( c = 0; c < numComps[a]; c ++ )
      {
      acumVals[a][c] = new double [ 8 ];
      lastVals[a][c] = new double [ 8 ];
      nodeVals[a][c] = new double [ 8 ];
      }
    tempAray    = NULL;
    }
  if ( this->Internal->ComponentNumbersObtained == 0 )
    {
    this->Internal->ComponentNumbersObtained = 1;
    this->Internal->NumberIntegralComponents = 0;
    for ( a = 0; a < numArays; a ++ )
      {
      this->Internal->NumberIntegralComponents += numComps[a];
      this->Internal->ComponentNumbersPerArray.push_back( numComps[a] );
      }
    }


  // create a vtkPoints for all the points of the fragment surfaces
  rectGrid->GetBounds( dataBbox );
  rectGrid->GetDimensions( dataDims );
  sliceSiz = dataDims[0] * dataDims[1];
  estiSize = sliceSiz    * dataDims[2];
  estiSize = estiSize / 1024 * 1024;
  estiSize = ( estiSize < 1024 ) ? 1024 : estiSize;
  surfPnts = vtkPoints::New();
  surfPnts->Allocate( estiSize, estiSize >> 1 );

  // create a vtkIncrementalOctreePointLocator and attach it to the vtkPoints
  // such that the point locator will reject duplicates as points are inserted
  // to the vtkPoints. From now on the point locator serves as a proxy of the
  // vtkPoints to collect 3D points.
  pntAdder = vtkIncrementalOctreePointLocator::New();
  pntAdder->SetTolerance( 0.0001 );
  pntAdder->InitPointInsertion( surfPnts, dataBbox, estiSize );

  // create a vtkCellArray for the surfaces of greater-than-isovalue sub-volumes
  surfaces = vtkCellArray::New();
  surfaces->Allocate( estiSize, estiSize >> 1 );

  // Create a vtkIdTypeArray for the global volume Ids assigned to the surfaces.
  // In fact the volume Ids might not necessarily be global since their ultimate
  // goal is to allow AddPolygonsToFaceHash() to determine which polygons / faces
  // form a volume.
  uniVIdxs = vtkIdTypeArray::New();
  uniVIdxs->SetName( "VolumeId" );
  uniVIdxs->Allocate( estiSize, estiSize >> 1 );

  // create a vtkDoubleArray of material volumes for the surfaces
  mVolumes = vtkDoubleArray::New();
  mVolumes->SetName( "MaterialVolume" );
  mVolumes->Allocate( estiSize, estiSize >> 1 );

  // create vtkDoubleArray objects to integrate non-fraction volume arrays and
  volArays = new vtkDoubleArray * [ numArays ];
  for ( a = 0; a < numArays; a ++ )
    {
    volArays[a] = vtkDoubleArray::New();
    volArays[a]->SetName( this->Internal->IntegrableAttributeNames[a].c_str() );
    volArays[a]->SetNumberOfComponents( numComps[a] );
    volArays[a]->Allocate( estiSize, estiSize >> 1 );
    }


  // marching cubes to create surfaces for the greater-than-isovalue sub-volumes
  pntKindx    =-sliceSiz;
  lastCord[2] = pZcoords->GetComponent( 0, 0 ); // for reusing z-coordinate
  for ( k = 0; k < dataDims[2] - 1; k ++ )
    {
    pntKindx      += sliceSiz;
    vtxCords[0][2] = lastCord[2];
    vtxCords[6][2] = lastCord[2] = pZcoords->GetComponent( k + 1, 0 );

    pntJindx    =-dataDims[0];
    lastCord[1] = pYcoords->GetComponent( 0, 0 ); // for reusing y-coordinate
    for ( j = 0; j < dataDims[1] - 1; j ++ )
      {
      pntJindx      += dataDims[0];
      vtxCords[0][1] = lastCord[1];
      vtxCords[6][1] = lastCord[1] = pYcoords->GetComponent( j + 1, 0 );

      lastCord[0]    = pXcoords->GetComponent( 0, 0 ); // for reusing x-coordinate

      // The attribute values at the vertices of the beginning quad on the
      // current row are obtained here in support of reusing them on a per quad
      // basis while marching cubes along a row. This beginning quad is taken as
      // the right quad of the "previous" cube on the row. Note only #1, #2, #5,
      // and #6 are used while 8 units are allocated for easy access purposes.
      pntIndex    = pntKindx + pntJindx + 0; // i = 0: the starting quad
      tmpPtIds[0] = pntIndex + dataDims[0];
      tmpPtIds[1] = pntIndex + sliceSiz;
      tmpPtIds[2] = pntIndex + sliceSiz + dataDims[0];

      lastFrcs[1] = volFracs[ pntIndex    ];
      lastFrcs[2] = volFracs[ tmpPtIds[0] ];
      lastFrcs[5] = volFracs[ tmpPtIds[1] ];
      lastFrcs[6] = volFracs[ tmpPtIds[2] ];

      lastVols[1] = geomVols[ pntIndex    ];
      lastVols[2] = geomVols[ tmpPtIds[0] ];
      lastVols[5] = geomVols[ tmpPtIds[1] ];
      lastVols[6] = geomVols[ tmpPtIds[2] ];

      for ( a = 0; a < numArays; a ++ )
        {
        valuAdrs[0] = numComps[a] * pntIndex;
        valuAdrs[1] = numComps[a] * tmpPtIds[0];
        valuAdrs[2] = numComps[a] * tmpPtIds[1];
        valuAdrs[3] = numComps[a] * tmpPtIds[2];
        for ( c = 0; c < numComps[a]; c ++ )
          {
          lastVals[a][c][1] = ptValues[a][ valuAdrs[0] + c ];
          lastVals[a][c][2] = ptValues[a][ valuAdrs[1] + c ];
          lastVals[a][c][5] = ptValues[a][ valuAdrs[2] + c ];
          lastVals[a][c][6] = ptValues[a][ valuAdrs[3] + c ];
          }
        }


      for ( i = 0; i < dataDims[0] - 1; i ++ )
        {
        // obtain the attribute values at the cube's eight vertices

        // obtain the left quad of the current cube by reusing
        // the right quad of the previous cube on the this row
        nodeFrcs[0] = lastFrcs[1];
        nodeFrcs[3] = lastFrcs[2];
        nodeFrcs[4] = lastFrcs[5];
        nodeFrcs[7] = lastFrcs[6];

        nodeVols[0] = lastVols[1];
        nodeVols[3] = lastVols[2];
        nodeVols[4] = lastVols[5];
        nodeVols[7] = lastVols[6];

        for ( a = 0; a < numArays;    a ++ )
        for ( c = 0; c < numComps[a]; c ++ )
          {
          nodeVals[a][c][0] = lastVals[a][c][1];
          nodeVals[a][c][3] = lastVals[a][c][2];
          nodeVals[a][c][4] = lastVals[a][c][5];
          nodeVals[a][c][7] = lastVals[a][c][6];
          }

        // gain access to the right quad (in x axis) of the current cube
        pntIndex    = pntKindx + pntJindx + i + 1;  // 1: the right quad
        tmpPtIds[0] = pntIndex + dataDims[0];
        tmpPtIds[1] = pntIndex + sliceSiz;
        tmpPtIds[2] = pntIndex + sliceSiz + dataDims[0];

        // obtain the right quad of the current cube by accessing the data
        // array and update the buffer of the right quad for the next cube
        nodeFrcs[1] = lastFrcs[1] = volFracs[ pntIndex    ];
        nodeFrcs[2] = lastFrcs[2] = volFracs[ tmpPtIds[0] ];
        nodeFrcs[5] = lastFrcs[5] = volFracs[ tmpPtIds[1] ];
        nodeFrcs[6] = lastFrcs[6] = volFracs[ tmpPtIds[2] ];

        nodeVols[1] = lastVols[1] = geomVols[ pntIndex    ];
        nodeVols[2] = lastVols[2] = geomVols[ tmpPtIds[0] ];
        nodeVols[5] = lastVols[5] = geomVols[ tmpPtIds[1] ];
        nodeVols[6] = lastVols[6] = geomVols[ tmpPtIds[2] ];

        for ( a = 0; a < numArays; a ++ )
          {
          valuAdrs[0] = numComps[a] * pntIndex;
          valuAdrs[1] = numComps[a] * tmpPtIds[0];
          valuAdrs[2] = numComps[a] * tmpPtIds[1];
          valuAdrs[3] = numComps[a] * tmpPtIds[2];
          for ( c = 0; c < numComps[a]; c ++ )
            {
            nodeVals[a][c][1] = lastVals[a][c][1] = ptValues[a][ valuAdrs[0] + c ];
            nodeVals[a][c][2] = lastVals[a][c][2] = ptValues[a][ valuAdrs[1] + c ];
            nodeVals[a][c][5] = lastVals[a][c][5] = ptValues[a][ valuAdrs[2] + c ];
            nodeVals[a][c][6] = lastVals[a][c][6] = ptValues[a][ valuAdrs[3] + c ];
            }
          }

        // update the x-coordinates of #0 (the near) and #6 (the far)
        // NOTE: the following two lines MUST be above the 'continue' switch
        // as they are used to transfer point coordinates for reuse purposes
        // (the transfer must not be interrupted even if the cube is skipped)
        vtxCords[0][0] = lastCord[0];
        vtxCords[6][0] = lastCord[0] = pXcoords->GetComponent( i + 1, 0 );

        // determine the case index
        for ( caseIndx = 0, m = 0; m < 8; m ++ )
          {
          if ( nodeFrcs[m] >= isoValue )
            {
            caseIndx |= CASEMASK[m];
            }
          }

        // early exit unless there is any greater-than-isovalue sub-volume
        // OR this is a ghost-level cell
        if ( caseIndx == 0 )
          {
          continue;
          }

        // get the 3D coordinates of the six vertices
        // #0 (the near) and #6 (the far) have been assigned above
        vtxCords[1][0] = vtxCords[6][0];
        vtxCords[1][1] = vtxCords[0][1];
        vtxCords[1][2] = vtxCords[0][2];

        vtxCords[2][0] = vtxCords[6][0];
        vtxCords[2][1] = vtxCords[6][1];
        vtxCords[2][2] = vtxCords[0][2];

        vtxCords[3][0] = vtxCords[0][0];
        vtxCords[3][1] = vtxCords[6][1];
        vtxCords[3][2] = vtxCords[0][2];

        vtxCords[4][0] = vtxCords[0][0];
        vtxCords[4][1] = vtxCords[0][1];
        vtxCords[4][2] = vtxCords[6][2];

        vtxCords[5][0] = vtxCords[6][0];
        vtxCords[5][1] = vtxCords[0][1];
        vtxCords[5][2] = vtxCords[6][2];

        vtxCords[7][0] = vtxCords[0][0];
        vtxCords[7][1] = vtxCords[6][1];
        vtxCords[7][2] = vtxCords[6][2];

        // todo: add code here to compute normals / gradients

        // Fill pntCords[12] ~ pntCords[19] with vtxCords and set their flags.
        // Note that we store the IIPs and vertices in a single array of 3D
        // coordinates to avoid intense if-statements. The array begins with
        // 12 IIPs (0 ~ 11), followed by 8 vertices (12 ~ 19).
        for ( m = 0; m < 8; m ++ )
          {
          pntReady[ m + 12 ]    = 1;
          pntCords[ m + 12 ][0] = vtxCords[m][0];
          pntCords[ m + 12 ][1] = vtxCords[m][1];
          pntCords[ m + 12 ][2] = vtxCords[m][2];

          // clear the hit counters and value-scattering buckets
          numbHits[m] = 0;
          acumVols[m] = 0.0;
          for ( a = 0; a < numArays;    a ++ )
          for ( c = 0; c < numComps[a]; c ++ )
            {
            acumVals[a][c][m] = 0.0;
            }
          }

        // Clear the IIP flags --- what we really care about via pntReady.
        // This means that the coordinates of the to-be-referenced IIPs
        // need to be computed when they are first referenced.
        for ( m = 0; m < 12; m ++ )
          {
          pntReady[m] = 0;
          }

        // gain access to the target LUT entry
        volPtIds = ( volCases + caseIndx )->PointIds;

        // process each ploygon (either an iso-triangle or a cube face)
        // that is described in this LUT entry
        m = 0;
        numCells = 0; // clear the number of polygons of this sub-volume
        while ( volPtIds[m] != -2 ) // flag -1 never comes to this line
          {
          // get the number of points forming a polygon (<= 5)
          nPlyPnts = volPtIds[ m ++ ];

          // access each point (either an IIP or a vertex) of the polygon
          for ( n = 0; n < nPlyPnts; n ++ )
            {
            // get the internal (LUT-based) index of this point
            lutPtIdx = volPtIds[ m ++ ];

            // Obtain the coordinates of an IIP if it is still unavailable.
            // Note that only an IIP's coordinates might be unknown since
            // those of the 8 vertices have been determined above as their
            // flags indicate.
            if ( pntReady[ lutPtIdx ] == 0 )
              {
              // now lutPtIdx is guaranteed to fall within [0, 11]

              // obtain the iso-value point coordinates via interpolation
              pntReady[lutPtIdx] = 1;
              vtxIndxs[0] = EDGEVTXS[lutPtIdx][0];
              vtxIndxs[1] = EDGEVTXS[lutPtIdx][1];
              interplt    =
                (  isoValue                - nodeFrcs[ vtxIndxs[0] ]  )  /
                (  nodeFrcs[ vtxIndxs[1] ] - nodeFrcs[ vtxIndxs[0] ]  );

              pntCords[lutPtIdx][0]
                = vtxCords[ vtxIndxs[0] ][0] + interplt *
                ( vtxCords[ vtxIndxs[1] ][0] - vtxCords[ vtxIndxs[0] ][0] );
              pntCords[lutPtIdx][1]
                = vtxCords[ vtxIndxs[0] ][1] + interplt *
                ( vtxCords[ vtxIndxs[1] ][1] - vtxCords[ vtxIndxs[0] ][1] );
              pntCords[lutPtIdx][2]
                = vtxCords[ vtxIndxs[0] ][2] + interplt *
                ( vtxCords[ vtxIndxs[1] ][2] - vtxCords[ vtxIndxs[0] ][2] );
              }

            // let the vertex scatter the attribute values to the sub-volume
            if ( lutPtIdx >= 12 )
              {
              vtxIndex= lutPtIdx - 12;
              numbHits[ vtxIndex ] ++;
              double    theVolum    = nodeFrcs[ vtxIndex ] * nodeVols[ vtxIndex ];
              acumVols[ vtxIndex ] += theVolum;
              for ( a = 0; a < numArays;    a ++ )
              for ( c = 0; c < numComps[a]; c ++ )
                {
                acumVals[a][c][ vtxIndex ] +=
                nodeVals[a][c][ vtxIndex ] * theVolum;
                }
              }

            // If possible, insert this point to the vtkPoints and assign it
            // with a global Id as the point data attribute.
            pntAdder->InsertUniquePoint( pntCords[lutPtIdx], plyPtIds[n] );
            } // end of accessing each point of the polygon

          // Now that the hybrid points (IIPs and vertices, forming a polygon)
          // have been inserted to the vtkPoints, let's insert the polygon.
          // Even though this may be a degenerate polygon, we still need to
          // keep it, which will be then sent to the face hash for combining
          // sub-volumes to create a single fragment. Rejection of degenerate
          // polygons may lead to wrong fragment extraction as they separate
          // two sub-volumes, preventing them from being combined together.
          cellIndx = surfaces->InsertNextCell( nPlyPnts, plyPtIds );

          // attach the volume Id as a cell data value to this polygon
          uniVIdxs->InsertValue( cellIndx, volIndex );

          // record the cell index for deferred subvolume-dependent attribute
          // integration, of which the result will be assigned to such a cell
          cellIdxs[ numCells ++ ] = cellIndx;

          // handle flag -1 (to proceed with a new volume)
          if ( volPtIds[m] == -1 )
            {
            m ++;
            volIndex ++;

            // initialize the attribute integration results
            integVol = 0.0;
            for ( a = 0; a < numArays;    a ++ )
            for ( c = 0; c < numComps[a]; c ++ )
              {
              integVal[a][c] = 0.0;
              }

            // collect the attribute values from the buckets
            for ( n = 0; n < 8; n ++ )
              {
              if ( numbHits[n] )
                {
                double   hitsNumb = 1.0 / numbHits[n];
                integVol += acumVols[n] * hitsNumb;
                for ( a = 0; a < numArays;    a ++ )
                for ( c = 0; c < numComps[a]; c ++ )
                  {
                  integVal[a][c] += acumVals[a][c][n] * hitsNumb;
                  }
                }

              // clear the hit counters and value-scattering buckets for the
              // next sub-volume
              numbHits[n] = 0;
              acumVols[n] = 0.0;
              for ( a = 0; a < numArays;    a ++ )
              for ( c = 0; c < numComps[a]; c ++ )
                {
                acumVals[a][c][n] = 0.0;
                }
              }

            // normalize the sum (one grid point is shared by 8 cells -- 0.125)
            integVol *= 0.125;
            for ( a = 0; a < numArays;    a ++ )
            for ( c = 0; c < numComps[a]; c ++ )
              {
              integVal[a][c] *= 0.125;
              }

            // assign the integration values to each surface of the sub-volume
            for ( n = 0; n < numCells; n ++ )
              {
              mVolumes->InsertValue( cellIdxs[n], integVol );
              for ( a = 0; a < numArays; a ++ )
                {
                volArays[a]->InsertTupleValue( cellIdxs[n], integVal[a] );
                }
              }

            // clear the number of polygons for the next sub-volume
            numCells = 0;
            }
          } // end while ( volPtIds[m] != -2 )

        // flag -2 (the LUT entry end) means that we have just got a new volume
        // please each LUT entry (except for entry #0: no any sub-volume is
        // extracted) should have a sub-volume extracted.
        volIndex ++;

        // initialize the attribute integration results
        integVol = 0.0;
        for ( a = 0; a < numArays;    a ++ )
        for ( c = 0; c < numComps[a]; c ++ )
          {
          integVal[a][c] = 0.0;
          }

        // collect the attribute values from the buckets
        for ( n = 0; n < 8; n ++ )
          {
          if ( numbHits[n] )
            {
            double   hitsNumb = 1.0 / numbHits[n];
            integVol += acumVols[n] * hitsNumb;
            for ( a = 0; a < numArays;    a ++ )
            for ( c = 0; c < numComps[a]; c ++ )
              {
              integVal[a][c] += acumVals[a][c][n] * hitsNumb;
              }
            }
          }

        // normalize the sum (one grid point is shared by 8 cells -- 0.125)
        integVol *= 0.125;
        for ( a = 0; a < numArays;    a ++ )
        for ( c = 0; c < numComps[a]; c ++ )
          {
          integVal[a][c] *= 0.125;
          }

        // assign the integration values to each surface of the sub-volume
        for ( n = 0; n < numCells; n ++ )
          {
          mVolumes->InsertValue( cellIdxs[n], integVol );
          for ( a = 0; a < numArays; a ++ )
            {
            volArays[a]->InsertTupleValue( cellIdxs[n], integVal[a] );
            }
          }

        } // for each i
      } // for each j
    } // for each k

  // fill the output vtkPolyData
  plyHedra->SetPoints( surfPnts );
  plyHedra->SetPolys ( surfaces );
  plyHedra->GetCellData()->SetGlobalIds ( uniVIdxs );
  plyHedra->GetCellData()->AddArray( mVolumes );
  for ( a = 0; a < numArays; a ++ )
    {
    plyHedra->GetCellData()->AddArray( volArays[a] );
    }
  plyHedra->Squeeze();


  // memory de-allocation
  for ( a = 0; a < numArays; a ++ )
    {
    for ( c = 0; c < numComps[a]; c ++ )
      {
      delete [] lastVals[a][c];
      delete [] nodeVals[a][c];
      delete [] acumVals[a][c];
      lastVals[a][c] = NULL;
      nodeVals[a][c] = NULL;
      acumVals[a][c] = NULL;
      }
    delete [] lastVals[a];
    delete [] nodeVals[a];
    delete [] acumVals[a];
    delete [] integVal[a];
    volArays[a]->Delete();
    lastVals[a] = NULL;
    nodeVals[a] = NULL;
    acumVals[a] = NULL;
    integVal[a] = NULL;
    volArays[a] = NULL;
    ptValues[a] = NULL;
    }
  delete [] numComps;
  delete [] lastVals;
  delete [] nodeVals;
  delete [] acumVals;
  delete [] integVal;
  delete [] volArays;
  delete [] ptValues;
  numComps = NULL;
  lastVals = NULL;
  nodeVals = NULL;
  acumVals = NULL;
  integVal = NULL;
  volArays = NULL;
  ptValues = NULL;

  surfPnts->Delete();
  surfaces->Delete();
  pntAdder->Delete();
  uniVIdxs->Delete();
  mVolumes->Delete();
  surfPnts = NULL;
  surfaces = NULL;
  pntAdder = NULL;
  uniVIdxs = NULL;
  mVolumes = NULL;

  volPtIds = NULL;
  volFracs = NULL;
  geomVols = NULL;
  pXcoords = NULL;
  pYcoords = NULL;
  pZcoords = NULL;
  volCases = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::InitializeFaceHash( vtkPolyData * plyHedra )
{
  if ( !plyHedra )
    {
    vtkErrorMacro( << "vtkPolyData NULL." << endl );
    return;
    }

  if ( this->FaceHash )
    {
    delete this->FaceHash;
    this->FaceHash = NULL;
    }

  int   hashSize = plyHedra->GetPoints()->GetNumberOfPoints();
  hashSize = ( hashSize < 1 ) ? 1 : hashSize;

  this->FaceHash = new vtkRectilinearGridConnectivityFaceHash;
  this->FaceHash->Initialize( hashSize );
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::AddPolygonsToFaceHash
  ( int blockIdx, vtkPolyData * plyHedra )
{
  // process the vtkPolyData and add its 2D polygons (faces) to the hash

  // make sure the vtkPolyData (set of polyhedra) contains cell data attributes
  // global volume Ids and material volume
  if (    plyHedra == NULL
       || !this->Internal->IntegrableCellDataArraysAvailable( plyHedra )
       || vtkIdTypeArray::SafeDownCast
          (  plyHedra->GetCellData()->GetArray( "VolumeId" )  ) == NULL
       || vtkDoubleArray::SafeDownCast
          (  plyHedra->GetCellData()->GetArray( "MaterialVolume" )  ) == NULL
     )
    {
    vtkErrorMacro( << "Input vtkPolyData (plyHedra), cell data VolumeId "
                   << "or MaterialVolume NULL, or integrable cell data "
                   << "attributes not found from the input." << endl );
    return;
    }


  int              i, a, c;
  int              procIndx = this->Controller->GetLocalProcessId();
  int              theShift = 0;
  int              bufIndex = 0;
  int              numArays = 0;
  int              tupleSiz = 0; // number of integrated components
  int              newIndex = 0; // index of a new face
  int              fragIndx = 1; // next fragment Id and 0 for removing faces
  int              minIndex = 1; // the smallest fragment Id so far
  int            * numComps = NULL; // number of integrated components
  double         * tupleBuf = NULL; // integrated component values
  double        ** attrPtrs = NULL;
  vtkCell        * thisFace = NULL; // a 2D polygon (instead of a 3D cell)
  vtkIdType        numFaces = 0; // number of 2D polygons in a vtkPolyData
  vtkIdType        volIndex = 0; // global volume Id attached to a 2D polygon
  vtkIdType      * vIdxsPtr = NULL; // array of global volume Ids
  vtkDoubleArray * theArray = NULL;
  vtkRectilinearGridConnectivityFace * hashFace = NULL; // a face in the hash
  vtkRectilinearGridConnectivityFace * newFaces[VTK_MAX_FACES_PER_CELL];
  for ( i = 0; i < VTK_MAX_FACES_PER_CELL; i ++ ) newFaces[i] = NULL;


  // determine the number of integrated components (including the material
  // volume) to be saved to the global fragment attributes array and
  // allocate a buffer for a tuple
  tupleSiz = this->Internal->NumberIntegralComponents + 1;
  tupleBuf = new double [ tupleSiz ];
  memset(  tupleBuf,  0,  sizeof( double ) * tupleSiz  );

  // gain access to the arrays of integrated attributes
  numArays    = int( this->Internal->IntegrableAttributeNames.size() ) + 1;
  numComps    = new int      [ numArays ];
  attrPtrs    = new double * [ numArays ];
  numComps[0] = 1;
  attrPtrs[0] = vtkDoubleArray::SafeDownCast
                (  plyHedra->GetCellData()->GetArray( "MaterialVolume" )  )
                ->GetPointer( 0 );
  for ( a = 1; a < numArays; a ++ )
    {
    theArray    = vtkDoubleArray::SafeDownCast
                  (  plyHedra->GetCellData()->GetArray
                     ( this->Internal->IntegrableAttributeNames[ a - 1 ].c_str() )
                  );
    attrPtrs[a] = theArray->GetPointer( 0 );
    numComps[a] = theArray->GetNumberOfComponents();
    theArray    = NULL;
    }


  // the vtkPolyData stores separated 2D polygons (triangles, quadrilaterlas,
  // and pentagons, of which each is though coupled with a global volume Id as
  // the cell data attribute to convey the connectivity of the polygons of the
  // same volume) to represent the surface of each greater-than-isovalue sub-
  // volume.
  vIdxsPtr = vtkIdTypeArray::SafeDownCast
             (  plyHedra->GetCellData()->GetArray( "VolumeId" )  )
             ->GetPointer( 0 );


  i        = 0;
  volIndex =-1;
  numFaces = plyHedra->GetNumberOfCells();
  while ( i < numFaces ) // for each separated 2D polygon (face)
    {
    // "0 < numFaces" guarantees that vIdxsPtr is not NULL

    // note that each cell is a 2D polygon (instead of a 3D cell) and we
    // have to use the cell data attribute, i.e., the attached volume Id,
    // to combine separated 2D polygons to reconstruct a volume.
    if ( vIdxsPtr[i] != volIndex )
      {
      // this is the first face of a NEW volume --- init some variables
      newIndex = 0;
      minIndex = fragIndx;
      volIndex = vIdxsPtr[i]; // grouping faces based on the volume Id

      // obtain the attribute values of the sub-volume via the first polygon
      bufIndex = 0;
      for ( a = 0; a < numArays; a ++ )
        {
        theShift = i * numComps[a];
        for ( c = 0; c < numComps[a]; c ++ )
          {
          tupleBuf[ bufIndex ++ ] = attrPtrs[a][ theShift + c ];
          }
        }
      }

    while ( vIdxsPtr[i] == volIndex ) // for each face of the current volume
      {
      // this is really a face of the current volume: add it to the hash
      hashFace = NULL;
      thisFace = plyHedra->GetCell( i );
      switch (  thisFace->GetNumberOfPoints()  )
        {
        case 3: hashFace
                = this->FaceHash->AddFace(  thisFace->GetPointId( 0 ),
                thisFace->GetPointId( 1 ),  thisFace->GetPointId( 2 )  );
                break;

        case 4: hashFace
                = this->FaceHash->AddFace(
                thisFace->GetPointId( 0 ),  thisFace->GetPointId( 1 ),
                thisFace->GetPointId( 2 ),  thisFace->GetPointId( 3 )  );
                break;

        case 5: hashFace
                = this->FaceHash->AddFace(  thisFace->GetPointId( 0 ),
                thisFace->GetPointId( 1 ),  thisFace->GetPointId( 2 ),
                thisFace->GetPointId( 3 ),  thisFace->GetPointId( 4 )  );
                break;

        default:
                hashFace = NULL;
                vtkWarningMacro( "Invalid number of points: face ignoired." );
                break;
        }
      thisFace = NULL;

      if ( hashFace )
        {
        // this face has been added to the hash (unnecessarily the first time
        // --- the same face may have been added to the hash as the constituent
        // polygon of another sub-volume and in this case this face is called
        // an 'internal' face)

        if ( hashFace->FragmentId > 0 )
          {
          // This is an internal face. It has been removed from the hash when
          // the hash attempts to accept it for the second time, though it is
          // accessible until a new face is allocated from the recycle bin.

          if ( hashFace->FragmentId != minIndex && minIndex < fragIndx )
            {
            // This face (X) is not the first one of this volume (R, otherwise
            // minIndex == fragIndx would hold). In fact, there has been a face
            // (Y, of this volume R) that is shared by this volume (R) and
            // a second volume (S, otherwise minIndx == fragIndx would hold).
            // In addition, this face (X) is shared by this volume (R) and a
            // third volume (T, which though has not been merged with volume S,
            // otherwise hashFace->FragmentId == minIndex would hold). In a word,
            // this volume (R) is connected with two currently un-merged volumes
            // S and T. Thus we need to make the fragment Ids of volumes S and T
            // equivalent to each other.
            this->EquivalenceSet
                ->AddEquivalence( minIndex, hashFace->FragmentId );
            }

          // keep track of the smallest fragment id to use for this volume
          if ( minIndex > hashFace->FragmentId ) // --- case A
            {
            // The first face (certainly internal, since hashFace->FragmentId
            // > 0 holds above) of this volume is guranteed to come here. In
            // addition, non-first internal faces (of this volume) that are
            // shared by new volumes also come here. In either case, minIndex
            // is updated below to reflect the smallest fragment Id so far and
            // will be assigned to those subsequent new faces of this volume.
            minIndex = hashFace->FragmentId;
            }
          }
        else
          {
          // this is a new face (hashFace->FragmentId is inited to be 0)
          hashFace->BlockId   = blockIdx;
          hashFace->PolygonId = i;
          hashFace->ProcessId = procIndx;

          // save this new face until we process all the faces of this
          // volume to determine the smallest fragment id for this volume
          if ( newIndex >= VTK_MAX_FACES_PER_CELL )
            {
            vtkErrorMacro( << "Too many faces for a greater-than-isovalue "
                           << "sub-volume." << endl );
            }
          else
            {
            newFaces[ newIndex ++ ] = hashFace;
            }
          } // end if a new face is added to the hash
        } // end if the input face is valid

      // process the next 2D polygon by updating the index of the face
      i ++;
      hashFace = NULL;

      } // for each face of a volume

    // The current face (2D polygon) belongs to a new volume. Before processing
    // it in the next cycle (for each separated 2D polygon), we need to do some
    // thing for the volume that we have just reconstructed / recognized.

    if ( minIndex == fragIndx )
      {
      // This is an isolated volume (possibly the first volume of a fragment)
      // since no any neighboring volume has been found (otherwise minIndex
      // would have been updated to be less than fragIndx in case A above).
      // The code below ensures the correct number of equivalence members.
      this->EquivalenceSet->AddEquivalence( fragIndx, fragIndx );
      fragIndx ++;
      }

    // update the smallest fragment Id used so far
    minIndex = this->EquivalenceSet->GetEquivalentSetId( minIndex );

    // Label the new faces of the volume with the final (smallest) fragment id.
    for ( int k = 0; k < newIndex; k ++ )
      {
      newFaces[k]->FragmentId = minIndex;
      }

    // fragment attributes integration
    this->IntegrateFragmentAttributes( minIndex, tupleSiz, tupleBuf );

    } // for each separated 2D polygon


  for ( i = 0; i < numArays; i ++ )
    {
    attrPtrs[i] = NULL;
    }
  delete [] attrPtrs;
  delete [] numComps;
  delete [] tupleBuf;
  attrPtrs = NULL;
  numComps = NULL;
  tupleBuf = NULL;
  vIdxsPtr = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::IntegrateFragmentAttributes
  ( int fragIndx, int numComps, double * attrVals )
{
  // note this function may be called with non-successive values of fragIndx

  double *   attrsPtr = NULL;
  vtkIdType  arrayIdx = 0;
  vtkIdType  fragSize = this->FragmentValues->GetNumberOfTuples();

  if ( fragSize <= fragIndx )
    {
    vtkIdType      xtntSize = ( fragIndx << 1 ) + 200;
    this->FragmentValues->Resize( xtntSize );
    this->FragmentValues->SetNumberOfTuples( fragIndx + 1 );

    attrsPtr = this->FragmentValues->GetPointer( fragSize * numComps );
    for ( arrayIdx = fragSize * numComps;
          arrayIdx < xtntSize * numComps; arrayIdx ++ )
      {
      *attrsPtr ++ = 0.0;
      }
    }

  attrsPtr = this->FragmentValues->GetPointer( fragIndx * numComps );
  for ( arrayIdx = 0; arrayIdx < numComps; arrayIdx ++, attrsPtr ++, attrVals ++ )
    {
    ( *attrsPtr ) += ( *attrVals );
    }
  attrsPtr   = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::ResolveIntegratedFragmentAttributes()
{
  if ( ! this->EquivalenceSet->Resolved )
    {
    vtkErrorMacro( << "Equivalences not resolved." << endl );
    return;
    }

  if ( this->FragmentValues->GetNumberOfTuples() <
       this->EquivalenceSet->GetNumberOfMembers()
     )
    {
    vtkErrorMacro( << "More partial fragments than volume entries." << endl );
    return;
    }

  int              i;
  int              numComps = this->FragmentValues->GetNumberOfComponents();
  vtkIdType        setIndex = 0;
  vtkIdType        initials = this->FragmentValues->GetNumberOfTuples();
  vtkIdType        resolves = this->EquivalenceSet->GetNumberOfResolvedSets();
  vtkDoubleArray * tmpArray = vtkDoubleArray::New();
  tmpArray->SetNumberOfComponents( numComps );
  tmpArray->SetNumberOfTuples( resolves );
  memset(  tmpArray->GetPointer(0),  0,
           resolves * numComps * sizeof( double )  );

  double * arayPtr1 = NULL;
  double * arayPtr0 = this->FragmentValues->GetPointer( 0 );
  for ( vtkIdType j = 0; j < initials; j ++ )
    {
    setIndex = this->EquivalenceSet->GetEquivalentSetId( j );
    for (  arayPtr1 = tmpArray->GetPointer( setIndex * numComps ),
           i = 0; i < numComps; i ++, arayPtr0 ++, arayPtr1 ++  )
      {
      ( *arayPtr1 ) += ( *arayPtr0 );
      }
    }

  this->FragmentValues->Delete();
  this->FragmentValues = tmpArray; // to use shallow copy? xxx

  tmpArray = NULL;
  arayPtr0 = NULL;
  arayPtr1 = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::ResolveFaceFragmentIds()
{
  vtkRectilinearGridConnectivityFace * thisFace = NULL;
  this->FaceHash->InitTraversal();

  while (  ( thisFace = this->FaceHash->GetNextFace() )  )
    {
    thisFace->FragmentId = this->EquivalenceSet
                               ->GetEquivalentSetId( thisFace->FragmentId );
    }

  thisFace = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::ResolveEquivalentFragments()
{
  this->EquivalenceSet->ResolveEquivalences();
  this->ResolveIntegratedFragmentAttributes();
  this->ResolveFaceFragmentIds();
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::ExtractFragmentPolygons
  ( int blockIdx, int & maxFsize, vtkPolyData * plyHedra,
    vtkPolyData * polygons, vtkIncrementalOctreePointLocator * gPtIdGen )
{
  if ( !plyHedra || !polygons || !gPtIdGen ||
       !this->Internal->IntegrableCellDataArraysAvailable( plyHedra )
     )
    {
    vtkErrorMacro( << "Input marching cubes vtkPolyData, output fragments "
                   << "vtkPolyData, or point locator (a global point Id "
                   << "generator) NULL, or integrable cell data attributes "
                   << "not found from input vtkPolyData." << endl );
    return;
    }


  // create a vtkEquivalenceSet to keep track of fragment ids for the polygons
  // of this polyhedra (resulting from marching cubes) and to determine which
  // fragment-ids need to be combined into a single one for merging fragments.
  if ( this->EquivalenceSet )
    {
    this->EquivalenceSet->Delete();
    this->EquivalenceSet = NULL;
    }
  this->EquivalenceSet = vtkEquivalenceSet::New();

  // create a face hash and initialize it with the number of points
  this->InitializeFaceHash( plyHedra );

  // get the number of integrated components (including the material volume)
  // to be saved to and extracted from the global fragment attributes array
  int  tupleSiz = this->Internal->NumberIntegralComponents + 1;

  // allocate a vtkDoubleArray to maintain the attributes of each fragment
  if ( this->FragmentValues )
    {
    this->FragmentValues->Delete();
    this->FragmentValues = NULL;
    }
  this->FragmentValues = vtkDoubleArray::New();
  this->FragmentValues->SetNumberOfComponents( tupleSiz );

  // add all polygons of the polyhedra to the face hash
  this->AddPolygonsToFaceHash( blockIdx, plyHedra );

  // extract the exterior surfaces / polygons of the fragments
  this->ResolveEquivalentFragments();


  // Obtain fragments' exterior surfaces / polygons of the polyhedra (the
  // greater-than-isovalue sub-volumes) through the face hash and export
  // these exterior surfaces to a vtkPolyData ('polygons').


  int               i;
  int               theShift;
  int               numArays;
  int               numFaces;
  int               numbPnts;        // for a face
  int             * numComps = NULL;
  double          * tupleBuf;        // integrated component values
  double            pntCoord[3];     // a face point
  double            thisBbox[6];
  vtkIdType         globePId;        // global Id of a point on exterior face
  vtkIdType         cellIndx;        // for the output vtkPolyData
  vtkIdType         facePIds[5];     // point Ids of a face (at most 5)
  vtkCell         * faceCell = NULL; // a face from the polyhedra
  vtkPoints       * hedraPts = NULL; // vtkPoints of the polyhedra
  vtkPoints       * polyPnts = NULL;
  vtkCellArray    * plyCells = NULL;
  vtkIntArray     * fragIdxs = NULL;
  vtkIdTypeArray  * uniPIdxs = NULL;
  vtkDoubleArray  * theArray = NULL;
  vtkDoubleArray ** attrVals = NULL;

  vtkIncrementalOctreePointLocator   * pntAdder = NULL; // for this block only
  vtkRectilinearGridConnectivityFace * thisFace = NULL; // face from the hash
  std::vector< vtkRectilinearGridConnectivityFace * > *  theGroup = NULL;
  std::vector< vtkRectilinearGridConnectivityFace * >::iterator faceItrt;
  std::map<     int,
                   std::vector< vtkRectilinearGridConnectivityFace * >
             >     faceGrps; // faces grouped by the fragment Id
  std::map<     int,
                   std::vector< vtkRectilinearGridConnectivityFace * >
             >::iterator   grpItrat;


  // First we need to retrieve the exterior faces (with non-zero fragment Ids)
  // from the face hash and put them in a temporary buffer in which they are
  // grouped by the fragment Id. Once the original polygons (triangles, quads,
  // and pentagons) are obtained from the marching-cubes' output (plyHedra),
  // these fragment-Ids will be attached to the polygons as a cell data
  // attribute. This cell data attribute will then be exploited in the final
  // fragments resolution process to recognize each 'macro' volume (i.e., a
  // fragment) that is made up of the (exterior) polygons with the same fragment
  // Id. In this way, the same fragment Id can be maintained for the constituent
  // exterior polygons of each fragment.


  numFaces = 0;
  this->FaceHash->InitTraversal();
  while (  ( thisFace = this->FaceHash->GetNextFace() )  )
    {
    if ( thisFace->FragmentId > 0 )
      {
      numFaces ++;
      grpItrat = faceGrps.find( thisFace->FragmentId );

      if (  grpItrat == faceGrps.end()  )
        {
        // create a faces group for this new fragment and add this face to it
        std::vector< vtkRectilinearGridConnectivityFace * >  macroVol;
        macroVol.push_back( thisFace );
        faceGrps[ thisFace->FragmentId ] = macroVol;
        }
      else
        {
        // add this face to the target group
        grpItrat->second.push_back( thisFace );
        }
      }
    }
  thisFace = NULL;


  // the vtkPoints of the output vtkPolyData
  polyPnts = vtkPoints::New();
  polyPnts->Allocate( numFaces << 1, numFaces );

  // a local (block-dependent) point locator used to insert the points
  // of the exterior polygons to the output vtkPolyData
  plyHedra->GetBounds( thisBbox ); // fortunately the bounds keep unchanged
  pntAdder = vtkIncrementalOctreePointLocator::New();
  pntAdder->InitPointInsertion( polyPnts, thisBbox, numFaces << 1 );

  // array of global point Ids (one per unique point)
  uniPIdxs = vtkIdTypeArray::New();
  uniPIdxs->SetName( "GlobalNodeId" );
  uniPIdxs->Allocate( numFaces << 1, numFaces );

  // the polygons / cells of the output vtkPolyData (with exterior faces only)
  plyCells = vtkCellArray::New();
  plyCells->Allocate( numFaces, numFaces >> 4 );

  // array of fragment Ids (one per constituent polygon)
  // here the fragment Ids are unnecessarily global since they are used for
  // grouping the exterior polygons only
  fragIdxs = vtkIntArray::New();
  fragIdxs->SetName( "FragmentId" );
  fragIdxs->Allocate( numFaces, numFaces >> 4 );

  // allocate a buffer for a tuple of integrated component values (including
  // the material volume) to be extracted from the global fragment attributes
  // array and create a set of vtkDoubleArray objects to collect the integrated
  // attribute values which are attached to the output polygons as the cell data
  numArays    = int( this->Internal->IntegrableAttributeNames.size() ) + 1;
  tupleBuf    = new      double      [ tupleSiz ];
  numComps    = new       int        [ numArays ];
  attrVals    = new vtkDoubleArray * [ numArays ];
  numComps[0] = 1;
  attrVals[0] = vtkDoubleArray::New();
  attrVals[0]->SetName( "MaterialVolume" );
  attrVals[0]->SetNumberOfComponents( 1 );
  attrVals[0]->Allocate( numFaces, numFaces >> 4 );
  for ( i = 1; i < numArays; i ++ )
    {
    theArray    = vtkDoubleArray::SafeDownCast
                  (  plyHedra->GetCellData()->GetArray
                     ( this->Internal->IntegrableAttributeNames[ i - 1 ].c_str() )
                  );
    numComps[i] = theArray->GetNumberOfComponents();
    attrVals[i] = vtkDoubleArray::New();
    attrVals[i]->SetName( theArray->GetName() );
    attrVals[i]->SetNumberOfComponents( numComps[i] );
    attrVals[i]->Allocate( numFaces, numFaces >> 4 );
    theArray    = NULL;
    }


  // Now that all exterior faces (though each with incomplete information ---
  // only three point-Ids are recorded) are grouped in the temporary buffer, we
  // need to retrieve them to obtain the original polygons which are assigned
  // with fragment-Ids as the cell data before being inserted to the output.

  maxFsize = 1;
  hedraPts = plyHedra->GetPoints();
  for ( grpItrat = faceGrps.begin(); grpItrat != faceGrps.end(); grpItrat ++ )
    {
    // the exterior polygons with the same fragment Id, i.e., the exterior
    // polygons of the same fragment, are inserted to the output vtkPolyData
    // one by one successively

    theGroup = &( grpItrat->second );
    maxFsize =  (  static_cast<int> ( theGroup->size() ) > maxFsize  )
                 ? static_cast<int> ( theGroup->size() ) : maxFsize;

    for ( faceItrt = theGroup->begin(); faceItrt != theGroup->end(); faceItrt ++ )
      {
      // gain access to the face (with incomplete info) that is guaranteed to
      // be exterior since we threw away internal faces when creating the
      // grouped faces
      thisFace = *faceItrt;

      // We have re-defined the meaning of vtkRectilinearGridConnectivityFace::
      // PolygonId, which is the polygon Id in the vtkPolyData (the polyhedra).
      // In this way, we can get direct access to the original polygon without
      // resorting to the reconstructed 3D cell (volume) at all.
      faceCell = plyHedra->GetCell( thisFace->PolygonId );

      // add ALL of the points of the face to the output vtkPolyData
      numbPnts = faceCell->GetNumberOfPoints();
      for ( i = 0; i < numbPnts; i ++ )
        {
        hedraPts->GetPoint(  faceCell->GetPointId( i ),  pntCoord  );

        // if possible, insert this point to the vtkPoints and assign it
        // with a global Id as the point data attribute
        if (  pntAdder->InsertUniquePoint( pntCoord, facePIds[i] )  )
          {
          gPtIdGen->InsertUniquePoint( pntCoord, globePId );
          uniPIdxs->InsertValue( facePIds[i], globePId );
          }
        }

      // add the original face to the output vtkPolyData (the PolygonId is
      // not useful any more and is ignored below)
      cellIndx = plyCells->InsertNextCell( numbPnts, facePIds );
      fragIdxs->InsertValue( cellIndx, thisFace->FragmentId );
      this->FragmentValues->GetTupleValue( thisFace->FragmentId, tupleBuf );
      for ( theShift = 0, i = 0; i < numArays; i ++ )
        {
        attrVals[i]->InsertTupleValue( cellIndx, tupleBuf + theShift );
        theShift += numComps[i];
        }

      thisFace = NULL;
      faceCell = NULL;

      // Set NULL to this entry to avoid the face from being destructed
      // when the vector (theGroup) is removed from the map (groups). We
      // will use 'delete this->FaceHash' later to destroy all the faces.
      *faceItrt = NULL;
      }

    theGroup->clear();
    theGroup = NULL;
    }
  faceGrps.clear();
  hedraPts = NULL;


  // fill the output vtkPolyData --- polygons
  polygons->SetPoints( polyPnts );
  polygons->SetPolys ( plyCells );
  polygons->GetPointData()->SetGlobalIds( uniPIdxs );
  polygons->GetCellData()->AddArray( fragIdxs ); // for the final resolution
  for ( i = 0; i < numArays; i ++ )
    {
    polygons->GetCellData()->AddArray( attrVals[i] );
    attrVals[i]->Delete();
    attrVals[i] = NULL;
    }
  polygons->Squeeze();

  // memory deallocation
  pntAdder->Delete();
  polyPnts->Delete();
  plyCells->Delete();
  uniPIdxs->Delete();
  fragIdxs->Delete();
  delete [] attrVals;
  delete [] numComps;
  delete [] tupleBuf;

  pntAdder = NULL;
  polyPnts = NULL;
  plyCells = NULL;
  uniPIdxs = NULL;
  fragIdxs = NULL;
  attrVals = NULL;
  numComps = NULL;
  tupleBuf = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::InitializeFaceHash
  ( vtkPolyData ** plyDatas, int numPolys )
{
  int       i;
  vtkIdType maxIndex = 0;

  // find the maximum global point Id to initialize the face hash
  for ( i = 0 ; i < numPolys; i ++ )
    {
    // The if-statement below is a MUST since an input vtkPolyData may be just
    // 'empty'. This is the case with both single-process mode and multi-process
    // mode if no any polygon is extracted from the marching-cubes process or
    // else no any polygon remains after in-hash polygons resolution (removal
    // of internal faces). Note that a process may be assigned with no any block
    // at all.
    if (    vtkIdTypeArray::SafeDownCast
            (  plyDatas[i]->GetPointData()->GetArray( "GlobalNodeId" )  )
         == NULL
       )
      {
      vtkDebugMacro( << "Point data GlobalNodeId not found in "
                     << "vtkPolyData #" << i << endl );
      continue;
      }

    vtkIdType   pntindex = 0;
    vtkIdType   numbPnts = plyDatas[i]->GetNumberOfPoints();
    vtkIdType * ptIdsPtr = vtkIdTypeArray::SafeDownCast
                (  plyDatas[i]->GetPointData()->GetArray( "GlobalNodeId" )  )
                ->GetPointer( 0 );

    for ( pntindex = 0; pntindex < numbPnts; pntindex ++, ptIdsPtr ++ )
      {
      maxIndex = ( *ptIdsPtr > maxIndex ) ? ( *ptIdsPtr ) : maxIndex;
      }
    }

  if ( this->FaceHash )
    {
    delete this->FaceHash;
    this->FaceHash = NULL;
    }
  this->FaceHash = new vtkRectilinearGridConnectivityFaceHash;
  this->FaceHash->Initialize( maxIndex + 1 );
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::AddPolygonsToFaceHash
  ( vtkPolyData ** plyDatas, int * maxFsize, int numPolys )
{
  if ( !plyDatas || !maxFsize )
    {
    vtkErrorMacro( "Input vtkPolyData array (plyDatas) or maxFsize NULL." );
    return;
    }

  int              i, j, k, a, c;
  int              procIndx = this->Controller->GetLocalProcessId();
  int              theShift = 0;
  int              bufIndex = 0;
  int              numArays = 0;
  int              tupleSiz = 0; // number of integrated components
  int              newIndex = 0; // index of a new face of the local fragment
  int              minIndex = 1; // the smallest (inter-block) fragment Id
  int              fragIndx = 1; // next inter-block fragment Id and 0 for
                                 // removing faces
  int *            lfIdsPtr = NULL; // array of local fragment Ids
  int            * numComps = NULL; // number of integrated components
  double  *        tupleBuf = NULL; // integrated component values
  double        ** attrPtrs = NULL;
  vtkCell *        thisFace = NULL; // a 2D polygon (not a 3D cell)
  vtkIdType        numFaces = 0; // number of 2D polygons of an input vtkPolyData
  vtkIdType        localFId = 0; // Id of the local fragment being processed
  vtkIdType        numbPnts = 0; // for a face
  vtkIdType        pointIds[5];  // point Ids of a face (at most 5 points)
  vtkIdType *      ptIdsPtr = NULL; // array of point Ids
  vtkDoubleArray * theArray = NULL;
  vtkRectilinearGridConnectivityFace *  hashFace = NULL; // a face in the hash
  vtkRectilinearGridConnectivityFace ** newFaces = NULL;


  // determine the number of integrated components (including the material
  // volume) to be saved to the global fragment attributs array and allocate a
  // buffer for a tuple
  tupleSiz = this->Internal->NumberIntegralComponents + 1;
  tupleBuf = new double [ tupleSiz ];
  memset(  tupleBuf,  0,  sizeof( double ) * tupleSiz  );


  // allocate pointers for access to the arrays of integrated attributes
  numArays = int( this->Internal->IntegrableAttributeNames.size() ) + 1;
  numComps = new int      [ numArays ];
  attrPtrs = new double * [ numArays ];
  numComps[0] = 1;
  attrPtrs[0] = NULL;
  for ( a = 1; a < numArays; a ++ )
    {
    numComps[a] = 0;
    attrPtrs[a] = NULL;
    }


  // process each vtkPolyData and add its 2D polygons (faces) to the hash
  for ( j = 0; j < numPolys; j ++ )
    {
    // each vtkPolyData stores individual 2D polygons (triangles, quads, and
    // pentagons) of which each is though coupled with a local / block-based
    // fragment Id as the cell data attribute to convey the connectivity of
    // the exterior polygons of the same fragment --- 'macro volume'

    if (    vtkIdTypeArray::SafeDownCast
            (  plyDatas[j]->GetPointData()->GetArray( "GlobalNodeId" )  )
            == NULL
         || vtkIntArray::SafeDownCast
            (  plyDatas[j]->GetCellData()->GetArray( "FragmentId" )  )
            == NULL
         || vtkDoubleArray::SafeDownCast
            (  plyDatas[j]->GetCellData()->GetArray( "MaterialVolume" )  )
            == NULL
         ||!this->Internal->IntegrableCellDataArraysAvailable( plyDatas[j] )
       )
      {
      vtkDebugMacro( << "Point data GlobalNodeId, cell data FragmentId, "
                     << "MaterialVolume, or integrable fragemnt "
                     << "attributes not found in vtkPolyData #" << j << endl );
      continue;
      }

    // gain access to global node Ids, global volume Ids, and local fragment Ids
    ptIdsPtr    = vtkIdTypeArray::SafeDownCast
                  (  plyDatas[j]->GetPointData()->GetArray( "GlobalNodeId" )  )
                  ->GetPointer( 0 );
    lfIdsPtr    = vtkIntArray::SafeDownCast
                  (  plyDatas[j]->GetCellData()->GetArray( "FragmentId" )  )
                  ->GetPointer( 0 );
    attrPtrs[0] = vtkDoubleArray::SafeDownCast
                 (  plyDatas[j]->GetCellData()->GetArray( "MaterialVolume" )  )
                 ->GetPointer( 0 );
    for ( a = 1; a < numArays; a ++ )
      {
      theArray    = vtkDoubleArray::SafeDownCast
                    (  plyDatas[j]->GetCellData()->GetArray
                       ( this->Internal->IntegrableAttributeNames[ a - 1 ].c_str() )
                    );
      attrPtrs[a] = theArray->GetPointer( 0 );
      numComps[a] = theArray->GetNumberOfComponents();
      theArray    = NULL;
      }

    // given the maximum size of a fragment, i.e., the maximum number of
    // faces per fragment in this vtkPolyData, allocate a buffer to maintain
    // the possible new faces of a single fragment
    newFaces = new vtkRectilinearGridConnectivityFace * [ maxFsize[j] ];
    for ( k = 0; k < maxFsize[j]; k ++ )
      {
      newFaces[k] = NULL;
      }

    i        = 0;
    localFId =-1;
    numFaces = plyDatas[j]->GetNumberOfCells();
    while ( i < numFaces ) // for each individual 2D polygon (face)
      {
      // "0 < numFaces" guarantees ptIdsPtr, and lfIdsPtr are not NULL

      // note that each cell is a 2D polygon (instead of a 3D cell) and we
      // have to use the cell data attribute, i.e., the local fragment Id,
      // to combine individual 2D polygons to reconstruct a 'macro volume'
      if ( lfIdsPtr[i] != localFId )
        {
        // the first face of a NEW 'macro' volume --- init some variables
        newIndex = 0;
        minIndex = fragIndx;
        localFId = lfIdsPtr[i]; // grouping faces via the local fragment Id

        // obtain the attribute value of the sub-volume via the first polygon
        bufIndex = 0;
        for ( a = 0; a < numArays;    a ++ )
          {
          theShift = i * numComps[a];
          for ( c = 0; c < numComps[a]; c ++ )
            {
            tupleBuf[ bufIndex ++ ] = attrPtrs[a][ theShift + c ];
            }
          }
        }

      while ( lfIdsPtr[i] == localFId ) // for each face of the 'macro' volume
        {
        // this is a face of the current 'macro' volume
        hashFace = NULL;
        thisFace = plyDatas[j]->GetCell( i );
        numbPnts = thisFace->GetNumberOfPoints();

        // add the face to the hash
        if ( numbPnts == 3 )
          {
          pointIds[0] = ptIdsPtr[  thisFace->GetPointId( 0 )  ];
          pointIds[1] = ptIdsPtr[  thisFace->GetPointId( 1 )  ];
          pointIds[2] = ptIdsPtr[  thisFace->GetPointId( 2 )  ];
          hashFace    = this->FaceHash->AddFace
                        ( pointIds[0], pointIds[1], pointIds[2] );
          }
        else
        if ( numbPnts == 4 )
          {
          pointIds[0] = ptIdsPtr[  thisFace->GetPointId( 0 )  ];
          pointIds[1] = ptIdsPtr[  thisFace->GetPointId( 1 )  ];
          pointIds[2] = ptIdsPtr[  thisFace->GetPointId( 2 )  ];
          pointIds[3] = ptIdsPtr[  thisFace->GetPointId( 3 )  ];
          hashFace    = this->FaceHash->AddFace( pointIds[0], pointIds[1],
                                                 pointIds[2], pointIds[3] );
          }
        else
        if ( numbPnts == 5 )
          {
          pointIds[0] = ptIdsPtr[  thisFace->GetPointId( 0 )  ];
          pointIds[1] = ptIdsPtr[  thisFace->GetPointId( 1 )  ];
          pointIds[2] = ptIdsPtr[  thisFace->GetPointId( 2 )  ];
          pointIds[3] = ptIdsPtr[  thisFace->GetPointId( 3 )  ];
          pointIds[4] = ptIdsPtr[  thisFace->GetPointId( 4 )  ];
          hashFace    = this->FaceHash->AddFace( pointIds[0], pointIds[1],
                                    pointIds[2], pointIds[3], pointIds[4] );
          }
        else
          {
          hashFace    = NULL;
          vtkWarningMacro( "Face ignored due to invalid number of points." );
          }
        thisFace = NULL;

        if ( hashFace )
          {
          // this face has been added to the hash and it is not necessarily the
          // first time --- the same face may have been added to the hash as the
          // constituent polygon of another sub-volume ('macro') and in this case
          // this face is called an 'internal' face

          if ( hashFace->FragmentId > 0 )
            {
            // This is an internal face. It has been removed from the hash when
            // the hash attempts to accept it for the second time, though it is
            // accessible until a new face is allocated from the recycle bin.

            if ( hashFace->FragmentId != minIndex && minIndex < fragIndx )
              {
              // This face (X) is not the first one of this 'macro' volume (R,
              // otherwise minIndex == fragIndx would hold). In fact, there has
              // been a face (Y, of this 'macro' volume R) that is shared by this
              // 'macro' volume (R) and a second 'macro' volume (S, otherwise
              // minIndx == fragIndx would hold). In addition, this face (X) is
              // shared by this 'macro' volume (R) and a third 'macro' volume (T,
              // which though has not been merged with 'macro' volume S, otherwise
              // hashFace->FragmentId == minIndex would hold). In a word, this
              // 'macro' volume (R) is connected with two currently un-merged 'macro'
              // volumes S and T. Thus we need to make the fragment Ids of 'macro'
              // volumes S and T equivalent to each other.
              this->EquivalenceSet->AddEquivalence
                                    ( minIndex, hashFace->FragmentId );
              }

            // keep track of the smallest fragment id to use for this 'macro' volume
            if ( minIndex > hashFace->FragmentId ) // --- case A
              {
              // The first face (certainly internal, since hashFace->FragmentId
              // > 0 holds above) of this 'macro' volume is guranteed to come here.
              // In addition, non-first internal faces (of this 'macro' volume) that
              // are shared by new 'macro' volumes also come here. In either case,
              // minIndex is updated below to reflect the smallest fragment Id so
              // far and will be assigned to those subsequent new faces of this
              // 'macro' volume.
              minIndex = hashFace->FragmentId;
              }
            }
          else
            {
            // this is a new face (hashFace->FragmentId is inited to be 0)
            hashFace->BlockId   = j;
            hashFace->PolygonId = i;
            hashFace->ProcessId = procIndx;

            // save this new face until we process all the faces of this
            // 'macro' volume to determine the smallest fragment id
            newFaces[ newIndex ++ ] = hashFace;

            } // end if a new face is added to the hash
          } // end if the input face is valid

        // process the next 2D polygon by updating the index of the face
        i ++;
        hashFace = NULL;

        } // for each face of a 'macro' volume

      // The current face (2D polygon) belongs to a new 'macro' volume. Before
      // processing it in the next cycle (for each separated 2D polygon), we
      // need to do some thing for the 'macro' volume that we have just recognized.

      if ( minIndex == fragIndx )
        {
        // This is an isolated 'macro' volume (possibly the first 'macro' volume
        // of a fragment) since no any neighboring 'macro' volume has been found
        // (otherwise minIndex would have been updated to be less than fragIndx
        // in case A above). The code below ensures the correct number of
        // equivalence members.
        this->EquivalenceSet->AddEquivalence( fragIndx, fragIndx );
        fragIndx ++;
        }

      // update the smallest fragment Id used so far
      minIndex = this->EquivalenceSet->GetEquivalentSetId( minIndex );

      // Label the new faces of the 'macro' volume with the final (smallest)
      // fragment id.
      for ( k = 0; k < newIndex; k ++ )
        {
        newFaces[k]->FragmentId = minIndex;
        }

      // fragment attributes integration
      this->IntegrateFragmentAttributes( minIndex, tupleSiz, tupleBuf );

      } // for each individual 2D polygon

    // clean up the buffer of new faces
    for ( k = 0; k < maxFsize[j]; k ++ )
      {
      newFaces[k] = NULL;
      }
    delete [] newFaces;
    newFaces = NULL;

    for ( i = 0; i < numArays; i ++ )
      {
      attrPtrs[i] = NULL;
      }
    ptIdsPtr = NULL;
    lfIdsPtr = NULL;
    } // for each input vtkPolyData


  delete [] attrPtrs;
  delete [] numComps;
  delete [] tupleBuf;
  attrPtrs = NULL;
  numComps = NULL;
  tupleBuf = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::GenerateOutputFromSingleProcess
  ( vtkPolyData ** surfaces, int numSurfs,
    unsigned char  partIndx, vtkPolyData * polyData )
{
  // Retrieve part of the surfaces (the exterior surfaces of the fragments) of
  // the greater-than-isovalue sub-volumes through the face hash and export
  // these exterior surfaces to a vtkPolyData.

  if ( !surfaces || !polyData )
    {
    vtkErrorMacro( << "surfaces or polyData NULL" << endl );
    return;
    }

  int               i, j;
  int               theShift;
  int               numArays;
  int               tupleSiz;        // number of integrated components
  int               degnerat;
  int               numbPnts;        // for a face
  int             * numComps = NULL;
  double            pntCoord[3];     // a face point
  double          * tupleBuf    = NULL; // integrated component values
  double          * rcBounds    = NULL;
  double            mbBounds[6] = { VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
                                    VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
                                    VTK_DOUBLE_MAX, VTK_DOUBLE_MIN
                                  };
  vtkIdType         facePIds[5];     // point Ids of a face (at most 5)
  vtkCell         * faceCell = NULL; // a face from the vtkPolyData
  vtkPoints       * surfPnts = NULL; // vtkPoints attached to vtkPolyData
  vtkPoints       * polyPnts = NULL;
  vtkCellArray    * polygons = NULL;
  vtkIntArray     * fragIdxs = NULL;
  vtkDoubleArray ** attrVals = NULL;
  vtkUnsignedCharArray               * partIdxs = NULL;
  vtkIncrementalOctreePointLocator   * pntAdder = NULL;
  vtkRectilinearGridConnectivityFace * thisFace = NULL;

  // points and polygons (cells)
  polyPnts = vtkPoints::New();
  polygons = vtkCellArray::New();

  // array of fragment Ids (one per constituent polygon)
  fragIdxs = vtkIntArray::New();
  fragIdxs->SetName( "FragmentId" );

  // array of part indices (one per constituent polygon, part index is
  // determined by the material volume fraction index)
  partIdxs = vtkUnsignedCharArray::New();
  partIdxs->SetName( "Part Index" );


  // allocate a buffer for a tuple of integrated component values (including
  // the material volume) to be extracted from the global fragment attributes
  // array and create a set of vtkDoubleArray objects to collect the integrated
  // attribute values which are attached to the output polygons as the cell data
  tupleSiz    = this->Internal->NumberIntegralComponents + 1;
  numArays    = int( this->Internal->IntegrableAttributeNames.size() ) + 1;
  tupleBuf    = new      double      [ tupleSiz ];
  numComps    = new       int        [ numArays ];
  attrVals    = new vtkDoubleArray * [ numArays ];
  numComps[0] = 1;
  attrVals[0] = vtkDoubleArray::New();
  attrVals[0]->SetName( "MaterialVolume" );
  attrVals[0]->SetNumberOfComponents( 1 );
  for ( i = 1; i < numArays; i ++ )
    {
    numComps[i] = this->Internal->ComponentNumbersPerArray[ i - 1 ];
    attrVals[i] = vtkDoubleArray::New();
    attrVals[i]->SetName( this->Internal
                              ->IntegrableAttributeNames[ i - 1 ].c_str() );
    attrVals[i]->SetNumberOfComponents( numComps[i] );
    }


  // create a point locator to maintain all the points accessed from
  // the original vtkPolyData (the exterior polygons of the fragments)
  pntAdder = vtkIncrementalOctreePointLocator::New();
  for ( i = 0; i < numSurfs; i ++ )
    {
    rcBounds    = surfaces[i]->GetBounds();
    mbBounds[0] = ( rcBounds[0] < mbBounds[0] ) ? rcBounds[0] : mbBounds[0];
    mbBounds[2] = ( rcBounds[2] < mbBounds[2] ) ? rcBounds[2] : mbBounds[2];
    mbBounds[4] = ( rcBounds[4] < mbBounds[4] ) ? rcBounds[4] : mbBounds[4];
    mbBounds[1] = ( rcBounds[1] > mbBounds[1] ) ? rcBounds[1] : mbBounds[1];
    mbBounds[3] = ( rcBounds[3] > mbBounds[3] ) ? rcBounds[3] : mbBounds[3];
    mbBounds[5] = ( rcBounds[5] > mbBounds[5] ) ? rcBounds[5] : mbBounds[5];
    rcBounds    = NULL;
    }
  pntAdder->SetTolerance( 0.0001 );
  pntAdder->InitPointInsertion( polyPnts, mbBounds, 10000 );

  // retrieve each face maintained in the hash
  this->FaceHash->InitTraversal();
  while (  ( thisFace = this->FaceHash->GetNextFace() )  )
    {
    // Skip the faces masked (with 0) by the inter-process resolution.
    // Any face with a zero fragment index is an internal face and
    // will not be present in the output.
    if ( thisFace->FragmentId > 0 )
      {
      // This is an exterior face of some fragment. Note that we have
      // re-defined the meaning of vtkRectilinearGridConnectivityFace::
      // PolygonId, which is just the polygon Id in the vtkPolyData. In this
      // way, we can get direct access to the original polygon without
      // resorting to the reconstructed 3D cell (volume) at all.
      surfPnts = surfaces[ thisFace->BlockId ]->GetPoints();
      faceCell = surfaces[ thisFace->BlockId ]->GetCell( thisFace->PolygonId );

      // Let's just duplicate points and we could set up a point map
      // between the input and the output.
      numbPnts = faceCell->GetNumberOfPoints();
      if ( numbPnts > 5 )
        {
        numbPnts = 5;
        vtkWarningMacro( << "Not triangle, quad, or pentagon." << endl );
        }

      // add the points of the face to the output vtkPolyData
      for ( i = 0; i < numbPnts; i ++ )
        {
        surfPnts->GetPoint(  faceCell->GetPointId( i ),  pntCoord  );
        pntAdder->InsertUniquePoint( pntCoord, facePIds[i] );
        }

      // As we are now collecting polygons for rendering (unless in multi-
      // process mode), polygons that degenerate to lines or points need to
      // be rejected. This rejection does not affect the fragment extraction
      // result at all while guaranteeing the generation of cell normals.
      //
      // It is assumed that polygon degenration seldom ocurs and therefore
      // the lack of early exit (when evaluating an if-statement) does not
      // cause a negative effect while the use of a single comparison can
      // speed up the whole check.
      degnerat = 0;
      for ( i = 0;     i < numbPnts - 1; i ++ )
      for ( j = i + 1; j < numbPnts;     j ++ )
        {
        degnerat += static_cast<int> (  !( facePIds[i] - facePIds[j] )  );
        }

      if ( numbPnts - degnerat >= 3 )
        {
        // it is a triangle, quad, or pentagon (line is rejected)

        // add the original face to the output vtkPolyData
        polygons->InsertNextCell( numbPnts, facePIds );
        fragIdxs->InsertNextValue( thisFace->FragmentId );
        partIdxs->InsertNextValue( partIndx );
        this->FragmentValues->GetTupleValue( thisFace->FragmentId, tupleBuf );
        for ( theShift = 0, i = 0; i < numArays; i ++ )
          {
          attrVals[i]->InsertNextTupleValue( tupleBuf + theShift );
          theShift += numComps[i];
          }
        }

      } // end if it is an exterior face
    } // end loop over faces in the hash

  thisFace = NULL;
  surfPnts = NULL;
  faceCell = NULL;

  // fille the output vtkPolyData
  polyData->SetPoints( polyPnts );
  polyData->SetPolys ( polygons );
  polyData->GetCellData()->AddArray( fragIdxs );
  polyData->GetCellData()->AddArray( partIdxs );
  for ( i = 0; i < numArays; i ++ )
    {
    polyData->GetCellData()->AddArray( attrVals[i] );
    attrVals[i]->Delete();
    attrVals[i] = NULL;
    }
  polyData->Squeeze();

  // memory deallocation
  pntAdder->Delete();
  polyPnts->Delete();
  polygons->Delete();
  fragIdxs->Delete();
  partIdxs->Delete();
  delete [] attrVals;
  delete [] numComps;
  delete [] tupleBuf;

  pntAdder = NULL;
  polyPnts = NULL;
  polygons = NULL;
  fragIdxs = NULL;
  partIdxs = NULL;
  attrVals = NULL;
  numComps = NULL;
  tupleBuf = NULL;
}

//-----------------------------------------------------------------------------
void  vtkRectilinearGridConnectivity::CreateInterProcessPolygons
  ( vtkPolyData * fragPoly, vtkPolyData * procPoly,
    vtkIncrementalOctreePointLocator * gPtIdGen, int & maxFsize )
{
  // Given the fragments extraction result (fragPoly) from a process, group the
  // the polygons based on the fragment Id before writing them to the output
  // vtkPolyData in group-wise order. In addition, a global point Id is assigned
  // to each point to support the subsequent inter-process polygons-resolution.

  if ( !fragPoly || !procPoly || ! gPtIdGen )
    {
    vtkErrorMacro( << "Input vtkPolyData (fragPoly), point locator (gPtIdGen), "
                   << "or output vtkPolyData (procPoly) NULL." << endl );
    return;
    }

  maxFsize = 1; // must be initialized before the if-statement below

  if (    vtkIntArray::SafeDownCast
          (  fragPoly->GetCellData()->GetArray( "FragmentId" )  ) == NULL
       || vtkDoubleArray::SafeDownCast
          (  fragPoly->GetCellData()->GetArray( "MaterialVolume" )  ) == NULL
       || !this->Internal->IntegrableCellDataArraysAvailable( fragPoly )
     )
    {
    vtkDebugMacro( << "Cell data FragmentId, MaterialVolume, or integrated "
                   << "fragment attributes not found from the extraction "
                   << "result of the process." << endl );
    return;
    }

  int               i;
  int               numArays;
  int               numbPnts;
  int               numCells;
  int               inCellId;
  int               cellIndx;
  int             * fIdxsPtr = NULL;
  int             * numComps = NULL;
  double         ** attrPtrs = NULL;
  double            pntCoord[3];
  vtkPoints       * polyPnts = NULL;
  vtkIdType         globalId = 0;
  vtkIntArray     * uniFIdxs = NULL;
  vtkCellArray    * polygons = NULL;
  vtkIdTypeArray  * uniPIdxs = NULL;
  vtkDoubleArray  * theArray = NULL;
  vtkDoubleArray ** attrVals = NULL;

  std::vector< int >        *  theGroup = NULL;
  std::vector< int >::iterator cellItrt;
  std::map<  int,  std::vector< int >  >  cellGrps;
  std::map<  int,  std::vector< int >  >::iterator   grpItrat;

  // number of points and that of cells
  numbPnts = fragPoly->GetNumberOfPoints();
  numCells = fragPoly->GetNumberOfCells();

  // copy the points
  polyPnts = vtkPoints::New();
  polyPnts->DeepCopy( fragPoly->GetPoints() );

  // allocate an array of cells
  polygons = vtkCellArray::New();
  polygons->Allocate( numCells );

  // allocate five arrays of data attributes
  uniPIdxs = vtkIdTypeArray::New();
  uniPIdxs->SetName( "GlobalNodeId" );
  uniPIdxs->SetNumberOfTuples( numbPnts );
  uniPIdxs->SetNumberOfComponents( 1 );

  uniFIdxs = vtkIntArray::New();
  uniFIdxs->SetName( "FragmentId" );
  uniFIdxs->SetNumberOfTuples( numCells );
  uniFIdxs->SetNumberOfComponents( 1 );


  // gain access to various data attributes and create a set of vtkDoubleArray
  // objects to collect the integrated attribute values (including the material
  // volume) which are forwarded to the output polygons as the cell data
  fIdxsPtr = vtkIntArray::SafeDownCast
             (  fragPoly->GetCellData()->GetArray( "FragmentId" )  )
             ->GetPointer( 0 );

  numArays    = int( this->Internal->IntegrableAttributeNames.size() ) + 1;
  numComps    = new       int        [ numArays ];
  attrPtrs    = new      double    * [ numArays ];
  attrVals    = new vtkDoubleArray * [ numArays ];
  attrPtrs[0] = vtkDoubleArray::SafeDownCast
                (  fragPoly->GetCellData()->GetArray( "MaterialVolume" )  )
                ->GetPointer( 0 );
  numComps[0] = 1;
  attrVals[0] = vtkDoubleArray::New();
  attrVals[0]->SetName( "MaterialVolume" );
  attrVals[0]->SetNumberOfComponents( 1 );
  attrVals[0]->SetNumberOfTuples( numCells );
  for ( i = 1; i < numArays; i ++ )
    {
    theArray    = vtkDoubleArray::SafeDownCast
                  (  fragPoly->GetCellData()->GetArray
                     ( this->Internal->IntegrableAttributeNames[ i - 1 ].c_str() )
                  );
    attrPtrs[i] = theArray->GetPointer( 0 );
    numComps[i] = theArray->GetNumberOfComponents();
    attrVals[i] = vtkDoubleArray::New();
    attrVals[i]->SetName( theArray->GetName() );
    attrVals[i]->SetNumberOfComponents( numComps[i] );
    attrVals[i]->SetNumberOfTuples( numCells );
    theArray    = NULL;
    }


  // fill the GlobalNodeId array
  for ( i = 0; i < numbPnts; i ++ )
    {
    fragPoly->GetPoint( i, pntCoord );
    gPtIdGen->InsertUniquePoint( pntCoord, globalId );
    uniPIdxs->SetComponent( i, 0, globalId );
    }


  // group the cells based on the fragment Id
  for ( i = 0; i < numCells; i ++ )
    {
    grpItrat = cellGrps.find( fIdxsPtr[i] );

    if ( grpItrat == cellGrps.end() )
      {
      // create a group (of cells) for this new fragment and add this cell to it
      std::vector< int >     cellGrup;
      cellGrup.push_back( i );
      cellGrps[ fIdxsPtr[i] ] = cellGrup;
      }
    else
      {
      // add this cell to the target group
      grpItrat->second.push_back( i );
      }
    }

  // retrieve the cells in group-wise order, copy their data attributes to
  // the output vtkPolyData, and obtain the size of the largest fragment
  // (in terms of the number of polygons in a fragment)
  cellIndx = 0;
  for ( grpItrat = cellGrps.begin(); grpItrat != cellGrps.end(); grpItrat ++ )
    {
    theGroup = &( grpItrat->second );
    maxFsize =  (  static_cast<int> ( theGroup->size() ) > maxFsize  )
                 ? static_cast<int> ( theGroup->size() ) : maxFsize;

    for ( cellItrt = theGroup->begin(); cellItrt != theGroup->end(); cellItrt ++ )
      {
      inCellId = *cellItrt;
      polygons->InsertNextCell(  fragPoly->GetCell( inCellId )->GetPointIds()  );
      uniFIdxs->SetComponent( cellIndx, 0, fIdxsPtr[inCellId] );
      for ( i = 0; i < numArays; i ++ )
        {
        attrVals[i]->SetTupleValue
                     ( cellIndx, attrPtrs[i] + inCellId * numComps[i] );
        }

      cellIndx ++;
      }

    theGroup->clear();
    theGroup = NULL;
    }
  cellGrps.clear();

  // fill the output vtkPolyData
  procPoly->SetPoints( polyPnts );
  procPoly->SetPolys( polygons );
  procPoly->GetPointData()->SetGlobalIds( uniPIdxs );
  procPoly->GetCellData()->AddArray( uniFIdxs );
  for ( i = 0; i < numArays; i ++ )
    {
    procPoly->GetCellData()->AddArray( attrVals[i] );
    attrVals[i]->Delete();
    attrVals[i] = NULL;
    attrPtrs[i] = NULL;
    }
  procPoly->Squeeze();

  // memory deallocation
  polyPnts->Delete();
  polygons->Delete();
  uniFIdxs->Delete();
  uniPIdxs->Delete();
  delete [] attrVals;
  delete [] attrPtrs;
  delete [] numComps;

  polyPnts = NULL;
  polygons = NULL;
  uniFIdxs = NULL;
  uniPIdxs = NULL;
  attrVals = NULL;
  fIdxsPtr = NULL;
  attrVals = NULL;
  attrPtrs = NULL;
  numComps = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::AddInterProcessPolygonsToFaceHash
  ( vtkPolyData ** procPlys, int * maxFsize, int numProcs )
{
  if ( !procPlys || !maxFsize )
    {
    vtkErrorMacro( "Input vtkPolyData array (procPlys) or maxFsize NULL." << endl );
    return;
    }

  int              i, j, k, a, c;
  int              theShift = 0;
  int              bufIndex = 0;
  int              numArays = 0;
  int              tupleSiz = 0; // number of integrated components
  int              newIndex = 0; // index of a new face of a local fragment
  int              minIndex = 1; // the smallest (inter-process) fragment Id
  int              fragIndx = 1; // next inter-process fragment Id and 0 for
                                 // removing faces
  int *            fIdxsPtr = NULL; // array of local fragment Ids
  int            * numComps = NULL; // number of integrated components
  double  *        tupleBuf = NULL; // integrated component values
  double        ** attrPtrs = NULL;
  vtkCell *        thisFace = NULL; // a 2D polygon (not a 3D cell)
  vtkIdType        numFaces = 0; // number of 2D polygons of an input vtkPolyData
  vtkIdType        procFIdx = 0; // Id of the local fragment being processed
  vtkIdType        numbPnts = 0; // for a face / polygon
  vtkIdType        pointIds[5];  // point Ids of a face (at most 5 points)
  vtkIdType *      pIdxsPtr = NULL; // array of point Ids
  vtkDoubleArray * theArray = NULL;
  vtkRectilinearGridConnectivityFace *  hashFace = NULL; // a face in the hash
  vtkRectilinearGridConnectivityFace ** newFaces = NULL;


  // determine the number of integrated components (including the material
  // volume) to be saved to the global fragment attributes array and allocate
  // a buffer for a tuple
  tupleSiz = this->Internal->NumberIntegralComponents + 1;
  tupleBuf = new double [ tupleSiz ];
  memset(  tupleBuf,  0,  sizeof( double ) * tupleSiz  );


  // allocate pointers for access to the arrays of integrated attributes
  numArays = int( this->Internal->IntegrableAttributeNames.size() ) + 1;
  numComps = new int      [ numArays ];
  attrPtrs = new double * [ numArays ];
  numComps[0] = 1;
  attrPtrs[0] = NULL;
  for ( a = 1; a < numArays; a ++ )
    {
    numComps[a] = 0;
    attrPtrs[a] = NULL;
    }


  // process each vtkPolyData and add 2D polygons (faces) to the global hash
  for ( j = 0; j < numProcs; j ++ )
    {
    // each vtkPolyData stores individual 2D polygons (triangles, quads, and
    // pentagons) of which each is though coupled with a local / process-based
    // fragment Id as the cell data attribute to convey the connectivity of
    // the exterior polygons of the same fragment --- 'macro volume'

    // Make sure this vtkPolyData has global node Ids, block Ids, fragment Ids,
    // and volume Ids. The vtkPolyData returned by a process (possibly assigned
    // with no any block at all due to the number of processes greater than that
    // of blocks) after local in-hash polygons-resolution may be just 'empty'.
    if (    vtkIdTypeArray::SafeDownCast
            (  procPlys[j]->GetPointData()->GetArray( "GlobalNodeId" )  )
            == NULL
         || vtkIntArray::SafeDownCast
            (  procPlys[j]->GetCellData()->GetArray( "FragmentId" )  )
            == NULL
         || vtkDoubleArray::SafeDownCast
            (  procPlys[j]->GetCellData()->GetArray( "MaterialVolume" )  )
            == NULL
         || !this->Internal->IntegrableCellDataArraysAvailable( procPlys[j] )
       )
      {
      vtkDebugMacro( << "Point data GlobalNodeId, cell data FragmentId, "
                     << "MaterialVolume, or integrable fragment "
                     << "attributes not found in vtkPolyData #" << j << endl );
      continue;
      }

    // gain access to global node Ids, block Ids, fragment Ids, and volume Ids
    pIdxsPtr    = vtkIdTypeArray::SafeDownCast
                  (  procPlys[j]->GetPointData()->GetArray( "GlobalNodeId" )  )
                  ->GetPointer( 0 );
    fIdxsPtr    = vtkIntArray::SafeDownCast
                  (  procPlys[j]->GetCellData()->GetArray( "FragmentId" )  )
                  ->GetPointer( 0 );
    attrPtrs[0] = vtkDoubleArray::SafeDownCast
                  (  procPlys[j]->GetCellData()->GetArray( "MaterialVolume" )  )
                  ->GetPointer( 0 );
    for ( a = 1; a < numArays; a ++ )
      {
      theArray    = vtkDoubleArray::SafeDownCast
                    (  procPlys[j]->GetCellData()->GetArray
                       ( this->Internal->IntegrableAttributeNames[ a - 1 ].c_str() )
                    );
      attrPtrs[a] = theArray->GetPointer( 0 );
      numComps[a] = theArray->GetNumberOfComponents();
      theArray    = NULL;
      }


    // given the maximum size of a fragment, i.e., the maximum number of
    // faces per fragment in this vtkPolyData, allocate a buffer to maintain
    // the possible new faces of a single fragment
    newFaces = new vtkRectilinearGridConnectivityFace * [ maxFsize[j] ];
    for ( k = 0; k < maxFsize[j]; k ++ )
      {
      newFaces[k] = NULL;
      }

    i        = 0;
    procFIdx =-1;
    numFaces = procPlys[j]->GetNumberOfCells();
    while ( i < numFaces ) // for each individual 2D polygon (face)
      {
      // "0 < numFaces" guarantees pIdxsPtr and fIdxsPtr are not NULL

      // note that each cell is a 2D polygon (instead of a 3D cell) and we
      // have to use the cell data attribute, i.e., the local fragment Id,
      // to combine individual 2D polygons to reconstruct a 'macro volume'
      if ( fIdxsPtr[i] != procFIdx )
        {
        // the first face of a NEW 'macro' volume --- init some variables
        newIndex = 0;
        minIndex = fragIndx;
        procFIdx = fIdxsPtr[i]; // group faces via the local fragment Id

        // obtain the attribute values of the sub-volume via the first polygon
        bufIndex = 0;
        for ( a = 0; a < numArays;    a ++ )
          {
          theShift = i * numComps[a];
          for ( c = 0; c < numComps[a]; c ++ )
            {
            tupleBuf[ bufIndex ++ ] = attrPtrs[a][ theShift + c ];
            }
          }
        }

      while ( fIdxsPtr[i] == procFIdx ) // for each face of the 'macro' volume
        {
        // this is a face of the current 'macro' volume
        hashFace = NULL;
        thisFace = procPlys[j]->GetCell( i );
        numbPnts = thisFace->GetNumberOfPoints();

        // add the face to the hash
        if ( numbPnts == 3 )
          {
          pointIds[0] = pIdxsPtr[  thisFace->GetPointId( 0 )  ];
          pointIds[1] = pIdxsPtr[  thisFace->GetPointId( 1 )  ];
          pointIds[2] = pIdxsPtr[  thisFace->GetPointId( 2 )  ];
          hashFace    = this->FaceHash->AddFace
                        ( pointIds[0], pointIds[1], pointIds[2] );
          }
        else
        if ( numbPnts == 4 )
          {
          pointIds[0] = pIdxsPtr[  thisFace->GetPointId( 0 )  ];
          pointIds[1] = pIdxsPtr[  thisFace->GetPointId( 1 )  ];
          pointIds[2] = pIdxsPtr[  thisFace->GetPointId( 2 )  ];
          pointIds[3] = pIdxsPtr[  thisFace->GetPointId( 3 )  ];
          hashFace    = this->FaceHash->AddFace( pointIds[0], pointIds[1],
                                                 pointIds[2], pointIds[3] );
          }
        else
        if ( numbPnts == 5 )
          {
          pointIds[0] = pIdxsPtr[  thisFace->GetPointId( 0 )  ];
          pointIds[1] = pIdxsPtr[  thisFace->GetPointId( 1 )  ];
          pointIds[2] = pIdxsPtr[  thisFace->GetPointId( 2 )  ];
          pointIds[3] = pIdxsPtr[  thisFace->GetPointId( 3 )  ];
          pointIds[4] = pIdxsPtr[  thisFace->GetPointId( 4 )  ];
          hashFace    = this->FaceHash->AddFace( pointIds[0], pointIds[1],
                                    pointIds[2], pointIds[3], pointIds[4] );
          }
        else
          {
          hashFace    = NULL;
          vtkWarningMacro( "Face ignored due to invalid number of points." );
          }
        thisFace = NULL;

        if ( hashFace )
          {
          // this face has been added to the hash and it is not necessarily the
          // first time --- the same face may have been added to the hash as the
          // constituent polygon of another sub-volume ('macro') and in this case
          // this face is called an 'internal' face

          if ( hashFace->FragmentId > 0 )
            {
            // This is an internal face. It has been removed from the hash when
            // the hash attempts to accept it for the second time, though it is
            // accessible until a new face is allocated from the recycle bin.

            if ( hashFace->FragmentId != minIndex && minIndex < fragIndx )
              {
              // This face (X) is not the first one of this 'macro' volume (R,
              // otherwise minIndex == fragIndx would hold). In fact, there has
              // been a face (Y, of this 'macro' volume R) that is shared by this
              // 'macro' volume (R) and a second 'macro' volume (S, otherwise
              // minIndx == fragIndx would hold). In addition, this face (X) is
              // shared by this 'macro' volume (R) and a third 'macro' volume (T,
              // which though has not been merged with 'macro' volume S, otherwise
              // hashFace->FragmentId == minIndex would hold). In a word, this
              // 'macro' volume (R) is connected with two currently un-merged 'macro'
              // volumes S and T. Thus we need to make the fragment Ids of 'macro'
              // volumes S and T equivalent to each other.
              this->EquivalenceSet->AddEquivalence
                                    ( minIndex, hashFace->FragmentId );
              }

            // keep track of the smallest fragment id to use for this 'macro' volume
            if ( minIndex > hashFace->FragmentId ) // --- case A
              {
              // The first face (certainly internal, since hashFace->FragmentId
              // > 0 holds above) of this 'macro' volume is guranteed to come here.
              // In addition, non-first internal faces (of this 'macro' volume) that
              // are shared by new 'macro' volumes also come here. In either case,
              // minIndex is updated below to reflect the smallest fragment Id so
              // far and will be assigned to those subsequent new faces of this
              // 'macro' volume.
              minIndex = hashFace->FragmentId;
              }
            }
          else
            {
            // this is a new face (hashFace->FragmentId is inited to be 0)
            hashFace->PolygonId = i;
            hashFace->ProcessId = j;

            // save this new face until we process all the faces of this
            // 'macro' volume to determine the smallest fragment id
            newFaces[ newIndex ++ ] = hashFace;

            } // end if a new face is added to the hash
          } // end if the input face is valid

        // process the next 2D polygon by updating the index of the face
        i ++;
        hashFace = NULL;

        } // for each face of a 'macro' volume

      // The current face (2D polygon) belongs to a new 'macro' volume. Before
      // processing it in the next cycle (for each separated 2D polygon), we
      // need to do some thing for the 'macro' volume that we have just recognized.

      if ( minIndex == fragIndx )
        {
        // This is an isolated 'macro' volume (possibly the first 'macro' volume
        // of a fragment) since no any neighboring 'macro' volume has been found
        // (otherwise minIndex would have been updated to be less than fragIndx
        // in case A above). The code below ensures the correct number of
        // equivalence members.
        this->EquivalenceSet->AddEquivalence( fragIndx, fragIndx );
        fragIndx ++;
        }

      // update the smallest fragment Id used so far
      minIndex = this->EquivalenceSet->GetEquivalentSetId( minIndex );

      // Label the new faces of the 'macro' volume with the final (smallest)
      // fragment id.
      for ( k = 0; k < newIndex; k ++ )
        {
        newFaces[k]->FragmentId = minIndex;
        }

      // fragment attributes integration
      this->IntegrateFragmentAttributes( minIndex, tupleSiz, tupleBuf );

      } // for each individual 2D polygon

    // clean up the buffer of new faces
    for ( k = 0; k < maxFsize[j]; k ++ )
      {
      newFaces[k] = NULL;
      }
    delete [] newFaces;
    newFaces = NULL;

    for ( a = 0; a < numArays; a ++ )
      {
      attrPtrs[a] = NULL;
      }
    pIdxsPtr = NULL;
    fIdxsPtr = NULL;
    } // for each input vtkPolyData

  delete [] attrPtrs;
  delete [] numComps;
  delete [] tupleBuf;
  attrPtrs = NULL;
  numComps = NULL;
  tupleBuf = NULL;
}

//-----------------------------------------------------------------------------
void vtkRectilinearGridConnectivity::GenerateOutputFromMultiProcesses
  ( vtkPolyData ** procPlys, int numProcs,
    unsigned char partIndx, vtkPolyData * polyData )
{
  // Access the global face hash to obtain the exterior surfaces (with non-zero
  // fragment Ids) of the fragments extracted by multiple processes and export
  // these final exterior surfaces to a vtkPolyData.

  if ( !procPlys || !polyData )
    {
    vtkErrorMacro( << "Input vtkPolyData array (procPlys) or output vtkPolyData "
                   << "(polyData) NULL." << endl );
    return;
    }

  int               i, j;
  int               theShift;
  int               numArays;
  int               tupleSiz;        // number of integrated components
  int               degnerat;
  int               numbPnts;        // for an exterior face
  int             * numComps = NULL;
  double            pntCoord[3];
  double          * tupleBuf    = NULL; // integrated component values
  double          * rcBounds    = NULL;
  double            mbBounds[6] = { VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
                                    VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
                                    VTK_DOUBLE_MAX, VTK_DOUBLE_MIN
                                  };
  vtkCell         * faceCell = NULL; // a polygon of a vtkPolyData
  vtkIdType         facePIds[5];     // point Ids of a polygon (at most 5)
  vtkPoints       * surfPnts = NULL; // vtkPoints attached to a vtkPolyData
  vtkPoints       * polyPnts = NULL;
  vtkIntArray     * fragIdxs = NULL;
  vtkIntArray     * procIdxs = NULL;
  vtkCellArray    * polygons = NULL;
  vtkDoubleArray ** attrVals = NULL;
  vtkUnsignedCharArray               * partIdxs = NULL;
  vtkIncrementalOctreePointLocator   * pntAdder = NULL;
  vtkRectilinearGridConnectivityFace * thisFace = NULL;

  // output points and polygons (cells)
  polyPnts = vtkPoints::New();
  polygons = vtkCellArray::New();

  // array of fragment Ids (one per constituent polygon)
  fragIdxs = vtkIntArray::New();
  fragIdxs->SetName( "FragmentId" );

  // array of process Ids (one per constituent polygon)
  procIdxs = vtkIntArray::New();
  procIdxs->SetName( "ProcessId" );

  // array of part indices (one per constituent polygon, part index is
  // determined by the material volume fraction index)
  partIdxs = vtkUnsignedCharArray::New();
  partIdxs->SetName( "Part Index" );


  // allocate a buffer for a tuple of integrated component values (including
  // the material volume) to be extracted from the global fragment attributes
  // array and create a set of vtkDoubleArray objects to collect the integrated
  // attribute values which are attached to the output polygons as the cell data
  tupleSiz    = this->Internal->NumberIntegralComponents + 1;
  numArays    = int( this->Internal->IntegrableAttributeNames.size() ) + 1;
  tupleBuf    = new      double      [ tupleSiz ];
  numComps    = new       int        [ numArays ];
  attrVals    = new vtkDoubleArray * [ numArays ];
  numComps[0] = 1;
  attrVals[0] = vtkDoubleArray::New();
  attrVals[0]->SetName( "MaterialVolume" );
  attrVals[0]->SetNumberOfComponents( 1 );
  for ( i = 1; i < numArays; i ++ )
    {
    numComps[i] = this->Internal->ComponentNumbersPerArray[ i - 1 ];
    attrVals[i] = vtkDoubleArray::New();
    attrVals[i]->SetName( this->Internal
                              ->IntegrableAttributeNames[ i - 1 ].c_str() );
    attrVals[i]->SetNumberOfComponents( numComps[i] );
    }


  // create a point locator to maintain all the points of the output
  pntAdder = vtkIncrementalOctreePointLocator::New();
  for ( i = 0; i < numProcs; i ++ )
    {
    rcBounds    = procPlys[i]->GetBounds();
    mbBounds[0] = ( rcBounds[0] < mbBounds[0] ) ? rcBounds[0] : mbBounds[0];
    mbBounds[2] = ( rcBounds[2] < mbBounds[2] ) ? rcBounds[2] : mbBounds[2];
    mbBounds[4] = ( rcBounds[4] < mbBounds[4] ) ? rcBounds[4] : mbBounds[4];
    mbBounds[1] = ( rcBounds[1] > mbBounds[1] ) ? rcBounds[1] : mbBounds[1];
    mbBounds[3] = ( rcBounds[3] > mbBounds[3] ) ? rcBounds[3] : mbBounds[3];
    mbBounds[5] = ( rcBounds[5] > mbBounds[5] ) ? rcBounds[5] : mbBounds[5];
    rcBounds    = NULL;
    }
  pntAdder->SetTolerance( 0.0001 );
  pntAdder->InitPointInsertion( polyPnts, mbBounds, 10000 );

  // retrieve each face maintained in the global hash
  this->FaceHash->InitTraversal();
  while (  ( thisFace = this->FaceHash->GetNextFace() )  )
    {
    // Skip the faces masked (with 0) by the inter-process resolution.
    // Any face with a zero fragment index is an internal face and
    // will not be present in the output.
    if ( thisFace->FragmentId > 0 )
      {
      // This is an exterior face of some fragment. Note that we have
      // re-defined the meaning of vtkRectilinearGridConnectivityFace::
      // PolygonId, which is just the polygon Id in the associated vtkPolyData
      // (specified by ProcessId --- not BlockId any more).
      surfPnts = procPlys[ thisFace->ProcessId ]->GetPoints();
      faceCell = procPlys[ thisFace->ProcessId ]->GetCell( thisFace->PolygonId );

      numbPnts = faceCell->GetNumberOfPoints();
      if ( numbPnts > 5 )
        {
        numbPnts = 5;
        vtkWarningMacro( << "Not triangle, quad, or pentagon." << endl );
        }

      // add the points of the polygon to the output vtkPolyData
      for ( i = 0; i < numbPnts; i ++ )
        {
        surfPnts->GetPoint(  faceCell->GetPointId( i ),  pntCoord  );
        pntAdder->InsertUniquePoint( pntCoord, facePIds[i] );
        }

      // As we are now collecting polygons for rendering, polygons that
      // degenerate to lines or points need to be rejected. This rejection
      // does not affect the fragment extraction result while guaranteeing
      // the generation of cell normals.
      //
      // It is assumed that polygon degenration seldom ocurs and therefore
      // the lack of early exit (when evaluating an if-statement) does not
      // cause a negative effect while the use of a single comparison can
      // speed up the whole check.
      degnerat = 0;
      for ( i = 0;     i < numbPnts - 1; i ++ )
      for ( j = i + 1; j < numbPnts;     j ++ )
        {
        degnerat += static_cast<int> (  !( facePIds[i] - facePIds[j] )  );
        }

      if ( numbPnts - degnerat >= 3 )
        {
        // it is a triangle, quad, or pentagon (line is rejected)

        // add the original polygon and the associated cell data attributes of
        // interest to the output vtkPolyData
        polygons->InsertNextCell( numbPnts, facePIds );
        fragIdxs->InsertNextValue( thisFace->FragmentId );
        procIdxs->InsertNextValue( thisFace->ProcessId );
        partIdxs->InsertNextValue( partIndx );
        this->FragmentValues->GetTupleValue( thisFace->FragmentId, tupleBuf );
        for ( theShift = 0, i = 0; i < numArays; i ++ )
          {
          attrVals[i]->InsertNextTupleValue( tupleBuf + theShift );
          theShift += numComps[i];
          }
        }

      } // end if it is an exterior face
    } // end loop over faces in the hash

  thisFace = NULL;
  surfPnts = NULL;
  faceCell = NULL;

  // fill the output vtkPolyData
  polyData->SetPoints( polyPnts );
  polyData->SetPolys ( polygons );
  polyData->GetCellData()->AddArray( fragIdxs );
  polyData->GetCellData()->AddArray( procIdxs );
  polyData->GetCellData()->AddArray( partIdxs );
  for ( i = 0; i < numArays; i ++ )
    {
    polyData->GetCellData()->AddArray( attrVals[i] );
    attrVals[i]->Delete();
    attrVals[i] = NULL;
    }
  polyData->Squeeze();

  // memory deallocation
  pntAdder->Delete();
  polyPnts->Delete();
  polygons->Delete();
  fragIdxs->Delete();
  procIdxs->Delete();
  partIdxs->Delete();
  delete [] attrVals;
  delete [] numComps;
  delete [] tupleBuf;

  pntAdder = NULL;
  polyPnts = NULL;
  polygons = NULL;
  fragIdxs = NULL;
  procIdxs = NULL;
  partIdxs = NULL;
  attrVals = NULL;
  numComps = NULL;
  tupleBuf = NULL;
}

//-----------------------------------------------------------------------------
// An extended marching cubes case table for generating cube faces (either
// truncated by iso-lines or not) in addition to iso-triangles. These two
// kinds of polygons in combination represent the surface(s) of the greater-
// than-isovalue sub-volume(s) extracted in a cube. An index in each entry
// may refer to an Interpolated Iso-Value Point (IIVP, i.e., the associated
// edge, 0 ~ 11) or one of the eight Vertices Of the Cube (VOC: 12 ~ 19).
// The constituent polygons (triangles, quads, or pentagons) of a sub-volume
// are listed successively, ending with flag -1 in sepration from another sub-
// volume that may follows. Each polygon begins with the number of the points
// followed by the specific IIVP-Ids and VOC-Ids. Flag -2 terminates the list
// of all sub-volumes, if any. The two integers in the annotation section of
// each entry indicate the case number (0 ~ 255) and the base case number (0
// ~ 15), respectively.

static vtkRectilinearGridConnectivityMarchingCubesVolumeCases
    RECTILINEAR_GRID_CONNECTIVITY_MARCHING_CUBES_VOLUME_CASES[] = {
{{ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   0     0 */
{{  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   1     1 */
{{  3,  0,  9,  1,  3,  0, 13,  9,  3,  9, 13,  1,  3,  1, 13,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   2     1 */
{{  3,  1,  3,  8,  3,  9,  1,  8,  3,  1,  9, 13,  3,  8,  3, 12,  4,  1, 13, 12,  3,  4,  8, 12, 13,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   3     2 */
{{  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   4     1 */
{{  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -1,  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   5     3 */
{{  3,  9, 11,  2,  3,  0,  9,  2,  3,  9,  0, 13,  3,  2, 11, 14,  4,  9, 13, 14, 11,  4,  2, 14, 13,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   6     2 */
{{  3,  2,  3,  8,  3,  2,  8, 11,  3, 11,  8,  9,  3,  2, 11, 14,  3,  8,  3, 12,  4,  9, 13, 14, 11,  4,  8, 12, 13,  9,  5,  2, 14, 13, 12,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   7     5 */
{{  3,  3,  2, 10,  3,  3, 15,  2,  3,  2, 15, 10,  3, 10, 15,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   8     1 */
{{  3,  0,  2, 10,  3,  8,  0, 10,  3,  0,  8, 12,  3, 10,  2, 15,  4,  0, 12, 15,  2,  4, 10, 15, 12,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*   9     2 */
{{  3,  1,  0,  9,  3,  1, 13,  0,  3,  0, 13,  9,  3,  9, 13,  1, -1,  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  10     3 */
{{  3,  1,  2, 10,  3,  1, 10,  9,  3,  9, 10,  8,  3,  1,  9, 13,  3, 10,  2, 15,  4,  8, 12, 13,  9,  4, 10, 15, 12,  8,  5,  1, 13, 12, 15,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  11     5 */
{{  3,  3,  1, 11,  3, 10,  3, 11,  3,  3, 10, 15,  3, 11,  1, 14,  4,  3, 15, 14,  1,  4, 11, 14, 15, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  12     2 */
{{  3,  0,  1, 11,  3,  0, 11,  8,  3,  8, 11, 10,  3,  0,  8, 12,  3, 11,  1, 14,  4, 10, 15, 12,  8,  4, 11, 14, 15, 10,  5,  0, 12, 15, 14,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  13     5 */
{{  3,  3,  0,  9,  3,  3,  9, 10,  3, 10,  9, 11,  3,  3, 10, 15,  3,  9,  0, 13,  4, 11, 14, 15, 10,  4,  9, 13, 14, 11,  5,  3, 15, 14, 13,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  14     5 */
{{  3,  9, 11,  8,  3, 11, 10,  8,  4, 13, 12, 15, 14,  4,  9, 13, 14, 11,  4, 11, 14, 15, 10,  4, 10, 15, 12,  8,  4,  8, 12, 13,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  15     8 */
{{  3,  4,  8,  7,  3,  4, 16,  8,  3,  8, 16,  7,  3,  7, 16,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  16     1 */
{{  3,  4,  0,  3,  3,  7,  4,  3,  3,  4,  7, 16,  3,  3,  0, 12,  4,  4, 16, 12,  0,  4,  3, 12, 16,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  17     2 */
{{  3,  0,  9,  1,  3,  0, 13,  9,  3,  9, 13,  1,  3,  1, 13,  0, -1,  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  18     3 */
{{  3,  4,  9,  1,  3,  4,  1,  7,  3,  7,  1,  3,  3,  4,  7, 16,  3,  1,  9, 13,  4,  3, 12, 16,  7,  4,  1, 13, 12,  3,  5,  4, 16, 12, 13,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  19     5 */
{{  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -1,  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  20     4 */
{{  3,  3,  7,  4,  3,  3,  4,  0,  3,  7, 16,  4,  3,  0, 12,  3,  4,  4, 16, 12,  0,  4,  3, 12, 16,  7, -1,  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  21     6 */
{{  3,  9, 11,  2,  3,  9,  2,  0,  3, 11, 14,  2,  3,  0, 13,  9,  4,  2, 14, 13,  0,  4,  9, 13, 14, 11, -1,  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  22     6 */
{{  3,  2,  9, 11,  3,  2,  7,  9,  3,  2,  3,  7,  3,  7,  4,  9,  3, 11, 14,  2,  3,  7, 16,  4,  4,  3, 12, 16,  7,  4,  9, 13, 14, 11,  5,  3,  2, 14, 13, 12,  5,  9,  4, 16, 12, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  23    11 */
{{  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -1,  3,  3,  2, 10,  3,  3, 15,  2,  3,  2, 15, 10,  3, 10, 15,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  24     3 */
{{  3, 10,  7,  4,  3, 10,  4,  2,  3,  2,  4,  0,  3, 10,  2, 15,  3,  4,  7, 16,  4,  0, 12, 15,  2,  4,  4, 16, 12,  0,  5, 10, 15, 12, 16,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  25     5 */
{{  3,  9,  1,  0,  3,  9, 13,  1,  3,  1, 13,  0,  3,  0, 13,  9, -1,  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -1,  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  26     7 */
{{  3,  4, 10,  7,  3,  9, 10,  4,  3,  9,  2, 10,  3,  9,  1,  2,  3,  9, 13,  1,  3,  2, 15, 10,  3,  7, 16,  4,  5,  2,  1, 13, 12, 15,  5,  9,  4, 16, 12, 13,  5,  7, 10, 15, 12, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  27     9 */
{{  3,  3,  1, 11,  3,  3, 11, 10,  3,  1, 14, 11,  3, 10, 15,  3,  4, 11, 14, 15, 10,  4,  3, 15, 14,  1, -1,  3,  7,  4,  8,  3,  7, 16,  4,  3,  4, 16,  8,  3,  8, 16,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  28     6 */
{{  3,  1, 11, 10,  3,  1, 10,  4,  3,  1,  4,  0,  3,  7,  4, 10,  3,  1, 14, 11,  3,  7, 16,  4,  4, 11, 14, 15, 10,  4,  4, 16, 12,  0,  5,  1,  0, 12, 15, 14,  5,  7, 10, 15, 12, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  29    14 */
{{  3,  4,  8,  7,  3,  4, 16,  8,  3,  8, 16,  7,  3,  7, 16,  4, -1,  3,  9, 10,  0,  3,  9, 11, 10,  3, 10,  3,  0,  3,  0, 13,  9,  3, 10, 15,  3,  4,  9, 13, 14, 11,  4, 11, 14, 15, 10,  5,  0,  3, 15, 14, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  30    12 */
{{  3,  4, 10,  7,  3,  4,  9, 10,  3,  9, 11, 10,  3,  7, 16,  4,  4, 13, 12, 15, 14,  4,  9, 13, 14, 11,  4, 11, 14, 15, 10,  5,  9,  4, 16, 12, 13,  5,  7, 10, 15, 12, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  31     5 */
{{  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  32     1 */
{{  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -1,  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  33     3 */
{{  3,  0,  4,  5,  3,  1,  0,  5,  3,  0,  1, 13,  3,  5,  4, 17,  4,  0, 13, 17,  4,  4,  5, 17, 13,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  34     2 */
{{  3,  8,  4,  5,  3,  8,  5,  3,  3,  3,  5,  1,  3,  8,  3, 12,  3,  5,  4, 17,  4,  1, 13, 12,  3,  4,  5, 17, 13,  1,  5,  8, 12, 13, 17,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  35     5 */
{{  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -1,  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  36     3 */
{{  3,  3,  8,  0,  3,  3, 12,  8,  3,  8, 12,  0,  3,  0, 12,  3, -1,  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -1,  3,  4,  5,  9,  3,  4, 17,  5,  3,  5, 17,  9,  3,  9, 17,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  37     7 */
{{  3,  5, 11,  2,  3,  5,  2,  4,  3,  4,  2,  0,  3,  5,  4, 17,  3,  2, 11, 14,  4,  0, 13, 17,  4,  4,  2, 14, 13,  0,  5,  5, 17, 13, 14, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  38     5 */
{{  3,  2,  5, 11,  3,  3,  5,  2,  3,  3,  4,  5,  3,  3,  8,  4,  3,  3, 12,  8,  3,  4, 17,  5,  3, 11, 14,  2,  5,  4,  8, 12, 13, 17,  5,  3,  2, 14, 13, 12,  5, 11,  5, 17, 13, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  39     9 */
{{  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -1,  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  40     4 */
{{  3,  0,  2, 10,  3,  0, 10,  8,  3,  2, 15, 10,  3,  8, 12,  0,  4, 10, 15, 12,  8,  4,  0, 12, 15,  2, -1,  3,  4,  5,  9,  3,  4, 17,  5,  3,  5, 17,  9,  3,  9, 17,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  41     6 */
{{  3,  0,  4,  5,  3,  0,  5,  1,  3,  4, 17,  5,  3,  1, 13,  0,  4,  5, 17, 13,  1,  4,  0, 13, 17,  4, -1,  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  42     6 */
{{  3,  2,  5,  1,  3,  2,  8,  5,  3,  2, 10,  8,  3,  4,  5,  8,  3,  2, 15, 10,  3,  4, 17,  5,  4, 10, 15, 12,  8,  4,  5, 17, 13,  1,  5,  2,  1, 13, 12, 15,  5,  4,  8, 12, 13, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  43    14 */
{{  3, 11, 10,  3,  3, 11,  3,  1,  3, 10, 15,  3,  3,  1, 14, 11,  4,  3, 15, 14,  1,  4, 11, 14, 15, 10, -1,  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  44     6 */
{{  3,  4,  5,  9,  3,  4, 17,  5,  3,  5, 17,  9,  3,  9, 17,  4, -1,  3,  0,  1,  8,  3,  8,  1, 11,  3,  8, 11, 10,  3,  1, 14, 11,  3,  8, 12,  0,  4, 11, 14, 15, 10,  4, 10, 15, 12,  8,  5,  1,  0, 12, 15, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  45    12 */
{{  3,  5,  0,  4,  3,  5, 10,  0,  3,  5, 11, 10,  3, 10,  3,  0,  3,  4, 17,  5,  3, 10, 15,  3,  4, 11, 14, 15, 10,  4,  0, 13, 17,  4,  5, 11,  5, 17, 13, 14,  5,  0,  3, 15, 14, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  46    11 */
{{  3,  5,  8,  4,  3,  5, 11,  8,  3, 11, 10,  8,  3,  4, 17,  5,  4, 14, 13, 12, 15,  4, 11, 14, 15, 10,  4, 10, 15, 12,  8,  5, 11,  5, 17, 13, 14,  5,  4,  8, 12, 13, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  47     5 */
{{  3,  9,  8,  7,  3,  5,  9,  7,  3,  9,  5, 17,  3,  7,  8, 16,  4,  9, 17, 16,  8,  4,  7, 16, 17,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  48     2 */
{{  3,  9,  0,  3,  3,  9,  3,  5,  3,  5,  3,  7,  3,  9,  5, 17,  3,  3,  0, 12,  4,  7, 16, 17,  5,  4,  3, 12, 16,  7,  5,  9, 17, 16, 12,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  49     5 */
{{  3,  0,  8,  7,  3,  0,  7,  1,  3,  1,  7,  5,  3,  0,  1, 13,  3,  7,  8, 16,  4,  5, 17, 13,  1,  4,  7, 16, 17,  5,  5,  0, 13, 17, 16,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  50     5 */
{{  3,  1,  3,  5,  3,  3,  7,  5,  4, 13, 17, 16, 12,  4,  1, 13, 12,  3,  4,  3, 12, 16,  7,  4,  7, 16, 17,  5,  4,  5, 17, 13,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  51     8 */
{{  3,  9,  8,  7,  3,  9,  7,  5,  3,  8, 16,  7,  3,  5, 17,  9,  4,  7, 16, 17,  5,  4,  9, 17, 16,  8, -1,  3, 11,  2,  1,  3, 11, 14,  2,  3,  2, 14,  1,  3,  1, 14, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  52     6 */
{{  3, 11,  2,  1,  3, 11, 14,  2,  3,  2, 14,  1,  3,  1, 14, 11, -1,  3,  9,  0,  5,  3,  5,  0,  3,  3,  5,  3,  7,  3,  0, 12,  3,  3,  5, 17,  9,  4,  3, 12, 16,  7,  4,  7, 16, 17,  5,  5,  0,  9, 17, 16, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  53    12 */
{{  3,  8,  2,  0,  3,  8,  5,  2,  3,  8,  7,  5,  3, 11,  2,  5,  3,  8, 16,  7,  3, 11, 14,  2,  4,  7, 16, 17,  5,  4,  2, 14, 13,  0,  5,  8,  0, 13, 17, 16,  5, 11,  5, 17, 13, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  54    14 */
{{  3,  2,  5, 11,  3,  2,  3,  5,  3,  3,  7,  5,  3, 11, 14,  2,  4, 12, 13, 17, 16,  4,  3, 12, 16,  7,  4,  7, 16, 17,  5,  5,  3,  2, 14, 13, 12,  5, 11,  5, 17, 13, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  55     5 */
{{  3,  7,  5,  9,  3,  7,  9,  8,  3,  5, 17,  9,  3,  8, 16,  7,  4,  9, 17, 16,  8,  4,  7, 16, 17,  5, -1,  3,  3,  2, 10,  3,  3, 15,  2,  3,  2, 15, 10,  3, 10, 15,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  56     6 */
{{  3,  9,  7,  5,  3,  9,  2,  7,  3,  9,  0,  2,  3,  2, 10,  7,  3,  5, 17,  9,  3,  2, 15, 10,  4,  0, 12, 15,  2,  4,  7, 16, 17,  5,  5,  0,  9, 17, 16, 12,  5,  7, 10, 15, 12, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  57    11 */
{{  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -1,  3,  0,  8,  1,  3,  1,  8,  7,  3,  1,  7,  5,  3,  8, 16,  7,  3,  1, 13,  0,  4,  7, 16, 17,  5,  4,  5, 17, 13,  1,  5,  8,  0, 13, 17, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  58    12 */
{{  3, 10,  1,  2,  3, 10,  7,  1,  3,  7,  5,  1,  3,  2, 15, 10,  4, 16, 12, 13, 17,  4,  7, 16, 17,  5,  4,  5, 17, 13,  1,  5,  7, 10, 15, 12, 16,  5,  2,  1, 13, 12, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  59     5 */
{{  3,  9,  8,  5,  3,  8,  7,  5,  3,  5, 17,  9,  3,  8, 16,  7,  4,  9, 17, 16,  8,  4,  7, 16, 17,  5, -1,  3, 11,  3,  1,  3, 11, 10,  3,  3,  1, 14, 11,  3, 10, 15,  3,  4, 11, 14, 15, 10,  4,  3, 15, 14,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  60    10 */
{{  3,  5,  0,  7,  3,  5,  9,  0,  3,  7,  0, 10,  3,  1, 11,  0,  3, 10,  0, 11,  3,  5, 17,  9,  3,  1, 14, 11,  4, 11, 14, 15, 10,  4,  7, 16, 17,  5,  5,  7, 10, 15, 12, 16,  5,  0,  9, 17, 16, 12,  5,  1,  0, 12, 15, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  61     6 */
{{  3, 10,  0, 11,  3, 10,  3,  0,  3, 11,  0,  5,  3,  8,  7,  0,  3,  5,  0,  7,  3, 10, 15,  3,  3,  8, 16,  7,  4,  7, 16, 17,  5,  4, 11, 14, 15, 10,  5, 11,  5, 17, 13, 14,  5,  0,  3, 15, 14, 13,  5,  8,  0, 13, 17, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  62     6 */
{{  3, 10,  5, 11,  3,  7,  5, 10,  4, 15, 14, 13, 12,  4, 12, 13, 17, 16,  4, 10, 11, 14, 15,  4,  5,  7, 16, 17,  5, 10, 15, 12, 16,  7,  5,  5, 17, 13, 14, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  63     2 */
{{  3, 11,  5,  6,  3, 11, 18,  5,  3,  5, 18,  6,  3,  6, 18, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  64     1 */
{{  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -1,  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  65     4 */
{{  3,  9,  1,  0,  3,  9, 13,  1,  3,  1, 13,  0,  3,  0, 13,  9, -1,  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  66     3 */
{{  3,  1,  3,  8,  3,  1,  8,  9,  3,  3, 12,  8,  3,  9, 13,  1,  4,  8, 12, 13,  9,  4,  1, 13, 12,  3, -1,  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  67     6 */
{{  3,  1,  5,  6,  3,  2,  1,  6,  3,  1,  2, 14,  3,  6,  5, 18,  4,  1, 14, 18,  5,  4,  6, 18, 14,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  68     2 */
{{  3,  1,  5,  6,  3,  1,  6,  2,  3,  5, 18,  6,  3,  2, 14,  1,  4,  6, 18, 14,  2,  4,  1, 14, 18,  5, -1,  3,  3,  8,  0,  3,  3, 12,  8,  3,  8, 12,  0,  3,  0, 12,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  69     6 */
{{  3,  9,  5,  6,  3,  9,  6,  0,  3,  0,  6,  2,  3,  9,  0, 13,  3,  6,  5, 18,  4,  2, 14, 13,  0,  4,  6, 18, 14,  2,  5,  9, 13, 14, 18,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  70     5 */
{{  3,  5,  8,  9,  3,  5,  2,  8,  3,  5,  6,  2,  3,  3,  8,  2,  3,  5, 18,  6,  3,  3, 12,  8,  4,  6, 18, 14,  2,  4,  8, 12, 13,  9,  5,  5,  9, 13, 14, 18,  5,  3,  2, 14, 13, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  71    14 */
{{  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -1,  3, 11,  5,  6,  3, 11, 18,  5,  3,  5, 18,  6,  3,  6, 18, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  72     3 */
{{  3, 10,  8,  0,  3, 10,  0,  2,  3,  8, 12,  0,  3,  2, 15, 10,  4,  0, 12, 15,  2,  4, 10, 15, 12,  8, -1,  3, 11,  5,  6,  3, 11, 18,  5,  3,  5, 18,  6,  3,  6, 18, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  73     6 */
{{  3,  0,  9,  1,  3,  0, 13,  9,  3,  9, 13,  1,  3,  1, 13,  0, -1,  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -1,  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  74     7 */
{{  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -1,  3,  1,  2,  9,  3,  9,  2, 10,  3,  9, 10,  8,  3,  2, 15, 10,  3,  9, 13,  1,  4, 10, 15, 12,  8,  4,  8, 12, 13,  9,  5,  2,  1, 13, 12, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  75    12 */
{{  3,  6, 10,  3,  3,  6,  3,  5,  3,  5,  3,  1,  3,  6,  5, 18,  3,  3, 10, 15,  4,  1, 14, 18,  5,  4,  3, 15, 14,  1,  5,  6, 18, 14, 15, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  76     5 */
{{  3,  0, 10,  8,  3,  0,  5, 10,  3,  0,  1,  5,  3,  5,  6, 10,  3,  8, 12,  0,  3,  5, 18,  6,  4,  1, 14, 18,  5,  4, 10, 15, 12,  8,  5,  1,  0, 12, 15, 14,  5, 10,  6, 18, 14, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  77    11 */
{{  3,  3,  6, 10,  3,  0,  6,  3,  3,  0,  5,  6,  3,  0,  9,  5,  3,  0, 13,  9,  3,  5, 18,  6,  3, 10, 15,  3,  5,  5,  9, 13, 14, 18,  5,  0,  3, 15, 14, 13,  5, 10,  6, 18, 14, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  78     9 */
{{  3,  6,  9,  5,  3,  6, 10,  9,  3, 10,  8,  9,  3,  5, 18,  6,  4, 15, 14, 13, 12,  4, 10, 15, 12,  8,  4,  8, 12, 13,  9,  5, 10,  6, 18, 14, 15,  5,  5,  9, 13, 14, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  79     5 */
{{  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -1,  3,  4,  8,  7,  3,  4, 16,  8,  3,  8, 16,  7,  3,  7, 16,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  80     3 */
{{  3,  4,  0,  3,  3,  4,  3,  7,  3,  0, 12,  3,  3,  7, 16,  4,  4,  3, 12, 16,  7,  4,  4, 16, 12,  0, -1,  3,  6, 11,  5,  3,  6, 18, 11,  3, 11, 18,  5,  3,  5, 18,  6, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  81     6 */
{{  3,  1,  0,  9,  3,  1, 13,  0,  3,  0, 13,  9,  3,  9, 13,  1, -1,  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -1,  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  82     7 */
{{  3, 11,  5,  6,  3, 11, 18,  5,  3,  5, 18,  6,  3,  6, 18, 11, -1,  3,  1,  7,  9,  3,  1,  3,  7,  3,  7,  4,  9,  3,  9, 13,  1,  3,  7, 16,  4,  4,  1, 13, 12,  3,  4,  3, 12, 16,  7,  5,  9,  4, 16, 12, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  83    12 */
{{  3,  6,  2,  1,  3,  6,  1,  5,  3,  2, 14,  1,  3,  5, 18,  6,  4,  1, 14, 18,  5,  4,  6, 18, 14,  2, -1,  3,  4,  8,  7,  3,  4, 16,  8,  3,  8, 16,  7,  3,  7, 16,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  84     6 */
{{  3,  1,  5,  2,  3,  5,  6,  2,  3,  2, 14,  1,  3,  5, 18,  6,  4,  1, 14, 18,  5,  4,  6, 18, 14,  2, -1,  3,  3,  4,  0,  3,  3,  7,  4,  3,  0, 12,  3,  3,  7, 16,  4,  4,  3, 12, 16,  7,  4,  4, 16, 12,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  85    10 */
{{  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -1,  3,  9,  5,  0,  3,  0,  5,  6,  3,  0,  6,  2,  3,  5, 18,  6,  3,  0, 13,  9,  4,  6, 18, 14,  2,  4,  2, 14, 13,  0,  5,  5,  9, 13, 14, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  86    12 */
{{  3,  7,  9,  3,  3,  7,  4,  9,  3,  3,  9,  2,  3,  5,  6,  9,  3,  2,  9,  6,  3,  7, 16,  4,  3,  5, 18,  6,  4,  6, 18, 14,  2,  4,  3, 12, 16,  7,  5,  3,  2, 14, 13, 12,  5,  9,  4, 16, 12, 13,  5,  5,  9, 13, 14, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  87     6 */
{{  3,  3,  2, 10,  3,  3, 15,  2,  3,  2, 15, 10,  3, 10, 15,  3, -1,  3,  7,  4,  8,  3,  7, 16,  4,  3,  4, 16,  8,  3,  8, 16,  7, -1,  3, 11,  5,  6,  3, 11, 18,  5,  3,  5, 18,  6,  3,  6, 18, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  88     7 */
{{  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -1,  3,  4,  2,  7,  3,  4,  0,  2,  3,  2, 10,  7,  3,  7, 16,  4,  3,  2, 15, 10,  4,  4, 16, 12,  0,  4,  0, 12, 15,  2,  5,  7, 10, 15, 12, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  89    12 */
{{  3,  0,  9,  1,  3,  0, 13,  9,  3,  9, 13,  1,  3,  1, 13,  0, -1,  3,  4,  8,  7,  3,  4, 16,  8,  3,  8, 16,  7,  3,  7, 16,  4, -1,  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -1,  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -2 }},    /*  90    13 */
{{  3,  9,  1,  2,  3,  9,  2, 10,  3,  9, 10,  4,  3,  7,  4, 10,  3,  9, 13,  1,  3,  2, 15, 10,  3,  7, 16,  4,  5,  9,  4, 16, 12, 13,  5,  2,  1, 13, 12, 15,  5,  7, 10, 15, 12, 16, -1,  3,  5,  6, 11,  3,  5, 18,  6,  3,  6, 18, 11,  3, 11, 18,  5, -2, -2, -2, -2, -2 }},    /*  91     7 */
{{  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -1,  3,  3,  5, 10,  3,  3,  1,  5,  3,  5,  6, 10,  3, 10, 15,  3,  3,  5, 18,  6,  4,  3, 15, 14,  1,  4,  1, 14, 18,  5,  5, 10,  6, 18, 14, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  92    12 */
{{  3,  5, 10,  1,  3,  5,  6, 10,  3,  1, 10,  0,  3,  7,  4, 10,  3,  0, 10,  4,  3,  5, 18,  6,  3,  7, 16,  4,  4,  4, 16, 12,  0,  4,  1, 14, 18,  5,  5,  1,  0, 12, 15, 14,  5, 10,  6, 18, 14, 15,  5,  7, 10, 15, 12, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  93     6 */
{{  3,  0,  9,  5,  3,  0,  5,  6,  3,  0,  6,  3,  3, 10,  3,  6,  3,  0, 13,  9,  3,  5, 18,  6,  3, 10, 15,  3,  5,  0,  3, 15, 14, 13,  5,  5,  9, 13, 14, 18,  5, 10,  6, 18, 14, 15, -1,  3,  8,  7,  4,  3,  8, 16,  7,  3,  7, 16,  4,  3,  4, 16,  8, -2, -2, -2, -2, -2 }},    /*  94     7 */
{{  3,  6,  9,  5,  3,  6, 10,  9,  3,  4,  9,  7,  3,  7,  9, 10,  3,  5, 18,  6,  3,  7, 16,  4,  4, 13, 12, 15, 14,  5,  9, 13, 14, 18,  5,  5,  6, 18, 14, 15, 10,  5, 10, 15, 12, 16,  7,  5,  4, 16, 12, 13,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  95     3 */
{{  3, 11,  9,  4,  3,  6, 11,  4,  3, 11,  6, 18,  3,  4,  9, 17,  4, 11, 18, 17,  9,  4,  4, 17, 18,  6, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  96     2 */
{{  3,  4,  6, 11,  3,  4, 11,  9,  3,  6, 18, 11,  3,  9, 17,  4,  4, 11, 18, 17,  9,  4,  4, 17, 18,  6, -1,  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  97     6 */
{{  3, 11,  1,  0,  3, 11,  0,  6,  3,  6,  0,  4,  3, 11,  6, 18,  3,  0,  1, 13,  4,  4, 17, 18,  6,  4,  0, 13, 17,  4,  5, 11, 18, 17, 13,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  98     5 */
{{  3,  8,  1,  3,  3,  8,  6,  1,  3,  8,  4,  6,  3,  6, 11,  1,  3,  3, 12,  8,  3,  6, 18, 11,  4,  4, 17, 18,  6,  4,  1, 13, 12,  3,  5,  4,  8, 12, 13, 17,  5,  1, 11, 18, 17, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /*  99    11 */
{{  3,  1,  9,  4,  3,  1,  4,  2,  3,  2,  4,  6,  3,  1,  2, 14,  3,  4,  9, 17,  4,  6, 18, 14,  2,  4,  4, 17, 18,  6,  5,  1, 14, 18, 17,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 100     5 */
{{  3,  3,  8,  0,  3,  3, 12,  8,  3,  8, 12,  0,  3,  0, 12,  3, -1,  3,  1,  9,  2,  3,  2,  9,  4,  3,  2,  4,  6,  3,  9, 17,  4,  3,  2, 14,  1,  4,  4, 17, 18,  6,  4,  6, 18, 14,  2,  5,  9,  1, 14, 18, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 101    12 */
{{  3,  0,  4,  2,  3,  4,  6,  2,  4, 13, 14, 18, 17,  4,  0, 13, 17,  4,  4,  4, 17, 18,  6,  4,  6, 18, 14,  2,  4,  2, 14, 13,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 102     8 */
{{  3,  8,  2,  3,  3,  8,  4,  2,  3,  4,  6,  2,  3,  3, 12,  8,  4, 17, 13, 14, 18,  4,  4, 17, 18,  6,  4,  6, 18, 14,  2,  5,  4,  8, 12, 13, 17,  5,  3,  2, 14, 13, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 103     5 */
{{  3, 11,  9,  4,  3, 11,  4,  6,  3,  9, 17,  4,  3,  6, 18, 11,  4,  4, 17, 18,  6,  4, 11, 18, 17,  9, -1,  3, 10,  3,  2,  3, 10, 15,  3,  3,  3, 15,  2,  3,  2, 15, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 104     6 */
{{  3,  0,  2,  8,  3,  2, 10,  8,  3,  8, 12,  0,  3,  2, 15, 10,  4,  0, 12, 15,  2,  4, 10, 15, 12,  8, -1,  3,  4, 11,  9,  3,  4,  6, 11,  3,  9, 17,  4,  3,  6, 18, 11,  4,  4, 17, 18,  6,  4, 11, 18, 17,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 105    10 */
{{  3,  3,  2, 10,  3,  3, 15,  2,  3,  2, 15, 10,  3, 10, 15,  3, -1,  3,  0,  6,  1,  3,  0,  4,  6,  3,  6, 11,  1,  3,  1, 13,  0,  3,  6, 18, 11,  4,  0, 13, 17,  4,  4,  4, 17, 18,  6,  5,  1, 11, 18, 17, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 106    12 */
{{  3,  6,  1,  4,  3,  6, 11,  1,  3,  4,  1,  8,  3,  2, 10,  1,  3,  8,  1, 10,  3,  6, 18, 11,  3,  2, 15, 10,  4, 10, 15, 12,  8,  4,  4, 17, 18,  6,  5,  4,  8, 12, 13, 17,  5,  1, 11, 18, 17, 13,  5,  2,  1, 13, 12, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 107     6 */
{{  3,  9,  4,  6,  3,  9,  6,  3,  3,  9,  3,  1,  3, 10,  3,  6,  3,  9, 17,  4,  3, 10, 15,  3,  4,  4, 17, 18,  6,  4,  3, 15, 14,  1,  5,  9,  1, 14, 18, 17,  5, 10,  6, 18, 14, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 108    14 */
{{  3,  8,  1, 10,  3,  8,  0,  1,  3, 10,  1,  6,  3,  9,  4,  1,  3,  6,  1,  4,  3,  8, 12,  0,  3,  9, 17,  4,  4,  4, 17, 18,  6,  4, 10, 15, 12,  8,  5, 10,  6, 18, 14, 15,  5,  1,  0, 12, 15, 14,  5,  9,  1, 14, 18, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 109     6 */
{{  3,  3,  6, 10,  3,  3,  0,  6,  3,  0,  4,  6,  3, 10, 15,  3,  4, 13, 14, 18, 17,  4,  0, 13, 17,  4,  4,  4, 17, 18,  6,  5,  0,  3, 15, 14, 13,  5, 10,  6, 18, 14, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 110     5 */
{{  3,  6,  8,  4,  3, 10,  8,  6,  4, 18, 17, 13, 14,  4, 14, 13, 12, 15,  4,  6,  4, 17, 18,  4,  8, 10, 15, 12,  5,  6, 18, 14, 15, 10,  5,  8, 12, 13, 17,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 111     2 */
{{  3,  7,  6, 11,  3,  7, 11,  8,  3,  8, 11,  9,  3,  7,  8, 16,  3, 11,  6, 18,  4,  9, 17, 16,  8,  4, 11, 18, 17,  9,  5,  7, 16, 17, 18,  6, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 112     5 */
{{  3,  0,  3,  7,  3,  0,  7, 11,  3,  0, 11,  9,  3,  6, 11,  7,  3,  0, 12,  3,  3,  6, 18, 11,  4,  3, 12, 16,  7,  4, 11, 18, 17,  9,  5,  0,  9, 17, 16, 12,  5,  6,  7, 16, 17, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 113    14 */
{{  3, 11,  7,  6,  3,  1,  7, 11,  3,  1,  8,  7,  3,  1,  0,  8,  3,  1, 13,  0,  3,  8, 16,  7,  3,  6, 18, 11,  5,  8,  0, 13, 17, 16,  5,  1, 11, 18, 17, 13,  5,  6,  7, 16, 17, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 114     9 */
{{  3, 11,  7,  6,  3, 11,  1,  7,  3,  1,  3,  7,  3,  6, 18, 11,  4, 13, 17, 16, 12,  4,  1, 13, 12,  3,  4,  3, 12, 16,  7,  5,  1, 11, 18, 17, 13,  5,  6,  7, 16, 17, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 115     5 */
{{  3,  1,  6,  2,  3,  1,  8,  6,  3,  1,  9,  8,  3,  8,  7,  6,  3,  2, 14,  1,  3,  8, 16,  7,  4,  9, 17, 16,  8,  4,  6, 18, 14,  2,  5,  9,  1, 14, 18, 17,  5,  6,  7, 16, 17, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 116    11 */
{{  3,  2,  9,  6,  3,  2,  1,  9,  3,  6,  9,  7,  3,  0,  3,  9,  3,  7,  9,  3,  3,  2, 14,  1,  3,  0, 12,  3,  4,  3, 12, 16,  7,  4,  6, 18, 14,  2,  5,  6,  7, 16, 17, 18,  5,  9,  1, 14, 18, 17,  5,  0,  9, 17, 16, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 117     6 */
{{  3,  7,  0,  8,  3,  7,  6,  0,  3,  6,  2,  0,  3,  8, 16,  7,  4, 18, 17, 13, 14,  4,  6, 18, 14,  2,  4,  2, 14, 13,  0,  5,  6,  7, 16, 17, 18,  5,  8,  0, 13, 17, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 118     5 */
{{  3,  7,  2,  3,  3,  6,  2,  7,  4, 16, 12, 13, 17,  4, 17, 13, 14, 18,  4,  7,  3, 12, 16,  4,  2,  6, 18, 14,  5,  7, 16, 17, 18,  6,  5,  2, 14, 13, 12,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 119     2 */
{{  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -1,  3, 11,  8,  6,  3, 11,  9,  8,  3,  8,  7,  6,  3,  6, 18, 11,  3,  8, 16,  7,  4, 11, 18, 17,  9,  4,  9, 17, 16,  8,  5,  6,  7, 16, 17, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 120    12 */
{{  3,  2,  7,  0,  3,  2, 10,  7,  3,  0,  7,  9,  3,  6, 11,  7,  3,  9,  7, 11,  3,  2, 15, 10,  3,  6, 18, 11,  4, 11, 18, 17,  9,  4,  0, 12, 15,  2,  5,  0,  9, 17, 16, 12,  5,  7, 10, 15, 12, 16,  5,  6,  7, 16, 17, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 121     6 */
{{  3,  1,  0,  8,  3,  1,  8,  7,  3,  1,  7, 11,  3,  6, 11,  7,  3,  1, 13,  0,  3,  8, 16,  7,  3,  6, 18, 11,  5,  1, 11, 18, 17, 13,  5,  8,  0, 13, 17, 16,  5,  6,  7, 16, 17, 18, -1,  3,  2, 10,  3,  3,  2, 15, 10,  3, 10, 15,  3,  3,  3, 15,  2, -2, -2, -2, -2, -2 }},    /* 122     7 */
{{  3, 10,  1,  2,  3, 10,  7,  1,  3, 11,  1,  6,  3,  6,  1,  7,  3,  2, 15, 10,  3,  6, 18, 11,  4, 13, 17, 16, 12,  5,  1, 13, 12, 15,  2,  5, 10, 15, 12, 16,  7,  5,  7, 16, 17, 18,  6,  5, 11, 18, 17, 13,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 123     3 */
{{  3,  8,  6,  9,  3,  8,  7,  6,  3,  9,  6,  1,  3, 10,  3,  6,  3,  1,  6,  3,  3,  8, 16,  7,  3, 10, 15,  3,  4,  3, 15, 14,  1,  4,  9, 17, 16,  8,  5,  9,  1, 14, 18, 17,  5,  6,  7, 16, 17, 18,  5, 10,  6, 18, 14, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 124     6 */
{{  3,  0,  1,  9,  3, 10,  7,  6,  5,  0, 12, 15, 14,  1,  5,  6, 18, 14, 15, 10,  5,  7, 16, 17, 18,  6,  5,  9, 17, 16, 12,  0,  5, 10, 15, 12, 16,  7,  5,  1, 14, 18, 17,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 125     4 */
{{  3,  7,  0,  8,  3,  7,  6,  0,  3,  3,  0, 10,  3, 10,  0,  6,  3,  8, 16,  7,  3, 10, 15,  3,  4, 13, 14, 18, 17,  5,  0, 13, 17, 16,  8,  5,  7, 16, 17, 18,  6,  5,  6, 18, 14, 15, 10,  5,  3, 15, 14, 13,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 126     3 */
{{  3,  7,  6, 10,  4, 12, 15, 14, 13,  4, 18, 17, 13, 14,  4, 13, 17, 16, 12,  5, 16, 17, 18,  6,  7,  5, 18, 14, 15, 10,  6,  5, 15, 12, 16,  7, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 127     1 */
{{  3,  7, 10,  6,  3,  7, 19, 10,  3, 10, 19,  6,  3,  6, 19,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 128     1 */
{{  3,  3,  8,  0,  3,  3, 12,  8,  3,  8, 12,  0,  3,  0, 12,  3, -1,  3, 10,  6,  7,  3, 10, 19,  6,  3,  6, 19,  7,  3,  7, 19, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 129     3 */
{{  3,  0,  9,  1,  3,  0, 13,  9,  3,  9, 13,  1,  3,  1, 13,  0, -1,  3, 10,  6,  7,  3, 10, 19,  6,  3,  6, 19,  7,  3,  7, 19, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 130     4 */
{{  3,  8,  9,  1,  3,  8,  1,  3,  3,  9, 13,  1,  3,  3, 12,  8,  4,  1, 13, 12,  3,  4,  8, 12, 13,  9, -1,  3, 10,  6,  7,  3, 10, 19,  6,  3,  6, 19,  7,  3,  7, 19, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 131     6 */
{{  3, 11,  2,  1,  3, 11, 14,  2,  3,  2, 14,  1,  3,  1, 14, 11, -1,  3,  6,  7, 10,  3,  6, 19,  7,  3,  7, 19, 10,  3, 10, 19,  6, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 132     3 */
{{  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -1,  3,  3,  8,  0,  3,  3, 12,  8,  3,  8, 12,  0,  3,  0, 12,  3, -1,  3,  6,  7, 10,  3,  6, 19,  7,  3,  7, 19, 10,  3, 10, 19,  6, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 133     7 */
{{  3,  2,  0,  9,  3,  2,  9, 11,  3,  0, 13,  9,  3, 11, 14,  2,  4,  9, 13, 14, 11,  4,  2, 14, 13,  0, -1,  3,  6,  7, 10,  3,  6, 19,  7,  3,  7, 19, 10,  3, 10, 19,  6, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 134     6 */
{{  3,  6,  7, 10,  3,  6, 19,  7,  3,  7, 19, 10,  3, 10, 19,  6, -1,  3,  2,  3, 11,  3, 11,  3,  8,  3, 11,  8,  9,  3,  3, 12,  8,  3, 11, 14,  2,  4,  8, 12, 13,  9,  4,  9, 13, 14, 11,  5,  3,  2, 14, 13, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 135    12 */
{{  3,  7,  3,  2,  3,  6,  7,  2,  3,  7,  6, 19,  3,  2,  3, 15,  4,  7, 19, 15,  3,  4,  2, 15, 19,  6, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 136     2 */
{{  3,  7,  8,  0,  3,  7,  0,  6,  3,  6,  0,  2,  3,  7,  6, 19,  3,  0,  8, 12,  4,  2, 15, 19,  6,  4,  0, 12, 15,  2,  5,  7, 19, 15, 12,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 137     5 */
{{  3,  2,  6,  7,  3,  2,  7,  3,  3,  6, 19,  7,  3,  3, 15,  2,  4,  7, 19, 15,  3,  4,  2, 15, 19,  6, -1,  3,  0,  9,  1,  3,  0, 13,  9,  3,  9, 13,  1,  3,  1, 13,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 138     6 */
{{  3,  1,  2,  6,  3,  1,  6,  8,  3,  1,  8,  9,  3,  8,  6,  7,  3,  9, 13,  1,  3,  6, 19,  7,  4,  2, 15, 19,  6,  4,  8, 12, 13,  9,  5,  2,  1, 13, 12, 15,  5,  8,  7, 19, 15, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 139    11 */
{{  3, 11,  6,  7,  3, 11,  7,  1,  3,  1,  7,  3,  3, 11,  1, 14,  3,  7,  6, 19,  4,  3, 15, 14,  1,  4,  7, 19, 15,  3,  5, 11, 14, 15, 19,  6, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 140     5 */
{{  3, 11,  6,  7,  3,  1, 11,  7,  3,  1,  7,  8,  3,  1,  8,  0,  3,  8, 12,  0,  3,  6, 19,  7,  3,  1, 14, 11,  5,  1,  0, 12, 15, 14,  5,  8,  7, 19, 15, 12,  5,  6, 11, 14, 15, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 141     9 */
{{  3,  0,  7,  3,  3,  0, 11,  7,  3,  0,  9, 11,  3,  6,  7, 11,  3,  0, 13,  9,  3,  6, 19,  7,  4,  9, 13, 14, 11,  4,  7, 19, 15,  3,  5,  0,  3, 15, 14, 13,  5,  6, 11, 14, 15, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 142    14 */
{{  3,  7, 11,  6,  3,  7,  8, 11,  3,  8,  9, 11,  3,  6, 19,  7,  4, 12, 15, 14, 13,  4,  8, 12, 13,  9,  4,  9, 13, 14, 11,  5,  8,  7, 19, 15, 12,  5,  6, 11, 14, 15, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 143     5 */
{{  3,  6,  4,  8,  3, 10,  6,  8,  3,  6, 10, 19,  3,  8,  4, 16,  4,  6, 19, 16,  4,  4,  8, 16, 19, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 144     2 */
{{  3,  3, 10,  6,  3,  3,  6,  0,  3,  0,  6,  4,  3,  3,  0, 12,  3,  6, 10, 19,  4,  4, 16, 12,  0,  4,  6, 19, 16,  4,  5,  3, 12, 16, 19, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 145     5 */
{{  3,  8, 10,  6,  3,  8,  6,  4,  3, 10, 19,  6,  3,  4, 16,  8,  4,  6, 19, 16,  4,  4,  8, 16, 19, 10, -1,  3,  9,  1,  0,  3,  9, 13,  1,  3,  1, 13,  0,  3,  0, 13,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 146     6 */
{{  3,  9,  6,  4,  3,  9,  3,  6,  3,  9,  1,  3,  3, 10,  6,  3,  3,  9, 13,  1,  3, 10, 19,  6,  4,  1, 13, 12,  3,  4,  6, 19, 16,  4,  5,  9,  4, 16, 12, 13,  5, 10,  3, 12, 16, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 147    14 */
{{  3,  6,  4,  8,  3,  6,  8, 10,  3,  4, 16,  8,  3, 10, 19,  6,  4,  8, 16, 19, 10,  4,  6, 19, 16,  4, -1,  3,  2,  1, 11,  3,  2, 14,  1,  3,  1, 14, 11,  3, 11, 14,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 148     6 */
{{  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -1,  3,  3, 10,  0,  3,  0, 10,  6,  3,  0,  6,  4,  3, 10, 19,  6,  3,  0, 12,  3,  4,  6, 19, 16,  4,  4,  4, 16, 12,  0,  5, 10,  3, 12, 16, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 149    12 */
{{  3,  4,  8, 10,  3,  4, 10,  6,  3,  4, 16,  8,  3, 10, 19,  6,  4,  8, 16, 19, 10,  4,  6, 19, 16,  4, -1,  3,  0,  9,  2,  3,  2,  9, 11,  3,  0, 13,  9,  3, 11, 14,  2,  4,  9, 13, 14, 11,  4,  2, 14, 13,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 150    10 */
{{  3, 11,  3,  9,  3, 11,  2,  3,  3,  9,  3,  4,  3, 10,  6,  3,  3,  4,  3,  6,  3, 11, 14,  2,  3, 10, 19,  6,  4,  6, 19, 16,  4,  4,  9, 13, 14, 11,  5,  9,  4, 16, 12, 13,  5,  3,  2, 14, 13, 12,  5, 10,  3, 12, 16, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 151     6 */
{{  3,  8,  3,  2,  3,  8,  2,  4,  3,  4,  2,  6,  3,  8,  4, 16,  3,  2,  3, 15,  4,  6, 19, 16,  4,  4,  2, 15, 19,  6,  5,  8, 16, 19, 15,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 152     5 */
{{  3,  0,  2,  4,  3,  4,  2,  6,  4, 12, 16, 19, 15,  4,  0, 12, 15,  2,  4,  2, 15, 19,  6,  4,  6, 19, 16,  4,  4,  4, 16, 12,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 153     8 */
{{  3,  1,  0,  9,  3,  1, 13,  0,  3,  0, 13,  9,  3,  9, 13,  1, -1,  3,  2,  4,  3,  3,  2,  6,  4,  3,  4,  8,  3,  3,  3, 15,  2,  3,  4, 16,  8,  4,  2, 15, 19,  6,  4,  6, 19, 16,  4,  5,  3,  8, 16, 19, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 154    12 */
{{  3,  1,  4,  9,  3,  1,  2,  4,  3,  2,  6,  4,  3,  9, 13,  1,  4, 15, 12, 16, 19,  4,  2, 15, 19,  6,  4,  6, 19, 16,  4,  5,  2,  1, 13, 12, 15,  5,  9,  4, 16, 12, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 155     5 */
{{  3,  8,  3,  1,  3,  8,  1,  6,  3,  8,  6,  4,  3,  6,  1, 11,  3,  4, 16,  8,  3,  1, 14, 11,  4,  3, 15, 14,  1,  4,  6, 19, 16,  4,  5,  3,  8, 16, 19, 15,  5,  6, 11, 14, 15, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 156    11 */
{{  3, 11,  0,  1,  3, 11,  6,  0,  3,  6,  4,  0,  3,  1, 14, 11,  4, 19, 15, 12, 16,  4,  6, 19, 16,  4,  4,  4, 16, 12,  0,  5,  6, 11, 14, 15, 19,  5,  1,  0, 12, 15, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 157     5 */
{{  3,  4,  3,  6,  3,  4,  8,  3,  3,  6,  3, 11,  3,  0,  9,  3,  3, 11,  3,  9,  3,  4, 16,  8,  3,  0, 13,  9,  4,  9, 13, 14, 11,  4,  6, 19, 16,  4,  5,  6, 11, 14, 15, 19,  5,  3,  8, 16, 19, 15,  5,  0,  3, 15, 14, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 158     6 */
{{  3, 11,  4,  9,  3,  6,  4, 11,  4, 14, 13, 12, 15,  4, 15, 12, 16, 19,  4, 11,  9, 13, 14,  4,  4,  6, 19, 16,  5, 11, 14, 15, 19,  6,  5,  4, 16, 12, 13,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 159     2 */
{{  3,  4,  5,  9,  3,  4, 17,  5,  3,  5, 17,  9,  3,  9, 17,  4, -1,  3,  7, 10,  6,  3,  7, 19, 10,  3, 10, 19,  6,  3,  6, 19,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 160     3 */
{{  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -1,  3,  4,  5,  9,  3,  4, 17,  5,  3,  5, 17,  9,  3,  9, 17,  4, -1,  3, 10,  6,  7,  3, 10, 19,  6,  3,  6, 19,  7,  3,  7, 19, 10, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 161     7 */
{{  3,  5,  1,  0,  3,  5,  0,  4,  3,  1, 13,  0,  3,  4, 17,  5,  4,  0, 13, 17,  4,  4,  5, 17, 13,  1, -1,  3,  7, 10,  6,  3,  7, 19, 10,  3, 10, 19,  6,  3,  6, 19,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 162     6 */
{{  3, 10,  6,  7,  3, 10, 19,  6,  3,  6, 19,  7,  3,  7, 19, 10, -1,  3,  8,  4,  3,  3,  3,  4,  5,  3,  3,  5,  1,  3,  4, 17,  5,  3,  3, 12,  8,  4,  5, 17, 13,  1,  4,  1, 13, 12,  3,  5,  4,  8, 12, 13, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 163    12 */
{{  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -1,  3, 11,  2,  1,  3, 11, 14,  2,  3,  2, 14,  1,  3,  1, 14, 11, -1,  3,  7, 10,  6,  3,  7, 19, 10,  3, 10, 19,  6,  3,  6, 19,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 164     7 */
{{  3,  6,  7, 10,  3,  6, 19,  7,  3,  7, 19, 10,  3, 10, 19,  6, -1,  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -1,  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -1,  3,  4,  5,  9,  3,  4, 17,  5,  3,  5, 17,  9,  3,  9, 17,  4, -2 }},    /* 165    13 */
{{  3,  7, 10,  6,  3,  7, 19, 10,  3, 10, 19,  6,  3,  6, 19,  7, -1,  3,  5, 11,  4,  3,  4, 11,  2,  3,  4,  2,  0,  3, 11, 14,  2,  3,  4, 17,  5,  4,  2, 14, 13,  0,  4,  0, 13, 17,  4,  5, 11,  5, 17, 13, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 166    12 */
{{  3,  3,  8,  4,  3,  3,  4,  5,  3,  3,  5,  2,  3, 11,  2,  5,  3,  3, 12,  8,  3,  4, 17,  5,  3, 11, 14,  2,  5,  3,  2, 14, 13, 12,  5,  4,  8, 12, 13, 17,  5, 11,  5, 17, 13, 14, -1,  3, 10,  6,  7,  3, 10, 19,  6,  3,  6, 19,  7,  3,  7, 19, 10, -2, -2, -2, -2, -2 }},    /* 167     7 */
{{  3,  7,  3,  2,  3,  7,  2,  6,  3,  3, 15,  2,  3,  6, 19,  7,  4,  2, 15, 19,  6,  4,  7, 19, 15,  3, -1,  3,  5,  9,  4,  3,  5, 17,  9,  3,  9, 17,  4,  3,  4, 17,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 168     6 */
{{  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -1,  3,  0,  6,  8,  3,  0,  2,  6,  3,  6,  7,  8,  3,  8, 12,  0,  3,  6, 19,  7,  4,  0, 12, 15,  2,  4,  2, 15, 19,  6,  5,  8,  7, 19, 15, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 169    12 */
{{  3,  3,  2,  6,  3,  3,  6,  7,  3,  3, 15,  2,  3,  6, 19,  7,  4,  2, 15, 19,  6,  4,  7, 19, 15,  3, -1,  3,  1,  0,  5,  3,  5,  0,  4,  3,  1, 13,  0,  3,  4, 17,  5,  4,  0, 13, 17,  4,  4,  5, 17, 13,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 170    10 */
{{  3,  6,  8,  2,  3,  6,  7,  8,  3,  2,  8,  1,  3,  4,  5,  8,  3,  1,  8,  5,  3,  6, 19,  7,  3,  4, 17,  5,  4,  5, 17, 13,  1,  4,  2, 15, 19,  6,  5,  2,  1, 13, 12, 15,  5,  8,  7, 19, 15, 12,  5,  4,  8, 12, 13, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 171     6 */
{{  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -1,  3, 11,  6,  1,  3,  1,  6,  7,  3,  1,  7,  3,  3,  6, 19,  7,  3,  1, 14, 11,  4,  7, 19, 15,  3,  4,  3, 15, 14,  1,  5,  6, 11, 14, 15, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 172    12 */
{{  3,  1, 11,  6,  3,  1,  6,  7,  3,  1,  7,  0,  3,  8,  0,  7,  3,  1, 14, 11,  3,  6, 19,  7,  3,  8, 12,  0,  5,  1,  0, 12, 15, 14,  5,  6, 11, 14, 15, 19,  5,  8,  7, 19, 15, 12, -1,  3,  9,  4,  5,  3,  9, 17,  4,  3,  4, 17,  5,  3,  5, 17,  9, -2, -2, -2, -2, -2 }},    /* 173     7 */
{{  3,  4, 11,  0,  3,  4,  5, 11,  3,  0, 11,  3,  3,  6,  7, 11,  3,  3, 11,  7,  3,  4, 17,  5,  3,  6, 19,  7,  4,  7, 19, 15,  3,  4,  0, 13, 17,  4,  5,  0,  3, 15, 14, 13,  5, 11,  5, 17, 13, 14,  5,  6, 11, 14, 15, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 174     6 */
{{  3,  7, 11,  6,  3,  7,  8, 11,  3,  5, 11,  4,  3,  4, 11,  8,  3,  6, 19,  7,  3,  4, 17,  5,  4, 14, 13, 12, 15,  5, 11, 14, 15, 19,  6,  5,  7, 19, 15, 12,  8,  5,  8, 12, 13, 17,  4,  5,  5, 17, 13, 14, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 175     3 */
{{  3,  6,  5,  9,  3,  6,  9, 10,  3, 10,  9,  8,  3,  6, 10, 19,  3,  9,  5, 17,  4,  8, 16, 19, 10,  4,  9, 17, 16,  8,  5,  6, 19, 16, 17,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 176     5 */
{{  3,  3, 10,  6,  3,  0,  3,  6,  3,  0,  6,  5,  3,  0,  5,  9,  3,  5, 17,  9,  3, 10, 19,  6,  3,  0, 12,  3,  5,  0,  9, 17, 16, 12,  5,  5,  6, 19, 16, 17,  5, 10,  3, 12, 16, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 177     9 */
{{  3,  0,  8, 10,  3,  0, 10,  5,  3,  0,  5,  1,  3,  5, 10,  6,  3,  1, 13,  0,  3, 10, 19,  6,  4,  8, 16, 19, 10,  4,  5, 17, 13,  1,  5,  8,  0, 13, 17, 16,  5,  5,  6, 19, 16, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 178    11 */
{{  3,  6,  3, 10,  3,  6,  5,  3,  3,  5,  1,  3,  3, 10, 19,  6,  4, 17, 16, 12, 13,  4,  5, 17, 13,  1,  4,  1, 13, 12,  3,  5,  5,  6, 19, 16, 17,  5, 10,  3, 12, 16, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 179     5 */
{{  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -1,  3,  9, 10,  5,  3,  9,  8, 10,  3, 10,  6,  5,  3,  5, 17,  9,  3, 10, 19,  6,  4,  9, 17, 16,  8,  4,  8, 16, 19, 10,  5,  5,  6, 19, 16, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 180    12 */
{{  3,  0,  3, 10,  3,  0, 10,  6,  3,  0,  6,  9,  3,  5,  9,  6,  3,  0, 12,  3,  3, 10, 19,  6,  3,  5, 17,  9,  5,  0,  9, 17, 16, 12,  5, 10,  3, 12, 16, 19,  5,  5,  6, 19, 16, 17, -1,  3,  1, 11,  2,  3,  1, 14, 11,  3, 11, 14,  2,  3,  2, 14,  1, -2, -2, -2, -2, -2 }},    /* 181     7 */
{{  3, 10,  5,  8,  3, 10,  6,  5,  3,  8,  5,  0,  3, 11,  2,  5,  3,  0,  5,  2,  3, 10, 19,  6,  3, 11, 14,  2,  4,  2, 14, 13,  0,  4,  8, 16, 19, 10,  5,  8,  0, 13, 17, 16,  5,  5,  6, 19, 16, 17,  5, 11,  5, 17, 13, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 182     6 */
{{  3,  6,  3, 10,  3,  6,  5,  3,  3,  2,  3, 11,  3, 11,  3,  5,  3, 10, 19,  6,  3, 11, 14,  2,  4, 12, 13, 17, 16,  5,  3, 12, 16, 19, 10,  5,  6, 19, 16, 17,  5,  5,  5, 17, 13, 14, 11,  5,  2, 14, 13, 12,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 183     3 */
{{  3,  5,  9,  8,  3,  5,  8,  2,  3,  5,  2,  6,  3,  3,  2,  8,  3,  5, 17,  9,  3,  3, 15,  2,  4,  9, 17, 16,  8,  4,  2, 15, 19,  6,  5,  5,  6, 19, 16, 17,  5,  3,  8, 16, 19, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 184    14 */
{{  3,  9,  6,  5,  3,  9,  0,  6,  3,  0,  2,  6,  3,  5, 17,  9,  4, 12, 16, 19, 15,  4,  0, 12, 15,  2,  4,  2, 15, 19,  6,  5,  0,  9, 17, 16, 12,  5,  5,  6, 19, 16, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 185     5 */
{{  3,  1,  8,  5,  3,  1,  0,  8,  3,  5,  8,  6,  3,  3,  2,  8,  3,  6,  8,  2,  3,  1, 13,  0,  3,  3, 15,  2,  4,  2, 15, 19,  6,  4,  5, 17, 13,  1,  5,  5,  6, 19, 16, 17,  5,  8,  0, 13, 17, 16,  5,  3,  8, 16, 19, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 186     6 */
{{  3,  1,  6,  5,  3,  2,  6,  1,  4, 13, 17, 16, 12,  4, 12, 16, 19, 15,  4,  1,  5, 17, 13,  4,  6,  2, 15, 19,  5,  1, 13, 12, 15,  2,  5,  6, 19, 16, 17,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 187     2 */
{{  3,  1,  6,  3,  3,  1, 11,  6,  3,  3,  6,  8,  3,  5,  9,  6,  3,  8,  6,  9,  3,  1, 14, 11,  3,  5, 17,  9,  4,  9, 17, 16,  8,  4,  3, 15, 14,  1,  5,  3,  8, 16, 19, 15,  5,  6, 11, 14, 15, 19,  5,  5,  6, 19, 16, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 188     6 */
{{  3, 11,  0,  1,  3, 11,  6,  0,  3,  9,  0,  5,  3,  5,  0,  6,  3,  1, 14, 11,  3,  5, 17,  9,  4, 12, 16, 19, 15,  5,  0, 12, 15, 14,  1,  5, 11, 14, 15, 19,  6,  5,  6, 19, 16, 17,  5,  5,  9, 17, 16, 12,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 189     3 */
{{  3,  0,  8,  3,  3,  5, 11,  6,  5,  0, 13, 17, 16,  8,  5,  6, 19, 16, 17,  5,  5, 11, 14, 15, 19,  6,  5,  3, 15, 14, 13,  0,  5,  5, 17, 13, 14, 11,  5,  8, 16, 19, 15,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 190     4 */
{{  3, 11,  6,  5,  4, 13, 17, 16, 12,  4, 19, 15, 12, 16,  4, 12, 15, 14, 13,  5, 14, 15, 19,  6, 11,  5, 19, 16, 17,  5,  6,  5, 17, 13, 14, 11,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 191     1 */
{{  3, 10, 11,  5,  3,  7, 10,  5,  3, 10,  7, 19,  3,  5, 11, 18,  4, 10, 19, 18, 11,  4,  5, 18, 19,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 192     2 */
{{  3, 10, 11,  5,  3, 10,  5,  7,  3, 11, 18,  5,  3,  7, 19, 10,  4,  5, 18, 19,  7,  4, 10, 19, 18, 11, -1,  3,  8,  0,  3,  3,  8, 12,  0,  3,  0, 12,  3,  3,  3, 12,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 193     6 */
{{  3,  5,  7, 10,  3,  5, 10, 11,  3,  7, 19, 10,  3, 11, 18,  5,  4, 10, 19, 18, 11,  4,  5, 18, 19,  7, -1,  3,  1,  0,  9,  3,  1, 13,  0,  3,  0, 13,  9,  3,  9, 13,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 194     6 */
{{  3, 11,  5,  7,  3, 11,  7, 10,  3, 11, 18,  5,  3,  7, 19, 10,  4,  5, 18, 19,  7,  4, 10, 19, 18, 11, -1,  3,  9,  1,  8,  3,  8,  1,  3,  3,  9, 13,  1,  3,  3, 12,  8,  4,  1, 13, 12,  3,  4,  8, 12, 13,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 195    10 */
{{  3, 10,  2,  1,  3, 10,  1,  7,  3,  7,  1,  5,  3, 10,  7, 19,  3,  1,  2, 14,  4,  5, 18, 19,  7,  4,  1, 14, 18,  5,  5, 10, 19, 18, 14,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 196     5 */
{{  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -1,  3,  1,  7,  2,  3,  1,  5,  7,  3,  7, 10,  2,  3,  2, 14,  1,  3,  7, 19, 10,  4,  1, 14, 18,  5,  4,  5, 18, 19,  7,  5,  2, 10, 19, 18, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 197    12 */
{{  3,  9,  5,  7,  3,  9,  7,  2,  3,  9,  2,  0,  3,  2,  7, 10,  3,  0, 13,  9,  3,  7, 19, 10,  4,  5, 18, 19,  7,  4,  2, 14, 13,  0,  5,  5,  9, 13, 14, 18,  5,  2, 10, 19, 18, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 198    11 */
{{  3,  7,  2,  5,  3,  7, 10,  2,  3,  5,  2,  9,  3,  3,  8,  2,  3,  9,  2,  8,  3,  7, 19, 10,  3,  3, 12,  8,  4,  8, 12, 13,  9,  4,  5, 18, 19,  7,  5,  5,  9, 13, 14, 18,  5,  2, 10, 19, 18, 14,  5,  3,  2, 14, 13, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 199     6 */
{{  3,  2, 11,  5,  3,  2,  5,  3,  3,  3,  5,  7,  3,  2,  3, 15,  3,  5, 11, 18,  4,  7, 19, 15,  3,  4,  5, 18, 19,  7,  5,  2, 15, 19, 18, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 200     5 */
{{  3,  8,  0,  2,  3,  8,  2,  5,  3,  8,  5,  7,  3, 11,  5,  2,  3,  8, 12,  0,  3, 11, 18,  5,  4,  0, 12, 15,  2,  4,  5, 18, 19,  7,  5,  8,  7, 19, 15, 12,  5, 11,  2, 15, 19, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 201    14 */
{{  3,  9,  1,  0,  3,  9, 13,  1,  3,  1, 13,  0,  3,  0, 13,  9, -1,  3,  5,  3, 11,  3,  5,  7,  3,  3,  3,  2, 11,  3, 11, 18,  5,  3,  3, 15,  2,  4,  5, 18, 19,  7,  4,  7, 19, 15,  3,  5, 11,  2, 15, 19, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 202    12 */
{{  3,  9,  2,  8,  3,  9,  1,  2,  3,  8,  2,  7,  3, 11,  5,  2,  3,  7,  2,  5,  3,  9, 13,  1,  3, 11, 18,  5,  4,  5, 18, 19,  7,  4,  8, 12, 13,  9,  5,  8,  7, 19, 15, 12,  5,  2,  1, 13, 12, 15,  5, 11,  2, 15, 19, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 203     6 */
{{  3,  1,  5,  3,  3,  3,  5,  7,  4, 14, 15, 19, 18,  4,  1, 14, 18,  5,  4,  5, 18, 19,  7,  4,  7, 19, 15,  3,  4,  3, 15, 14,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 204     8 */
{{  3,  0,  7,  8,  3,  0,  1,  7,  3,  1,  5,  7,  3,  8, 12,  0,  4, 14, 15, 19, 18,  4,  1, 14, 18,  5,  4,  5, 18, 19,  7,  5,  1,  0, 12, 15, 14,  5,  8,  7, 19, 15, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 205     5 */
{{  3,  9,  3,  0,  3,  9,  5,  3,  3,  5,  7,  3,  3,  0, 13,  9,  4, 18, 14, 15, 19,  4,  5, 18, 19,  7,  4,  7, 19, 15,  3,  5,  5,  9, 13, 14, 18,  5,  0,  3, 15, 14, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 206     5 */
{{  3,  9,  7,  8,  3,  5,  7,  9,  4, 13, 12, 15, 14,  4, 14, 15, 19, 18,  4,  9,  8, 12, 13,  4,  7,  5, 18, 19,  5,  9, 13, 14, 18,  5,  5,  7, 19, 15, 12,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 207     2 */
{{  3,  5,  4,  8,  3,  5,  8, 11,  3, 11,  8, 10,  3,  5, 11, 18,  3,  8,  4, 16,  4, 10, 19, 18, 11,  4,  8, 16, 19, 10,  5,  5, 18, 19, 16,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 208     5 */
{{  3,  5,  4,  0,  3,  5,  0, 10,  3,  5, 10, 11,  3, 10,  0,  3,  3, 11, 18,  5,  3,  0, 12,  3,  4,  4, 16, 12,  0,  4, 10, 19, 18, 11,  5,  4,  5, 18, 19, 16,  5, 10,  3, 12, 16, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 209    11 */
{{  3,  0,  9,  1,  3,  0, 13,  9,  3,  9, 13,  1,  3,  1, 13,  0, -1,  3,  8, 11,  4,  3,  8, 10, 11,  3, 11,  5,  4,  3,  4, 16,  8,  3, 11, 18,  5,  4,  8, 16, 19, 10,  4, 10, 19, 18, 11,  5,  4,  5, 18, 19, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 210    12 */
{{  3, 11,  4, 10,  3, 11,  5,  4,  3, 10,  4,  3,  3,  9,  1,  4,  3,  3,  4,  1,  3, 11, 18,  5,  3,  9, 13,  1,  4,  1, 13, 12,  3,  4, 10, 19, 18, 11,  5, 10,  3, 12, 16, 19,  5,  4,  5, 18, 19, 16,  5,  9,  4, 16, 12, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 211     6 */
{{  3,  2,  1,  5,  3,  2,  5,  8,  3,  2,  8, 10,  3,  4,  8,  5,  3,  2, 14,  1,  3,  4, 16,  8,  4,  1, 14, 18,  5,  4,  8, 16, 19, 10,  5,  2, 10, 19, 18, 14,  5,  4,  5, 18, 19, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 212    14 */
{{  3,  0, 10,  4,  3,  0,  3, 10,  3,  4, 10,  5,  3,  2,  1, 10,  3,  5, 10,  1,  3,  0, 12,  3,  3,  2, 14,  1,  4,  1, 14, 18,  5,  4,  4, 16, 12,  0,  5,  4,  5, 18, 19, 16,  5, 10,  3, 12, 16, 19,  5,  2, 10, 19, 18, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 213     6 */
{{  3,  0,  5,  2,  3,  0,  9,  5,  3,  2,  5, 10,  3,  4,  8,  5,  3, 10,  5,  8,  3,  0, 13,  9,  3,  4, 16,  8,  4,  8, 16, 19, 10,  4,  2, 14, 13,  0,  5,  2, 10, 19, 18, 14,  5,  5,  9, 13, 14, 18,  5,  4,  5, 18, 19, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 214     6 */
{{  3,  9,  5,  4,  3,  2,  3, 10,  5,  9, 13, 14, 18,  5,  5, 10, 19, 18, 14,  2,  5,  3, 12, 16, 19, 10,  5,  4, 16, 12, 13,  9,  5,  2, 14, 13, 12,  3,  5,  5, 18, 19, 16,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 215     4 */
{{  3,  2, 11,  5,  3,  3,  2,  5,  3,  3,  5,  4,  3,  3,  4,  8,  3,  4, 16,  8,  3, 11, 18,  5,  3,  3, 15,  2,  5,  3,  8, 16, 19, 15,  5,  4,  5, 18, 19, 16,  5, 11,  2, 15, 19, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 216     9 */
{{  3,  5,  2, 11,  3,  5,  4,  2,  3,  4,  0,  2,  3, 11, 18,  5,  4, 16, 19, 15, 12,  4,  4, 16, 12,  0,  4,  0, 12, 15,  2,  5,  4,  5, 18, 19, 16,  5, 11,  2, 15, 19, 18, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 217     5 */
{{  3,  3,  2, 11,  3,  3, 11,  5,  3,  3,  5,  8,  3,  4,  8,  5,  3,  3, 15,  2,  3, 11, 18,  5,  3,  4, 16,  8,  5,  3,  8, 16, 19, 15,  5, 11,  2, 15, 19, 18,  5,  4,  5, 18, 19, 16, -1,  3,  0,  9,  1,  3,  0, 13,  9,  3,  9, 13,  1,  3,  1, 13,  0, -2, -2, -2, -2, -2 }},    /* 218     7 */
{{  3,  5,  2, 11,  3,  5,  4,  2,  3,  1,  2,  9,  3,  9,  2,  4,  3, 11, 18,  5,  3,  9, 13,  1,  4, 15, 12, 16, 19,  5,  2, 15, 19, 18, 11,  5,  5, 18, 19, 16,  4,  5,  4, 16, 12, 13,  9,  5,  1, 13, 12, 15,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 219     3 */
{{  3,  8,  5,  4,  3,  8,  3,  5,  3,  3,  1,  5,  3,  4, 16,  8,  4, 15, 19, 18, 14,  4,  3, 15, 14,  1,  4,  1, 14, 18,  5,  5,  3,  8, 16, 19, 15,  5,  4,  5, 18, 19, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 220     5 */
{{  3,  0,  5,  4,  3,  1,  5,  0,  4, 12, 16, 19, 15,  4, 15, 19, 18, 14,  4,  0,  4, 16, 12,  4,  5,  1, 14, 18,  5,  0, 12, 15, 14,  1,  5,  5, 18, 19, 16,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 221     2 */
{{  3,  8,  5,  4,  3,  8,  3,  5,  3,  9,  5,  0,  3,  0,  5,  3,  3,  4, 16,  8,  3,  0, 13,  9,  4, 18, 14, 15, 19,  5,  5, 18, 19, 16,  4,  5,  8, 16, 19, 15,  3,  5,  3, 15, 14, 13,  0,  5,  9, 13, 14, 18,  5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 222     3 */
{{  3,  9,  5,  4,  4, 12, 16, 19, 15,  4, 18, 14, 15, 19,  4, 15, 14, 13, 12,  5, 13, 14, 18,  5,  9,  5, 18, 19, 16,  4,  5,  5, 16, 12, 13,  9,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 223     1 */
{{  3,  4,  7, 10,  3,  4, 10,  9,  3,  9, 10, 11,  3,  4,  9, 17,  3, 10,  7, 19,  4, 11, 18, 17,  9,  4, 10, 19, 18, 11,  5,  4, 17, 18, 19,  7, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 224     5 */
{{  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -1,  3,  4,  7,  9,  3,  9,  7, 10,  3,  9, 10, 11,  3,  7, 19, 10,  3,  9, 17,  4,  4, 10, 19, 18, 11,  4, 11, 18, 17,  9,  5,  7,  4, 17, 18, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 225    12 */
{{  3,  1, 10, 11,  3,  1,  4, 10,  3,  1,  0,  4,  3,  7, 10,  4,  3,  1, 13,  0,  3,  7, 19, 10,  4,  0, 13, 17,  4,  4, 10, 19, 18, 11,  5,  1, 11, 18, 17, 13,  5,  7,  4, 17, 18, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 226    14 */
{{  3,  3,  4,  1,  3,  3,  8,  4,  3,  1,  4, 11,  3,  7, 10,  4,  3, 11,  4, 10,  3,  3, 12,  8,  3,  7, 19, 10,  4, 10, 19, 18, 11,  4,  1, 13, 12,  3,  5,  1, 11, 18, 17, 13,  5,  4,  8, 12, 13, 17,  5,  7,  4, 17, 18, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 227     6 */
{{  3,  4,  7, 10,  3,  9,  4, 10,  3,  9, 10,  2,  3,  9,  2,  1,  3,  2, 14,  1,  3,  7, 19, 10,  3,  9, 17,  4,  5,  9,  1, 14, 18, 17,  5,  2, 10, 19, 18, 14,  5,  7,  4, 17, 18, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 228     9 */
{{  3,  9,  4,  7,  3,  9,  7, 10,  3,  9, 10,  1,  3,  2,  1, 10,  3,  9, 17,  4,  3,  7, 19, 10,  3,  2, 14,  1,  5,  9,  1, 14, 18, 17,  5,  7,  4, 17, 18, 19,  5,  2, 10, 19, 18, 14, -1,  3,  0,  3,  8,  3,  0, 12,  3,  3,  3, 12,  8,  3,  8, 12,  0, -2, -2, -2, -2, -2 }},    /* 229     7 */
{{  3, 10,  4,  7,  3, 10,  2,  4,  3,  2,  0,  4,  3,  7, 19, 10,  4, 14, 18, 17, 13,  4,  2, 14, 13,  0,  4,  0, 13, 17,  4,  5,  2, 10, 19, 18, 14,  5,  7,  4, 17, 18, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 230     5 */
{{  3, 10,  4,  7,  3, 10,  2,  4,  3,  8,  4,  3,  3,  3,  4,  2,  3,  7, 19, 10,  3,  3, 12,  8,  4, 17, 13, 14, 18,  5,  4, 17, 18, 19,  7,  5, 10, 19, 18, 14,  2,  5,  2, 14, 13, 12,  3,  5,  8, 12, 13, 17,  4, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 231     3 */
{{  3,  2, 11,  9,  3,  2,  9,  7,  3,  2,  7,  3,  3,  7,  9,  4,  3,  3, 15,  2,  3,  9, 17,  4,  4, 11, 18, 17,  9,  4,  7, 19, 15,  3,  5, 11,  2, 15, 19, 18,  5,  7,  4, 17, 18, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 232    11 */
{{  3,  9,  7, 11,  3,  9,  4,  7,  3, 11,  7,  2,  3,  8,  0,  7,  3,  2,  7,  0,  3,  9, 17,  4,  3,  8, 12,  0,  4,  0, 12, 15,  2,  4, 11, 18, 17,  9,  5, 11,  2, 15, 19, 18,  5,  7,  4, 17, 18, 19,  5,  8,  7, 19, 15, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 233     6 */
{{  3,  3, 11,  7,  3,  3,  2, 11,  3,  7, 11,  4,  3,  1,  0, 11,  3,  4, 11,  0,  3,  3, 15,  2,  3,  1, 13,  0,  4,  0, 13, 17,  4,  4,  7, 19, 15,  3,  5,  7,  4, 17, 18, 19,  5, 11,  2, 15, 19, 18,  5,  1, 11, 18, 17, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 234     6 */
{{  3,  1,  2, 11,  3,  8,  4,  7,  5,  1, 13, 12, 15,  2,  5,  7, 19, 15, 12,  8,  5,  4, 17, 18, 19,  7,  5, 11, 18, 17, 13,  1,  5,  8, 12, 13, 17,  4,  5,  2, 15, 19, 18, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 235     4 */
{{  3,  4,  1,  9,  3,  4,  7,  1,  3,  7,  3,  1,  3,  9, 17,  4,  4, 19, 18, 14, 15,  4,  7, 19, 15,  3,  4,  3, 15, 14,  1,  5,  7,  4, 17, 18, 19,  5,  9,  1, 14, 18, 17, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 236     5 */
{{  3,  4,  1,  9,  3,  4,  7,  1,  3,  0,  1,  8,  3,  8,  1,  7,  3,  9, 17,  4,  3,  8, 12,  0,  4, 14, 15, 19, 18,  5,  1, 14, 18, 17,  9,  5,  4, 17, 18, 19,  7,  5,  7, 19, 15, 12,  8,  5,  0, 12, 15, 14,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 237     3 */
{{  3,  4,  3,  0,  3,  7,  3,  4,  4, 17, 13, 14, 18,  4, 18, 14, 15, 19,  4,  4,  0, 13, 17,  4,  3,  7, 19, 15,  5,  4, 17, 18, 19,  7,  5,  3, 15, 14, 13,  0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 238     2 */
{{  3,  4,  7,  8,  4, 13, 12, 15, 14,  4, 19, 18, 14, 15,  4, 14, 18, 17, 13,  5, 17, 18, 19,  7,  4,  5, 19, 15, 12,  8,  7,  5, 12, 13, 17,  4,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 239     1 */
{{  3,  9,  8, 11,  3, 11,  8, 10,  4, 17, 18, 19, 16,  4,  9, 17, 16,  8,  4,  8, 16, 19, 10,  4, 10, 19, 18, 11,  4, 11, 18, 17,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 240     8 */
{{  3,  3,  9,  0,  3,  3, 10,  9,  3, 10, 11,  9,  3,  0, 12,  3,  4, 19, 16, 17, 18,  4, 10, 19, 18, 11,  4, 11, 18, 17,  9,  5, 10,  3, 12, 16, 19,  5,  0,  9, 17, 16, 12, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 241     5 */
{{  3,  0, 11,  1,  3,  0,  8, 11,  3,  8, 10, 11,  3,  1, 13,  0,  4, 16, 17, 18, 19,  4,  8, 16, 19, 10,  4, 10, 19, 18, 11,  5,  8,  0, 13, 17, 16,  5,  1, 11, 18, 17, 13, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 242     5 */
{{  3,  3, 11,  1,  3, 10, 11,  3,  4, 12, 13, 17, 16,  4, 16, 17, 18, 19,  4,  3,  1, 13, 12,  4, 11, 10, 19, 18,  5,  3, 12, 16, 19, 10,  5, 11, 18, 17, 13,  1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 243     2 */
{{  3,  1, 10,  2,  3,  1,  9, 10,  3,  9,  8, 10,  3,  2, 14,  1,  4, 17, 18, 19, 16,  4,  9, 17, 16,  8,  4,  8, 16, 19, 10,  5,  9,  1, 14, 18, 17,  5,  2, 10, 19, 18, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 244     5 */
{{  3,  3,  9,  0,  3,  3, 10,  9,  3,  1,  9,  2,  3,  2,  9, 10,  3,  0, 12,  3,  3,  2, 14,  1,  4, 17, 18, 19, 16,  5,  9, 17, 16, 12,  0,  5,  3, 12, 16, 19, 10,  5, 10, 19, 18, 14,  2,  5,  1, 14, 18, 17,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 245     3 */
{{  3,  0, 10,  2,  3,  8, 10,  0,  4, 13, 14, 18, 17,  4, 17, 18, 19, 16,  4,  0,  2, 14, 13,  4, 10,  8, 16, 19,  5,  0, 13, 17, 16,  8,  5, 10, 19, 18, 14,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 246     2 */
{{  3,  3, 10,  2,  4, 13, 14, 18, 17,  4, 19, 16, 17, 18,  4, 17, 16, 12, 13,  5, 12, 16, 19, 10,  3,  5, 19, 18, 14,  2, 10,  5, 14, 13, 12,  3,  2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 247     1 */
{{  3,  2,  8,  3,  3,  2, 11,  8,  3, 11,  9,  8,  3,  3, 15,  2,  4, 18, 19, 16, 17,  4, 11, 18, 17,  9,  4,  9, 17, 16,  8,  5, 11,  2, 15, 19, 18,  5,  3,  8, 16, 19, 15, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 248     5 */
{{  3,  9,  2, 11,  3,  0,  2,  9,  4, 17, 18, 19, 16,  4, 16, 19, 15, 12,  4,  9, 11, 18, 17,  4,  2,  0, 12, 15,  5,  9, 17, 16, 12,  0,  5,  2, 15, 19, 18, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 249     2 */
{{  3,  2,  8,  3,  3,  2, 11,  8,  3,  0,  8,  1,  3,  1,  8, 11,  3,  3, 15,  2,  3,  1, 13,  0,  4, 16, 17, 18, 19,  5,  8, 16, 19, 15,  3,  5,  2, 15, 19, 18, 11,  5, 11, 18, 17, 13,  1,  5,  0, 13, 17, 16,  8, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 250     3 */
{{  3,  1,  2, 11,  4, 17, 18, 19, 16,  4, 15, 12, 16, 19,  4, 16, 12, 13, 17,  5, 13, 12, 15,  2,  1,  5, 15, 19, 18, 11,  2,  5, 18, 17, 13,  1, 11, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 251     1 */
{{  3,  1,  8,  3,  3,  9,  8,  1,  4, 14, 15, 19, 18,  4, 18, 19, 16, 17,  4,  1,  3, 15, 14,  4,  8,  9, 17, 16,  5,  1, 14, 18, 17,  9,  5,  8, 16, 19, 15,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 252     2 */
{{  3,  0,  1,  9,  4, 16, 17, 18, 19,  4, 14, 15, 19, 18,  4, 19, 15, 12, 16,  5, 12, 15, 14,  1,  0,  5, 14, 18, 17,  9,  1,  5, 17, 16, 12,  0,  9, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 253     1 */
{{  3,  0,  8,  3,  4, 14, 15, 19, 18,  4, 16, 17, 18, 19,  4, 18, 17, 13, 14,  5, 13, 17, 16,  8,  0,  5, 16, 19, 15,  3,  8,  5, 15, 14, 13,  0,  3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }},    /* 254     1 */
{{  4, 12, 16, 19, 15,  4, 13, 14, 18, 17,  4, 12, 13, 17, 16,  4, 15, 19, 18, 14,  4, 12, 15, 14, 13,  4, 16, 17, 18, 19, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }}     /* 255     0 */
};

//-----------------------------------------------------------------------------
vtkRectilinearGridConnectivityMarchingCubesVolumeCases *
vtkRectilinearGridConnectivityMarchingCubesVolumeCases::GetCases()
{
  return RECTILINEAR_GRID_CONNECTIVITY_MARCHING_CUBES_VOLUME_CASES;
}
