// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause

#include "vtkEndian.h"
#include "vtkIdList.h"
#include "vtkIncrementalOctreePointLocator.h"
#include "vtkMath.h"
#include "vtkPoints.h"
#include "vtkTestUtilities.h"
#include "vtkUnstructuredGrid.h"
#include "vtkUnstructuredGridReader.h"
#include <vtksys/SystemTools.hxx>

#include <iostream>

// TODO: Convert this test to use C++-based RAII memory management instead.
// NOLINTBEGIN(bugprone-suspicious-realloc-usage)

#define VTK_BRUTE_FORCE_VERIFICATION

// The following epsilon value is needed to address numerical inaccuracy /
// precision issues on some platforms: Fast-Release-g++, Win32-cygwin344,
// Win32-minGW, Win32-FreeVC++, and Win32-VS71. In particular, two of the
// references of this value below, as marked, handle Win32-cygwin344 and
// Win32-minGW. The numerical inaccuracy problem has nothing to do with
// the incremental octree point locator or the associated incremental octree
// node itself. Instead it is just due to the multiple sub-tests themselves
// (combined in this single file) in which the brute-force mode employs many
// if-statements / comparisons that involve double values.
//
// For example, vtkMath::Distance2BetweenPoints( point1, point2 ) may not be
// directly used in if-statements (even for some platforms other than the above
// mentioned 5), even though the incremental octree point locator always uses
// double variables for computation and returning values. Another example is
// that the min SQUARED distance D between point A (a given point) and point B
// (the closest point to A) may not be directly used to test the number of points
// within the SQUARED radius D relative to point A herein, though it is supposed
// to be ok (and the number is expected to be 1). The fact is that an epsilon
// needs to be added to D for such a test. Otherwise the numerical inaccuracy
// issue would just cause 0 to be returned --- no any point exists within the
// SQUARED radius D relative to A. Please note that this problem is not caused by
// sqrt() at all because the incremental octree point locator offers an accurate
// function variant FindPointsWithinSquaredRadius() to avoid the obvious numerical
// inaccuracy related to sqrt().
//
// Given the numerical inaccuracy issues on some platforms, the rapid verification
// mode might not be used. Fortunately, this test is fast enough.
#define INC_OCT_PNT_LOC_TESTS_ZERO 0.00000000000001

//------------------------------------------------------------------------------
// Meta information of the test data
//
// number of grid points           = 2288
// number of unique points         = 2200 (in terms of zero tolerance)
// number of duplicate occurrences = 88
// bounding box: [ -2.839926, 2.862497 ]
//               [ -2.856848, 2.856848 ]
//               [  0.000000, 1.125546 ]
//
// min squared distance = 1.036624e-005 (for zero-tolerance unique points)
// max squared distance = 3.391319e+001

//------------------------------------------------------------------------------
// Swap an array of (or a single) int, double, or vtkIdType values when
// loading little-endian / Win-based disk files on big-endian platforms.
// Note the two data files for this test are in little-endian binary format.
void SwapForBigEndian(unsigned char* theArray, int tupleSiz, int numTuple)
{
  int i, j;
  unsigned char* tmpChar0 = theArray;
  unsigned char* tmpChar1 = (unsigned char*)malloc(tupleSiz);

  for (j = 0; j < numTuple; j++, tmpChar0 += tupleSiz)
  {
    for (i = 0; i < tupleSiz; i++)
    {
      tmpChar1[i] = tmpChar0[tupleSiz - 1 - i];
    }

    for (i = 0; i < tupleSiz; i++)
    {
      tmpChar0[i] = tmpChar1[i];
    }
  }

  tmpChar0 = nullptr;
  free(tmpChar1);
  tmpChar1 = nullptr;
}

//------------------------------------------------------------------------------
int TestIncrementalOctreePointLocator(int argc, char* argv[])
{
  // specifications
  int nClzNpts = 4;
  int octreRes[3] = { 64, 128, 256 };
  double tolerans[2] = { 0.0, 0.01 };

  // variables
  int r, t, m, i, j, k;
  int retValue = 0;
  int numbPnts = 0;
  int inserted = 0;
  int numInsrt = 0;
  int nLocPnts = 0;
  int nUniques = 0;
  int nDuplics = 0;
  int arrayIdx = 0;
  int numExPts = 0;
  char* fileName = nullptr;
  FILE* diskFile = nullptr;
  FILE* pntsFile = nullptr;
  double pntCoord[3] = { 0.0, 0.0, 0.0 };
  double tmpDist2 = 0.0;
  double fTempRad = 0.0;
  double* pDataPts = nullptr;
  double* xtentPts = nullptr;
  double* pLocPnts = nullptr;
  double* minDist2 = nullptr;
  double* maxDist2 = nullptr;
  double* clzNdst2 = nullptr;
  vtkPoints* dataPnts = nullptr;
  vtkPoints* insrtPts = nullptr;
  vtkIdType pointIdx;
  vtkIdType* truthIds = nullptr;
  vtkIdType* resltIds = nullptr;
  vtkIdList* ptIdList = nullptr;
  vtkIdList* idxLists[3] = { nullptr, nullptr, nullptr };
  vtkUnstructuredGrid* unstruct = nullptr;
  vtkUnstructuredGridReader* ugReader = nullptr;
  vtkIncrementalOctreePointLocator* octLocat = nullptr;
  size_t n;

  // load an unstructured grid dataset
  fileName = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/post.vtk");
  ugReader = vtkUnstructuredGridReader::New();
  ugReader->SetFileName(fileName);
  delete[] fileName;
  ugReader->Update();
  unstruct = ugReader->GetOutput();
  dataPnts = unstruct->GetPoints();
  numbPnts = dataPnts->GetNumberOfPoints();

  // open a file for reading or writing the ground truth data
  fileName = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/IncOctPntLocResult.dat");
#ifndef VTK_BRUTE_FORCE_VERIFICATION
  diskFile = vtksys::SystemTools::Fopen(fileName, "rb");
  truthIds = (vtkIdType*)realloc(truthIds, sizeof(vtkIdType) * numbPnts);
#endif
  delete[] fileName;
  fileName = nullptr;

  // obtain the 3D point coordinates
  pDataPts = (double*)realloc(pDataPts, sizeof(double) * 3 * numbPnts);
  for (i = 0; i < numbPnts; i++)
  {
    dataPnts->GetPoint(i, pDataPts + (i << 1) + i);
  }

  // allocate memory for exactly duplicate points and inherit some points
  nUniques = 4;
  nDuplics = 300;
  arrayIdx = numbPnts * 3;
  numExPts = numbPnts + nUniques * nDuplics;
  xtentPts = (double*)realloc(xtentPts, sizeof(double) * 3 * numExPts);
  memcpy(xtentPts, pDataPts, sizeof(double) * 3 * numbPnts);

  // add an additional number of exactly duplicate points
  for (j = 0; j < nUniques; j++)
  {
    i = (numbPnts / (nUniques + 2)) * (j + 1);
    i = (i << 1) + i;
    pntCoord[0] = pDataPts[i];
    pntCoord[1] = pDataPts[i + 1];
    pntCoord[2] = pDataPts[i + 2];
    for (i = 0; i < nDuplics; i++, arrayIdx += 3)
    {
      xtentPts[arrayIdx] = pntCoord[0];
      xtentPts[arrayIdx + 1] = pntCoord[1];
      xtentPts[arrayIdx + 2] = pntCoord[2];
    }
  }

  // memory allocation
  ptIdList = vtkIdList::New();
  ptIdList->Allocate(numbPnts, numbPnts >> 1);
  insrtPts = vtkPoints::New();
  insrtPts->Allocate(numbPnts, numbPnts >> 1);
  octLocat = vtkIncrementalOctreePointLocator::New();

  // =========================================================================
  // ============================ Point Insertion ============================
  // =========================================================================
  for (r = 0; (r < 3) && (retValue == 0); r++) // three resolutions
  {

    // -----------------------------------------------------------------------
    // --------------------- check-based point insertion ---------------------
    // -----------------------------------------------------------------------
    for (t = 0; (t < 2) && (retValue == 0); t++)   // two  tolerances
      for (m = 0; (m < 3) && (retValue == 0); m++) // three functions
      {

        ptIdList->Reset(); // indices of the inserted points: based on dataPnts
        insrtPts->Reset();
        octLocat->FreeSearchStructure();
        octLocat->SetMaxPointsPerLeaf(octreRes[r]);
        octLocat->SetTolerance(tolerans[t]);
        octLocat->InitPointInsertion(insrtPts, dataPnts->GetBounds(), numbPnts);

        // ---------------------------------------------------------------------
        if (m == 0) // vtkIncrementalOctreePointLocator::InsertUniquePoint()
        {
          for (i = 0; i < numbPnts; i++)
          {
            inserted = octLocat->InsertUniquePoint(pDataPts + (i << 1) + i, pointIdx);
            if (inserted)
              ptIdList->InsertNextId(i);
          }
        }
        else if (m == 1) // vtkIncrementalOctreePointLocator::InsertNextPoint()
        {
          for (i = 0; i < numbPnts; i++)
          {
            inserted = octLocat->IsInsertedPoint(pDataPts + (i << 1) + i);
            if (inserted == -1)
            {
              octLocat->InsertNextPoint(pDataPts + (i << 1) + i);
              ptIdList->InsertNextId(i);
            }
          }
        }
        else // vtkIncrementalOctreePointLocator::InsertPoint()
        {
          numInsrt = 0;
          for (i = 0; i < numbPnts; i++)
          {
            inserted = octLocat->IsInsertedPoint(pDataPts + (i << 1) + i);
            if (inserted == -1)
            {
              octLocat->InsertPoint(numInsrt++, pDataPts + (i << 1) + i);
              ptIdList->InsertNextId(i);
            }
          }
        }
        // -------------------------------------------------------------------//

#ifdef VTK_BRUTE_FORCE_VERIFICATION
        // ---------------------------------------------------------------------
        // verify the point insertion result in brute force mode

        double tempPnt0[3];
        double tempPnt1[3];
        double tolerns2[2] = { tolerans[0] * tolerans[0], tolerans[1] * tolerans[1] };
        numInsrt = ptIdList->GetNumberOfIds();

        // check if the squared distance between any two inserted points is
        // less than the threshold
        for (j = 0; (j < numInsrt - 1) && (retValue == 0); j++)
        {
          insrtPts->GetPoint(j, tempPnt0);
          for (i = j + 1; (i < numInsrt) && (retValue == 0); i++)
          {
            insrtPts->GetPoint(i, tempPnt1);
            tmpDist2 = vtkMath::Distance2BetweenPoints(tempPnt0, tempPnt1);
            if (tmpDist2 <= tolerns2[t])
              retValue = 1;
          }
        }

        // check if there is any point whose distance to ALL inserted points
        // is greater than the threshold
        for (j = 0; (j < numbPnts) && (retValue == 0); j++)
        {
          if (ptIdList->IsId(j) != -1)
            continue; // already inserted
          int bGreater = 1;
          for (i = 0; i < numInsrt; i++)
          {
            insrtPts->GetPoint(i, tempPnt1);
            tmpDist2 = vtkMath::Distance2BetweenPoints(pDataPts + (j << 1) + j, tempPnt1);
            if (tmpDist2 <= tolerns2[t])
              bGreater = 0; // No 'break' here !!!
          }
          retValue = bGreater;
        }

// -------------------------------------------------------------------//
#else
        // ---------------------------------------------------------------------
        // rapid point index-based verification
        n = fread(&numInsrt, sizeof(int), 1, diskFile);
        if (n != 1)
        {
          std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
          return 1;
        }
#ifdef VTK_WORDS_BIGENDIAN
        SwapForBigEndian((unsigned char*)(&numInsrt), sizeof(int), 1);
#endif

        if (numInsrt == ptIdList->GetNumberOfIds())
        {
          int samePtId = 1;
          n = fread(truthIds, sizeof(vtkIdType), numInsrt, diskFile);
          if (n != static_cast<size_t>(numInsrt))
          {
            std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
            return 1;
          }
#ifdef VTK_WORDS_BIGENDIAN
          SwapForBigEndian((unsigned char*)truthIds, sizeof(vtkIdType), numInsrt);
#endif

          for (i = 0; (i < numInsrt) && (samePtId == 1); i++)
          {
            samePtId = (truthIds[i] == ptIdList->GetId(i)) ? 1 : 0;
          }
          retValue = 1 - samePtId;
        }
        else
          retValue = 1;
// -------------------------------------------------------------------//
#endif

      } // end of two tolerances and three functions

    // -----------------------------------------------------------------------
    // ------------------ direct check-free point insertion ------------------
    // -----------------------------------------------------------------------
    insrtPts->Reset();
    octLocat->FreeSearchStructure();
    octLocat->SetMaxPointsPerLeaf(octreRes[r]);
    octLocat->InitPointInsertion(insrtPts, dataPnts->GetBounds(), numbPnts);
    for (i = 0; i < numbPnts; i++)
    {
      octLocat->InsertPointWithoutChecking(pDataPts + (i << 1) + i, pointIdx, 1);
    }
    retValue = (insrtPts->GetNumberOfPoints() == numbPnts) ? 0 : 1;

  } // end of three resolutions

  // =========================================================================
  // direct check-free insertion of  a huge number of EXACTLY DUPLICATE points
  //           (number > the maximum number of points per leaf node)
  // =========================================================================
  if (retValue == 0)
  {
    // perform direct / check-free point insertion
    for (r = 0; (r < 3) && (retValue == 0); r++) // three resolutions
    {
      insrtPts->Reset();
      octLocat->FreeSearchStructure();
      octLocat->SetMaxPointsPerLeaf(octreRes[r]);
      octLocat->InitPointInsertion(insrtPts, dataPnts->GetBounds(), numExPts);
      for (i = 0; i < numExPts; i++)
      {
        octLocat->InsertPointWithoutChecking(xtentPts + (i << 1) + i, pointIdx, 1);
      }

      retValue = (insrtPts->GetNumberOfPoints() == numExPts) ? 0 : 1;
    }
  }
  if (xtentPts)
  {
    free(xtentPts);
    xtentPts = nullptr;
  }
  // =======================================================================//
  // =======================================================================//

  // reclaim this vtkPoints as it will be never used again
  if (insrtPts)
  {
    insrtPts->Delete();
    insrtPts = nullptr;
  }

  // =========================================================================
  // ============================ Point  Location ============================
  // =========================================================================

  // load points and radius data from a disk file for point location tasks
  fileName = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/IncOctPntLocData.dat");
  pntsFile = vtksys::SystemTools::Fopen(fileName, "rb");
  delete[] fileName;
  fileName = nullptr;
  n = fread(&nLocPnts, sizeof(int), 1, pntsFile);
  if (n != 1)
  {
    std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
    fclose(pntsFile);
    free(pDataPts);
    return 1;
  }
#ifdef VTK_WORDS_BIGENDIAN
  SwapForBigEndian((unsigned char*)(&nLocPnts), sizeof(int), 1);
#endif
  pLocPnts = (double*)malloc(sizeof(double) * nLocPnts * 3);
  minDist2 = (double*)malloc(sizeof(double) * nLocPnts);
  maxDist2 = (double*)malloc(sizeof(double) * nLocPnts);
  n = fread(pLocPnts, sizeof(double), nLocPnts * 3, pntsFile);
  if (n != static_cast<size_t>(nLocPnts * 3))
  {
    std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
    fclose(pntsFile);
    free(pLocPnts);
    free(minDist2);
    free(maxDist2);
    free(pDataPts);
    return 1;
  }
// fread( minDist2, sizeof( double ), nLocPnts,     pntsFile );
// fread( maxDist2, sizeof( double ), nLocPnts,     pntsFile );
#ifdef VTK_WORDS_BIGENDIAN
  SwapForBigEndian((unsigned char*)pLocPnts, sizeof(double), nLocPnts * 3);
// SwapForBigEndian
//  (  ( unsigned char * ) minDist2,  sizeof( double ),  nLocPnts      );
// SwapForBigEndian
//  (  ( unsigned char * ) maxDist2,  sizeof( double ),  nLocPnts      );
#endif
  fclose(pntsFile);
  pntsFile = nullptr;

  // destroy the context of point insertion while attaching the dataset
  octLocat->FreeSearchStructure();
  octLocat->SetDataSet(unstruct);

  // memory allocation
  clzNdst2 = (double*)realloc(clzNdst2, sizeof(double) * nClzNpts);
  for (i = 0; i < 3; i++)
  {
    idxLists[i] = vtkIdList::New();
    idxLists[i]->Allocate(nClzNpts, nClzNpts);
  }

  // the main component
  for (r = 0; (r < 3) && (retValue == 0); r++) // three resolutions
  {

    // establish a new octree with the specified resolution
    octLocat->Modified();
    octLocat->SetMaxPointsPerLeaf(octreRes[r]);
    octLocat->BuildLocator();

    // memory allocation
    resltIds = (vtkIdType*)realloc(resltIds, sizeof(vtkIdType) * nLocPnts);
#ifndef VTK_BRUTE_FORCE_VERIFICATION
    truthIds = (vtkIdType*)realloc(truthIds, sizeof(vtkIdType) * nLocPnts);
#endif

    // -----------------------------------------------------------------------
    // -------------------- location of the closest point --------------------
    // -----------------------------------------------------------------------

    // find the closest point
    for (i = 0; i < nLocPnts; i++)
    {
      resltIds[i] = octLocat->FindClosestPoint(pLocPnts + (i << 1) + i, minDist2 + i);
    }

#ifdef VTK_BRUTE_FORCE_VERIFICATION
    // -----------------------------------------------------------------------
    // verify the result in brute force mode
    for (j = 0; (j < nLocPnts) && (retValue == 0); j++)
      for (i = 0; (i < numbPnts) && (retValue == 0); i++)
      {
        if (i == resltIds[j])
          continue; // just the selected closest point
        tmpDist2 =
          vtkMath::Distance2BetweenPoints(pLocPnts + (j << 1) + j, pDataPts + (i << 1) + i);
        if (tmpDist2 + INC_OCT_PNT_LOC_TESTS_ZERO < minDist2[j])
          retValue = 1;
      }
// ---------------------------------------------------------------------//
#else
    // -----------------------------------------------------------------------
    // rapid point index-based verification
    n = fread(&nLocPnts, sizeof(int), 1, diskFile);
    if (n != 1)
    {
      std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
      return 1;
    }
#ifdef VTK_WORDS_BIGENDIAN
    SwapForBigEndian((unsigned char*)(&nLocPnts), sizeof(int), 1);
#endif

    n = fread(truthIds, sizeof(vtkIdType), nLocPnts, diskFile);
    if (n != static_cast<size_t>(nLocPnts))
    {
      std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
      return 1;
    }
#ifdef VTK_WORDS_BIGENDIAN
    SwapForBigEndian((unsigned char*)truthIds, sizeof(vtkIdType), nLocPnts);
#endif

    for (i = 0; (i < nLocPnts) && (retValue == 0); i++)
    {
      retValue = (resltIds[i] == truthIds[i]) ? 0 : 1;
    }
// ---------------------------------------------------------------------//
#endif

    if (retValue == 1)
      continue;

    // -----------------------------------------------------------------------
    // ------------------ location of the closest N points -------------------
    // -----------------------------------------------------------------------

    // memory allocation
    ptIdList->SetNumberOfIds(nClzNpts * 10); // to claim part of the memory
    resltIds = (vtkIdType*)realloc(resltIds, sizeof(vtkIdType) * nClzNpts * nLocPnts);
#ifndef VTK_BRUTE_FORCE_VERIFICATION
    truthIds = (vtkIdType*)realloc(truthIds, sizeof(vtkIdType) * nClzNpts * nLocPnts);
    n = fread(&numInsrt, sizeof(int), 1, diskFile);
    if (n != 1)
    {
      std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
      return 1;
    }
#ifdef VTK_WORDS_BIGENDIAN
    SwapForBigEndian((unsigned char*)(&numInsrt), sizeof(int), 1);
#endif

    n = fread(truthIds, sizeof(vtkIdType), numInsrt, diskFile);
    if (n != static_cast<size_t>(numInsrt))
    {
      std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
      return 1;
    }
#ifdef VTK_WORDS_BIGENDIAN
    SwapForBigEndian((unsigned char*)truthIds, sizeof(vtkIdType), numInsrt);
#endif
#endif

    // -----------------------------------------------------------------------
    // -----------------------------------------------------------------------
    // find the closest N points (with embedded brute-force verification)
    for (i = 0; i < nLocPnts; i++)
    {
      ptIdList->Reset();
      octLocat->FindClosestNPoints(nClzNpts, pLocPnts + (i << 1) + i, ptIdList);

//------------------------------------------------------------------------------
// verify the result in brute force mode
#ifdef VTK_BRUTE_FORCE_VERIFICATION

      // check the order of the closest points
      for (j = 0; j < nClzNpts; j++)
      {
        pointIdx = ptIdList->GetId(j);
        clzNdst2[j] = vtkMath::Distance2BetweenPoints(
          pLocPnts + (i << 1) + i, pDataPts + (pointIdx << 1) + pointIdx);
      }
      for (j = 0; (j < nClzNpts - 1) && (retValue == 0); j++)
      {
        retValue = (clzNdst2[j + 1] >= clzNdst2[j]) ? 0 : 1;
      }

      // write some data for locating the closest point
      // within a radius and the points within a radius
      minDist2[i] = clzNdst2[0];
      maxDist2[i] = clzNdst2[nClzNpts - 1];

      // check if there are any ignored but closer points
      for (j = 0; (j < numbPnts) && (retValue == 0); j++)
      {
        tmpDist2 =
          vtkMath::Distance2BetweenPoints(pLocPnts + (i << 1) + i, pDataPts + (j << 1) + j);
        if (tmpDist2 + INC_OCT_PNT_LOC_TESTS_ZERO // for cygwin and minGW
            < clzNdst2[nClzNpts - 1]              // Not "<=" here as there
          && ptIdList->IsId(j) == -1              // may be other points that were
        )
          retValue = 1; // rejected simply due to the limit of N
      }

      if (retValue == 1)
        break;

#endif
      // -------------------------------------------------------------------//

      // transfer the point indices for subsequent file writing purposes
      memcpy(resltIds + i * nClzNpts, ptIdList->GetPointer(0), sizeof(vtkIdType) * nClzNpts);
    }
// ---------------------------------------------------------------------//
// ---------------------------------------------------------------------//

//------------------------------------------------------------------------------
// rapid point index-based verification
#ifndef VTK_BRUTE_FORCE_VERIFICATION
    numInsrt = nClzNpts * nLocPnts; // data has been read at the beginning
    for (i = 0; (i < numInsrt) && (retValue == 0); i++)
    {
      retValue = (resltIds[i] == truthIds[i]) ? 0 : 1;
    }
#endif
    // ---------------------------------------------------------------------//

    if (retValue == 1)
      continue;

    // -----------------------------------------------------------------------
    // ------------ location of the closest point within a radius ------------
    // -----------------------------------------------------------------------

    // memory allocation
    resltIds = (vtkIdType*)realloc(resltIds, sizeof(vtkIdType) * nLocPnts * 3);

    // find the closest point within three radii
    for (i = 0; i < nLocPnts; i++)
    {
      j = (i << 1) + i;
      pointIdx = octLocat->FindClosestPoint(pLocPnts + j, minDist2 + i);

      // there are some points falling on the in-octree points
      fTempRad = (minDist2[i] <= INC_OCT_PNT_LOC_TESTS_ZERO) ? 0.000001 : minDist2[i];
      fTempRad = sqrt(fTempRad); // causes inaccuracy if minDist2 is nonzero

      resltIds[j] = octLocat->FindClosestPointWithinRadius(fTempRad * 0.5, pLocPnts + j, tmpDist2);
      if (minDist2[i] <= INC_OCT_PNT_LOC_TESTS_ZERO)
        resltIds[j + 1] =
          octLocat->FindClosestPointWithinRadius(fTempRad * 1.0, pLocPnts + j, tmpDist2);
      else // for non-zero cases, use the original squared radius for accuracy
        resltIds[j + 1] =
          octLocat->FindClosestPointWithinSquaredRadius(minDist2[i], pLocPnts + j, tmpDist2);
      resltIds[j + 2] =
        octLocat->FindClosestPointWithinRadius(fTempRad * 1.5, pLocPnts + j, tmpDist2);

//------------------------------------------------------------------------------
// verify the result in brute force mode
#ifdef VTK_BRUTE_FORCE_VERIFICATION
      if (((minDist2[i] > INC_OCT_PNT_LOC_TESTS_ZERO) && (resltIds[j] != -1)) ||
        ((minDist2[i] <= INC_OCT_PNT_LOC_TESTS_ZERO) && (resltIds[j] != pointIdx)) ||
        (resltIds[j + 1] != pointIdx) || (resltIds[j + 2] != pointIdx))
      {
        retValue = 1;
        break;
      }
// ---------------------------------------------------------------------//
#endif
    }

//------------------------------------------------------------------------------
// rapid point index-based verification
#ifndef VTK_BRUTE_FORCE_VERIFICATION
    n = fread(&numInsrt, sizeof(int), 1, diskFile);
    if (n != 1)
    {
      std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
      return 1;
    }
#ifdef VTK_WORDS_BIGENDIAN
    SwapForBigEndian((unsigned char*)(&numInsrt), sizeof(int), 1);
#endif

    truthIds = (vtkIdType*)realloc(truthIds, sizeof(vtkIdType) * numInsrt);
    n = fread(truthIds, sizeof(vtkIdType), numInsrt, diskFile);
    if (n != static_cast<size_t>(numInsrt))
    {
      std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
      return 1;
    }
#ifdef VTK_WORDS_BIGENDIAN
    SwapForBigEndian((unsigned char*)truthIds, sizeof(vtkIdType), numInsrt);
#endif

    for (i = 0; (i < numInsrt) && (retValue == 0); i++)
    {
      retValue = (resltIds[i] == truthIds[i]) ? 0 : 1;
    }
#endif
    // ---------------------------------------------------------------------//

    if (retValue == 1)
      continue;

    // -----------------------------------------------------------------------
    // --------------- location of the points within a radius ----------------
    // -----------------------------------------------------------------------

    ptIdList->Reset();
    for (i = 0; i < nLocPnts; i++)
    {
      // set the coordinate index of the point under check
      j = (i << 1) + i;
      minDist2[i] += INC_OCT_PNT_LOC_TESTS_ZERO; // for cygwin and minGW

      // obtain the points within three radii (use squared radii for test
      // as sqrt can incur inaccuracy that complicates the test)
      idxLists[0]->Reset();
      idxLists[1]->Reset();
      idxLists[2]->Reset();
      if (minDist2[i] <= INC_OCT_PNT_LOC_TESTS_ZERO)
      {
        // each ( maxDist2[i] * 0.3 ) has been guaranteed to be >
        // INC_OCT_PNT_LOC_TESTS_ZERO
        octLocat->FindPointsWithinSquaredRadius(maxDist2[i] * 0.3, pLocPnts + j, idxLists[0]);
        octLocat->FindPointsWithinSquaredRadius(maxDist2[i] * 0.6, pLocPnts + j, idxLists[1]);
        octLocat->FindPointsWithinSquaredRadius(maxDist2[i], pLocPnts + j, idxLists[2]);
      }
      else
      {
        octLocat->FindPointsWithinSquaredRadius(minDist2[i] * 0.5, pLocPnts + j, idxLists[0]);
        octLocat->FindPointsWithinSquaredRadius(minDist2[i], pLocPnts + j, idxLists[1]);
        octLocat->FindPointsWithinSquaredRadius(maxDist2[i], pLocPnts + j, idxLists[2]);
        if (idxLists[0]->GetNumberOfIds() == 0)
          idxLists[0]->InsertNextId(-1); // to handle an empty id list
      }

      // copy the point indices to a vtkIdList for comparison and file writing
      for (m = 0; m < 3; m++)
      {
        numInsrt = idxLists[m]->GetNumberOfIds();
        for (k = 0; k < numInsrt; k++)
        {
          ptIdList->InsertNextId(idxLists[m]->GetId(k));
        }
      }

//------------------------------------------------------------------------------
// verify the result in brute force mode
#ifdef VTK_BRUTE_FORCE_VERIFICATION

      // check if the monotonical property holds among the 3 point-index lists
      if (minDist2[i] <= INC_OCT_PNT_LOC_TESTS_ZERO)
      {
        pointIdx = octLocat->FindClosestPoint(pLocPnts + j);
        if (idxLists[0]->IsId(pointIdx) == -1 || idxLists[1]->IsId(pointIdx) == -1 ||
          idxLists[2]->IsId(pointIdx) == -1 ||
          idxLists[1]->GetNumberOfIds() < idxLists[0]->GetNumberOfIds() ||
          idxLists[2]->GetNumberOfIds() < idxLists[0]->GetNumberOfIds() ||
          idxLists[2]->GetNumberOfIds() < idxLists[1]->GetNumberOfIds())
          retValue = 1;
      }
      else
      {
        if (idxLists[0]->GetNumberOfIds() != 1 || idxLists[0]->GetId(0) != -1 ||
          idxLists[1]->GetNumberOfIds() < 1 ||
          idxLists[2]->GetNumberOfIds() < idxLists[1]->GetNumberOfIds())
          retValue = 1;
      }

      // check the points within each of the three radii
      for (m = 0; (m < 3) && (retValue == 0); m++)
      {
        // get the squared radius actually used
        if (minDist2[i] <= INC_OCT_PNT_LOC_TESTS_ZERO)
        {
          if (m == 0)
            fTempRad = maxDist2[i] * 0.3;
          else if (m == 1)
            fTempRad = maxDist2[i] * 0.6;
          else
            fTempRad = maxDist2[i];
        }
        else
        {
          if (m == 0)
            fTempRad = minDist2[i] * 0.5;
          else if (m == 1)
            fTempRad = minDist2[i];
          else
            fTempRad = maxDist2[i];
        }

        // check if there is any false insertion
        numInsrt = idxLists[m]->GetNumberOfIds();
        for (k = 0; (k < numInsrt) && (retValue == 0); k++)
        {
          if (m == 0 && idxLists[0]->GetId(0) == -1)
            break;

          pointIdx = idxLists[m]->GetId(k);
          pointIdx = (pointIdx << 1) + pointIdx;
          tmpDist2 = vtkMath::Distance2BetweenPoints(pLocPnts + j, pDataPts + pointIdx);

          if (tmpDist2 > fTempRad + INC_OCT_PNT_LOC_TESTS_ZERO)
            retValue = 1;
        }

        // check if there is any missed insertion
        numInsrt = 0;
        for (k = 0; k < numbPnts; k++)
        {
          tmpDist2 = vtkMath::Distance2BetweenPoints(pLocPnts + j, pDataPts + (k << 1) + k);
          if (tmpDist2 + INC_OCT_PNT_LOC_TESTS_ZERO <= fTempRad)
            numInsrt++;
        }

        // get the actual size of the vtkIdList for comparison
        int listSize = (m == 0 && idxLists[0]->GetId(0) == -1)
          ? 0 // for an actually nullptr vtkIdList
          : idxLists[m]->GetNumberOfIds();
        if (numInsrt > listSize)
          retValue = 1;
      }
#endif
      // -------------------------------------------------------------------//
    }

//------------------------------------------------------------------------------
// rapid point index-based verification
#ifndef VTK_BRUTE_FORCE_VERIFICATION
    n = fread(&numInsrt, sizeof(int), 1, diskFile);
    if (n != 1)
    {
      std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
      return 1;
    }
#ifdef VTK_WORDS_BIGENDIAN
    SwapForBigEndian((unsigned char*)(&numInsrt), sizeof(int), 1);
#endif

    truthIds = (vtkIdType*)realloc(truthIds, sizeof(vtkIdType) * numInsrt);
    n = fread(truthIds, sizeof(vtkIdType), numInsrt, diskFile);
    if (n != static_cast<size_t>(numInsrt))
    {
      std::cerr << "IO error " << __FILE__ << ":" << __LINE__ << "\n";
      return 1;
    }
#ifdef VTK_WORDS_BIGENDIAN
    SwapForBigEndian((unsigned char*)truthIds, sizeof(vtkIdType), numInsrt);
#endif

    vtkIdType* tmpPtIds = ptIdList->GetPointer(0);
    for (i = 0; (i < numInsrt) && (retValue == 0); i++)
    {
      retValue = (tmpPtIds[i] == truthIds[i]) ? 0 : 1;
    }
    tmpPtIds = nullptr;
#endif
    // ---------------------------------------------------------------------//
  }

  // memory clearance
  dataPnts = nullptr;
  unstruct = nullptr;
  if (ptIdList)
  {
    ptIdList->Delete();
    ptIdList = nullptr;
  }
  if (octLocat)
  {
    octLocat->Delete();
    octLocat = nullptr;
  }
  if (ugReader)
  {
    ugReader->Delete();
    ugReader = nullptr;
  }

  free(truthIds);
  truthIds = nullptr;

  free(resltIds);
  resltIds = nullptr;

  free(pDataPts);
  pDataPts = nullptr;

  free(pLocPnts);
  pLocPnts = nullptr;

  free(minDist2);
  minDist2 = nullptr;

  free(maxDist2);
  maxDist2 = nullptr;

  free(clzNdst2);
  clzNdst2 = nullptr;

  if (diskFile)
  {
    fclose(diskFile);
    diskFile = nullptr;
  }

  for (i = 0; i < 3; i++)
  {
    if (idxLists[i])
    {
      idxLists[i]->Delete();
      idxLists[i] = nullptr;
    }
  }

  return retValue;
}

// NOLINTEND(bugprone-suspicious-realloc-usage)
