// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void GenericExample::readFilterParameters(AbstractFilterParametersReader* reader, int index)
{
/* FILTER_WIDGETCODEGEN_AUTO_GENERATED_CODE BEGIN*/

  reader->openFilterGroup(this, index);
  setStlFilePrefix( reader->readValue("StlFilePrefix", getStlFilePrefix()) );
  setMaxIterations( reader->readValue("MaxIterations", getMaxIterations()) );
  setMisorientationTolerance( reader->readValue("MisorientationTolerance", getMisorientationTolerance()) );
  setInputFile( reader->readValue("InputFile", getInputFile()) );
  setInputPath( reader->readValue("InputPath", getInputPath()) );
  setOutputFile( reader->readValue("OutputFile", getOutputFile()) );
  setOutputPath( reader->readValue("OutputPath", getOutputPath()) );
  setWriteAlignmentShifts( reader->readValue("WriteAlignmentShifts", getWriteAlignmentShifts()) );
  setConversionType( reader->readValue("ConversionType", getConversionType()) );
  setDimensions( reader->readValue("Dimensions", getDimensions()) );
  setOrigin( reader->readValue("Origin", getOrigin()) );
  setCrystalSymmetryRotations( reader->readValue("CrystalSymmetryRotations", getCrystalSymmetryRotations()) );

  setSelectedVoxelCellArrayName( reader->readValue("SelectedVoxelCellArrayName", getSelectedVoxelCellArrayName()) );
  setSelectedVoxelFieldArrayName( reader->readValue("SelectedVoxelFieldArrayName", getSelectedVoxelFieldArrayName()) );
  setSelectedVoxelEnsembleArrayName( reader->readValue("SelectedVoxelEnsembleArrayName", getSelectedVoxelEnsembleArrayName()) );
  setSelectedSurfaceMeshPointArrayName( reader->readValue("SelectedSurfaceMeshPointArrayName", getSelectedSurfaceMeshPointArrayName()) );
  setSelectedSurfaceMeshFaceArrayName( reader->readValue("SelectedSurfaceMeshFaceArrayName", getSelectedSurfaceMeshFaceArrayName()) );
  setSelectedSurfaceMeshEdgeArrayName( reader->readValue("SelectedSurfaceMeshEdgeArrayName", getSelectedSurfaceMeshEdgeArrayName()) );
  setSelectedSolidMeshPointArrayName( reader->readValue("SelectedSolidMeshPointArrayName", getSelectedSolidMeshPointArrayName()) );
  setSelectedSolidMeshFaceArrayName( reader->readValue("SelectedSolidMeshFaceArrayName", getSelectedSolidMeshFaceArrayName()) );
  setSelectedSolidMeshEdgeArrayName( reader->readValue("SelectedSolidMeshEdgeArrayName", getSelectedSolidMeshEdgeArrayName()) );


  setStrVector( reader->readValue("StrVector", getStrVector() ) );
  reader->closeFilterGroup();
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AlignSections::readFilterParameters(AbstractFilterParametersReader* reader, int index)
{
  reader->openFilterGroup(this, index);
  setAlignmentShiftFileName( reader->readString("AlignmentShiftFileName", getAlignmentShiftFileName()));
  setWriteAlignmentShifts( reader->readValue("WriteAlignmentShifts", getWriteAlignmentShifts()));
  reader->closeFilterGroup();
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void TestFilter::writeFilterParameters(AbstractFilterParametersWriter* writer)

{
 /* Place code that will write the inputs values into a file. reference the
   AbstractFilterParametersWriter class for the proper API to use. */
  writer->writeValue("StlFilePrefix", getStlFilePrefix() );
  writer->writeValue("MaxIterations", getMaxIterations() );
  writer->writeValue("MisorientationTolerance", getMisorientationTolerance() );
  writer->writeValue("InputFile", getInputFile() );
  writer->writeValue("InputPath", getInputPath() );
  writer->writeValue("OutputFile", getOutputFile() );
  writer->writeValue("OutputPath", getOutputPath() );
  writer->writeValue("WriteAlignmentShifts", getWriteAlignmentShifts() );
  writer->writeValue("ConversionType", getConversionType() );
  writer->writeValue("SelectedCellArrayName", getSelectedCellArrayName() );
  writer->writeValue("SelectedFieldArrayName", getSelectedFieldArrayName() );
  writer->writeValue("SelectedEnsembleArrayName", getSelectedEnsembleArrayName() );
  writer->writeValue("SurfaceMeshPointArrayName", getSurfaceMeshPointArrayName() );
  writer->writeValue("SurfaceMeshFaceArrayName", getSurfaceMeshFaceArrayName() );
  writer->writeValue("SurfaceMeshEdgeArrayName", getSurfaceMeshEdgeArrayName() );
  writer->writeValue("SolidMeshPointArrayName", getSolidMeshPointArrayName() );
  writer->writeValue("SolidMeshFaceArrayName", getSolidMeshFaceArrayName() );
  writer->writeValue("SolidMeshEdgeArrayName", getSolidMeshEdgeArrayName() );
  writer->writeValue("Dimensions", getDimensions() );
  writer->writeValue("Origin", getOrigin() );

  writer->writeValue("CellComparisonInputs", m_CellComparisonInputs);
  writer->writeValue("AxisAngleRotations", m_AxisAngleRotations);
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
int GenericExample::writeFilterParameters(AbstractFilterParametersWriter* writer, int index)
{
  writer->openFilterGroup(this, index);
  /* Place code that will write the inputs values into a file. reference the
   AbstractFilterParametersWriter class for the proper API to use. */
  writer->writeValue("StlFilePrefix", getStlFilePrefix());
  writer->writeValue("MaxIterations", getMaxIterations());
  writer->writeValue("MisorientationTolerance", getMisorientationTolerance());
  writer->writeValue("InputFile", getInputFile());
  writer->writeValue("InputPath", getInputPath());
  writer->writeValue("OutputFile", getOutputFile());
  writer->writeValue("OutputPath", getOutputPath());
  writer->writeValue("WriteAlignmentShifts", getWriteAlignmentShifts());
  writer->writeValue("ConversionType", getConversionType());
  writer->writeValue("Dimensions", getDimensions());
  writer->writeValue("Origin", getOrigin());
  writer->writeValue("CrystalSymmetryRotations", getCrystalSymmetryRotations());

  writer->writeValue("SelectedVoxelCellArrayName", getSelectedVoxelCellArrayName());
  writer->writeValue("SelectedVoxelFieldArrayName", getSelectedVoxelFieldArrayName());
  writer->writeValue("SelectedVoxelEnsembleArrayName", getSelectedVoxelEnsembleArrayName());
  writer->writeValue("SelectedSurfaceMeshPointArrayName", getSelectedSurfaceMeshPointArrayName());
  writer->writeValue("SelectedSurfaceMeshFaceArrayName", getSelectedSurfaceMeshFaceArrayName());
  writer->writeValue("SelectedSurfaceMeshEdgeArrayName", getSelectedSurfaceMeshEdgeArrayName());
  writer->writeValue("SelectedSolidMeshPointArrayName", getSelectedSolidMeshPointArrayName());
  writer->writeValue("SelectedSolidMeshFaceArrayName", getSelectedSolidMeshFaceArrayName());
  writer->writeValue("SelectedSolidMeshEdgeArrayName", getSelectedSolidMeshEdgeArrayName());

  writer->writeValue("StrVector", getStrVector());

  writer->closeFilterGroup();
  return ++index;
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
int AlignSectionsFeature::writeFilterParameters(AbstractFilterParametersWriter* writer, int index)
{
  writer->writeValue("AlignmentShiftFileName", getAlignmentShiftFileName());
  writer->writeValue("WriteAlignmentShifts", getWriteAlignmentShifts());
    writer->closeFilterGroup();
    return ++index; // we want to return the next index that was just written to
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AlignSectionsFeature::dataCheck(bool preflight, size_t voxels, size_t fields, size_t ensembles)
{
  setErrorCondition(0);
  std::stringstream ss;
  VoxelDataContainer* m = getVoxelDataContainer();

  if(true == getWriteAlignmentShifts() && getAlignmentShiftFileName().empty() == true)
  {
    ss << "The Alignment Shift file name must be set before executing this filter.";
    setErrorCondition(-1);
     addErrorMessage(getHumanLabel(), ss.str(), getErrorCondition());
  }

  GET_PREREQ_DATA(m, DREAM3D, CellData, GoodVoxels, ss, -303, bool, BoolArrayType, voxels, 1)
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AlignSections::setupFilterParameters()
{
  FilterParameterVector parameters;
  QStringList linkedProps("AlignmentShiftFileName");
  parameters.push_back(LinkedBooleanFilterParameter::New("Write Alignment Shift File", "WriteAlignmentShifts", getWriteAlignmentShifts(), linkedProps, FilterParameter::Parameter));
  parameters.push_back(OutputFileFilterParameter::New("Alignment File", "AlignmentShiftFileName", getAlignmentShiftFileName(), FilterParameter::Parameter, "", "*.txt"));
  setFilterParameters(parameters);
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AlignSectionsMutualInformation::find_shifts(std::vector<int64_t>& xshifts, std::vector<int64_t>& yshifts)
{
  DataContainer::Pointer m = getDataContainerArray()->getDataContainer(getDataContainerName());

  int64_t totalPoints = m->getAttributeMatrix(getCellAttributeMatrixName())->getNumTuples();
  Int32ArrayType::Pointer p = Int32ArrayType::CreateArray((totalPoints * 1), "_INTERNAL_USE_ONLY_MIFeatureIds");
  m_FeatureIds = p->getPointer(0);

  std::ofstream outFile;
  if (getWriteAlignmentShifts() == true)
  {
    outFile.open(getAlignmentShiftFileName().toLatin1().data());
  }

  size_t udims[3] = { 0, 0, 0 };
  m->getGeometryAs<ImageGeom>()->getDimensions(udims);
#if (CMP_SIZEOF_SIZE_T == 4)
  typedef int32_t DimType;
#else
  typedef int64_t DimType;
#endif
  DimType dims[3] =
  {
    static_cast<DimType>(udims[0]),
    static_cast<DimType>(udims[1]),
    static_cast<DimType>(udims[2]),
  };

  float disorientation = 0.0f;
  float mindisorientation = std::numeric_limits<float>::max();
  float** mutualinfo12 = NULL;
  float* mutualinfo1 = NULL;
  float* mutualinfo2 = NULL;
  int32_t featurecount1 = 0, featurecount2 = 0;
  int64_t newxshift = 0;
  int64_t newyshift = 0;
  int64_t oldxshift = 0;
  int64_t oldyshift = 0;
  float count = 0.0f;
  DimType slice = 0;

  int32_t refgnum = 0, curgnum = 0;
  DimType refposition = 0;
  DimType curposition = 0;

  form_features_sections();

  std::vector<std::vector<float> >  misorients;
  misorients.resize(dims[0]);
  for (DimType a = 0; a < dims[0]; a++)
  {
    misorients[a].assign(dims[1], 0.0f);
  }

  for (DimType iter = 1; iter < dims[2]; iter++)
  {
    QString ss = QObject::tr("Aligning Sections || Determining Shifts || %1% Complete").arg(((float)iter / dims[2]) * 100);
    notifyStatusMessage(getMessagePrefix(), getHumanLabel(), ss);
    mindisorientation = std::numeric_limits<float>::max();
    slice = (dims[2] - 1) - iter;
    featurecount1 = featurecounts[slice];
    featurecount2 = featurecounts[slice + 1];
    mutualinfo12 = new float *[featurecount1];
    mutualinfo1 = new float[featurecount1];
    mutualinfo2 = new float[featurecount2];

    for (int32_t a = 0; a < featurecount1; a++)
    {
      mutualinfo1[a] = 0.0f;
      mutualinfo12[a] = new float[featurecount2];
      for (int32_t b = 0; b < featurecount2; b++)
      {
        mutualinfo12[a][b] = 0.0f;
        mutualinfo2[b] = 0.0f;
      }
    }
    oldxshift = -1;
    oldyshift = -1;
    newxshift = 0;
    newyshift = 0;
    for (DimType a = 0; a < dims[0]; a++)
    {
      for (DimType b = 0; b < dims[1]; b++)
      {
        misorients[a][b] = 0;
      }
    }
    while (newxshift != oldxshift || newyshift != oldyshift)
    {
      oldxshift = newxshift;
      oldyshift = newyshift;
      for (int32_t j = -3; j < 4; j++)
      {
        for (int32_t k = -3; k < 4; k++)
        {
          disorientation = 0;
          count = 0;
          if (misorients[k + oldxshift + dims[0] / 2][j + oldyshift + dims[1] / 2] == 0 && abs(k + oldxshift) < (dims[0] / 2)
              && (j + oldyshift) < (dims[1] / 2))
          {
            for (DimType l = 0; l < dims[1]; l = l + 4)
            {
              for (DimType n = 0; n < dims[0]; n = n + 4)
              {
                if ((l + j + oldyshift) >= 0 && (l + j + oldyshift) < dims[1] && (n + k + oldxshift) >= 0 && (n + k + oldxshift) < dims[0])
                {
                  refposition = ((slice + 1) * dims[0] * dims[1]) + (l * dims[0]) + n;
                  curposition = (slice * dims[0] * dims[1]) + ((l + j + oldyshift) * dims[0]) + (n + k + oldxshift);
                  refgnum = m_FeatureIds[refposition];
                  curgnum = m_FeatureIds[curposition];
                  if (curgnum >= 0 && refgnum >= 0)
                  {
                    mutualinfo12[curgnum][refgnum]++;
                    mutualinfo1[curgnum]++;
                    mutualinfo2[refgnum]++;
                    count++;
                  }
                }
                else
                {
                  mutualinfo12[0][0]++;
                  mutualinfo1[0]++;
                  mutualinfo2[0]++;
                }
              }
            }
            float ha = 0.0f;
            float hb = 0.0f;
            float hab = 0.0f;
            for (int32_t b = 0; b < featurecount1; b++)
            {
              mutualinfo1[b] = mutualinfo1[b] / count;
              if (mutualinfo1[b] != 0) { ha = ha + mutualinfo1[b] * logf(mutualinfo1[b]); }
            }
            for (int32_t c = 0; c < featurecount2; c++)
            {
              mutualinfo2[c] = mutualinfo2[c] / float(count);
              if (mutualinfo2[c] != 0) { hb = hb + mutualinfo2[c] * logf(mutualinfo2[c]); }
            }
            for (int32_t b = 0; b < featurecount1; b++)
            {
              for (int32_t c = 0; c < featurecount2; c++)
              {
                mutualinfo12[b][c] = mutualinfo12[b][c] / count;
                if (mutualinfo12[b][c] != 0) { hab = hab + mutualinfo12[b][c] * logf(mutualinfo12[b][c]); }
                float value = 0.0f;
                if (mutualinfo1[b] > 0 && mutualinfo2[c] > 0) { value = (mutualinfo12[b][c] / (mutualinfo1[b] * mutualinfo2[c])); }
                if (value != 0) { disorientation = disorientation + (mutualinfo12[b][c] * logf(value)); }
              }
            }
            for (int32_t b = 0; b < featurecount1; b++)
            {
              for (int32_t c = 0; c < featurecount2; c++)
              {
                mutualinfo12[b][c] = 0.0f;
                mutualinfo1[b] = 0.0f;
                mutualinfo2[c] = 0.0f;
              }
            }
            disorientation = 1.0f / disorientation;
            misorients[k + oldxshift + dims[0] / 2][j + oldyshift + dims[1] / 2] = disorientation;
            if (disorientation < mindisorientation)
            {
              newxshift = k + oldxshift;
              newyshift = j + oldyshift;
              mindisorientation = disorientation;
            }
          }
        }
      }
    }
    xshifts[iter] = xshifts[iter - 1] + newxshift;
    yshifts[iter] = yshifts[iter - 1] + newyshift;
    if (getWriteAlignmentShifts() == true)
    {
      outFile << slice << "	" << slice + 1 << "	" << newxshift << "	" << newyshift << "	" << xshifts[iter] << "	" << yshifts[iter] << "\n";
    }
    delete[] mutualinfo1;
    delete[] mutualinfo2;
    for (int32_t i = 0; i < featurecount1; i++)
    {
      delete mutualinfo12[i];
    }
    delete[] mutualinfo12;
    mutualinfo1 = NULL;
    mutualinfo2 = NULL;
    mutualinfo12 = NULL;
  }

  m->getAttributeMatrix(getCellAttributeMatrixName())->removeAttributeArray(DREAM3D::CellData::FeatureIds);

  if (getWriteAlignmentShifts() == true)
  {
    outFile.close();
  }
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AlignSectionsFeature::find_shifts(std::vector<int64_t>& xshifts, std::vector<int64_t>& yshifts)
{
  DataContainer::Pointer m = getDataContainerArray()->getDataContainer(getDataContainerName());

  std::ofstream outFile;
  if (getWriteAlignmentShifts() == true)
  {
    outFile.open(getAlignmentShiftFileName().toLatin1().data());
  }
  size_t udims[3] = { 0, 0, 0 };
  m->getGeometryAs<ImageGeom>()->getDimensions(udims);

  int64_t dims[3] =
  {
    static_cast<int64_t>(udims[0]),
    static_cast<int64_t>(udims[1]),
    static_cast<int64_t>(udims[2]),
  };

  float disorientation = 0.0f;
  float mindisorientation = std::numeric_limits<float>::max();
  int32_t newxshift = 0;
  int32_t newyshift = 0;
  int32_t oldxshift = 0;
  int32_t oldyshift = 0;
  float count = 0.0f;
  int64_t slice = 0;
  int64_t refposition = 0;
  int64_t curposition = 0;
  std::vector<std::vector<float> >  misorients(dims[0]);

  for (int64_t a = 0; a < dims[0]; a++)
  {
    misorients[a].assign(dims[1], 0.0f);
  }

  for (int64_t iter = 1; iter < dims[2]; iter++)
  {
    QString ss = QObject::tr("Aligning Sections || Determining Shifts || %1% Complete").arg(((float)iter / dims[2]) * 100);
    notifyStatusMessage(getMessagePrefix(), getHumanLabel(), ss);
    mindisorientation = std::numeric_limits<float>::max();
    slice = (dims[2] - 1) - iter;
    oldxshift = -1;
    oldyshift = -1;
    newxshift = 0;
    newyshift = 0;
    for (int64_t a = 0; a < dims[0]; a++)
    {
      for (int64_t b = 0; b < dims[1]; b++)
      {
        misorients[a][b] = 0;
      }
    }
    while (newxshift != oldxshift || newyshift != oldyshift)
    {
      oldxshift = newxshift;
      oldyshift = newyshift;
      for (int32_t j = -3; j < 4; j++)
      {
        for (int32_t k = -3; k < 4; k++)
        {
          disorientation = 0.0f;
          count = 0.0f;
          if (misorients[k + oldxshift + dims[0] / 2][j + oldyshift + dims[1] / 2] == 0.0f && abs(k + oldxshift) < (dims[0] / 2)
              && (j + oldyshift) < (dims[1] / 2))
          {
            for (int64_t l = 0; l < dims[1]; l = l + 4)
            {
              for (int64_t n = 0; n < dims[0]; n = n + 4)
              {
                if ((l + j + oldyshift) >= 0 && (l + j + oldyshift) < dims[1] && (n + k + oldxshift) >= 0 && (n + k + oldxshift) < dims[0])
                {
                  refposition = ((slice + 1) * dims[0] * dims[1]) + (l * dims[0]) + n;
                  curposition = (slice * dims[0] * dims[1]) + ((l + j + oldyshift) * dims[0]) + (n + k + oldxshift);
                  if (m_GoodVoxels[refposition] != m_GoodVoxels[curposition]) { disorientation++; }
                  count++;
                }
                else
                {

                }
              }
            }
            disorientation = disorientation / count;
            misorients[k + oldxshift + dims[0] / 2][j + oldyshift + dims[1] / 2] = disorientation;
            if (disorientation < mindisorientation)
            {
              newxshift = k + oldxshift;
              newyshift = j + oldyshift;
              mindisorientation = disorientation;
            }
          }
        }
      }
    }
    xshifts[iter] = xshifts[iter - 1] + newxshift;
    yshifts[iter] = yshifts[iter - 1] + newyshift;
    if (getWriteAlignmentShifts() == true)
    {
      outFile << slice << "	" << slice + 1 << "	" << newxshift << "	" << newyshift << "	" << xshifts[iter] << "	" << yshifts[iter] << std::endl;
    }
  }
  if (getWriteAlignmentShifts() == true)
  {
    outFile.close();
  }
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AdaptiveAlignmentMutualInformation::find_shifts(std::vector<int64_t>& xshifts, std::vector<int64_t>& yshifts, std::vector<float>& xneedshifts, std::vector<float>& yneedshifts)
{
	DataContainer::Pointer m = getDataContainerArray()->getDataContainer(getDataContainerName());

	int64_t totalPoints = m->getAttributeMatrix(getCellAttributeMatrixName())->getNumTuples();
	m_MIFeaturesPtr = Int32ArrayType::CreateArray((totalPoints * 1), "_INTERNAL_USE_ONLY_MIFeatureIds");
	m_MIFeaturesPtr->initializeWithZeros();
	int32_t* miFeatureIds = m_MIFeaturesPtr->getPointer(0);

	size_t udims[3] = { 0, 0, 0 };
	m->getGeometryAs<ImageGeom>()->getDimensions(udims);

	uint64_t dims[3] =
	{
		static_cast<uint64_t>(udims[0]),
		static_cast<uint64_t>(udims[1]),
		static_cast<uint64_t>(udims[2]),
	};

	uint64_t maxstoredshifts = 1;
	if (xneedshifts.size() > 0) maxstoredshifts = 20;

	float disorientation = 0.0f;

	std::vector<std::vector<int64_t>>  newxshift(dims[2]);
	std::vector<std::vector<int64_t>>  newyshift(dims[2]);
	std::vector<std::vector<float>>  mindisorientation(dims[2]);
	for (uint64_t a = 1; a < dims[2]; a++)
	{
		newxshift[a].resize(maxstoredshifts, 0);
		newyshift[a].resize(maxstoredshifts, 0);
		mindisorientation[a].resize(maxstoredshifts, std::numeric_limits<float>::max());
	}

	float** mutualinfo12 = NULL;
	float* mutualinfo1 = NULL;
	float* mutualinfo2 = NULL;
	int32_t featurecount1 = 0, featurecount2 = 0;
	int64_t oldxshift = 0;
	int64_t oldyshift = 0;
	float count = 0.0f;
	uint64_t slice = 0;

	int32_t refgnum = 0, curgnum = 0;
	uint64_t refposition = 0;
	uint64_t curposition = 0;

	form_features_sections();

	// Allocate a 2D Array which will be reused from slice to slice
	// second dimension is assigned in each cycle separately
	std::vector<std::vector<bool> >  misorients(dims[0]);

	const uint64_t halfDim0 = static_cast<uint64_t>(dims[0] * 0.5f);
	const uint64_t halfDim1 = static_cast<uint64_t>(dims[1] * 0.5f);
	uint64_t progInt = 0;

	for (uint64_t iter = 1; iter < dims[2]; iter++)
	{
		progInt = static_cast<uint64_t>(iter * 100 / static_cast<float>(dims[2]));
		QString ss = QObject::tr("Aligning Anisotropic Sections || Determining Shifts || %1% Complete").arg(progInt);
		notifyStatusMessage(getMessagePrefix(), getHumanLabel(), ss);

		slice = (dims[2] - 1) - iter;
		featurecount1 = featurecounts[slice];
		featurecount2 = featurecounts[slice + 1];
		mutualinfo12 = new float *[featurecount1];
		mutualinfo1 = new float[featurecount1];
		mutualinfo2 = new float[featurecount2];

		for (int32_t a = 0; a < featurecount1; a++)
		{
			mutualinfo1[a] = 0.0f;
			mutualinfo12[a] = new float[featurecount2];
			for (int32_t b = 0; b < featurecount2; b++)
			{
				mutualinfo12[a][b] = 0.0f;
				mutualinfo2[b] = 0.0f;
			}
		}
		oldxshift = -1;
		oldyshift = -1;

		for (uint64_t i = 0; i < dims[0]; i++)
		{
			misorients[i].assign(dims[1], false);
		}

		while (newxshift[iter][0] != oldxshift || newyshift[iter][0] != oldyshift)
		{
			oldxshift = newxshift[iter][0];
			oldyshift = newyshift[iter][0];

			for (int32_t j = -3; j < 4; j++)
			{
				for (int32_t k = -3; k < 4; k++)
				{
					disorientation = 0;
					count = 0;
					if (llabs(k + oldxshift) < halfDim0 && llabs(j + oldyshift) < halfDim1 && misorients[k + oldxshift + halfDim0][j + oldyshift + halfDim1] == false)
					{
						for (uint64_t l = 0; l < dims[1]; l = l + 4)
						{
							for (uint64_t n = 0; n < dims[0]; n = n + 4)
							{
								if ((l + j + oldyshift) >= 0 && (l + j + oldyshift) < dims[1] && (n + k + oldxshift) >= 0 && (n + k + oldxshift) < dims[0])
								{
									refposition = ((slice + 1) * dims[0] * dims[1]) + (l * dims[0]) + n;
									curposition = (slice * dims[0] * dims[1]) + ((l + j + oldyshift) * dims[0]) + (n + k + oldxshift);
									refgnum = miFeatureIds[refposition];
									curgnum = miFeatureIds[curposition];
									if (curgnum >= 0 && refgnum >= 0)
									{
										mutualinfo12[curgnum][refgnum]++;
										mutualinfo1[curgnum]++;
										mutualinfo2[refgnum]++;
										count++;
									}
								}
								else
								{
									mutualinfo12[0][0]++;
									mutualinfo1[0]++;
									mutualinfo2[0]++;
								}
							}
						}
						float ha = 0.0f;
						float hb = 0.0f;
						float hab = 0.0f;
						for (int32_t b = 0; b < featurecount1; b++)
						{
							mutualinfo1[b] = mutualinfo1[b] / count;
							if (mutualinfo1[b] != 0) { ha = ha + mutualinfo1[b] * logf(mutualinfo1[b]); }
						}
						for (int32_t c = 0; c < featurecount2; c++)
						{
							mutualinfo2[c] = mutualinfo2[c] / float(count);
							if (mutualinfo2[c] != 0) { hb = hb + mutualinfo2[c] * logf(mutualinfo2[c]); }
						}
						for (int32_t b = 0; b < featurecount1; b++)
						{
							for (int32_t c = 0; c < featurecount2; c++)
							{
								mutualinfo12[b][c] = mutualinfo12[b][c] / count;
								if (mutualinfo12[b][c] != 0) { hab = hab + mutualinfo12[b][c] * logf(mutualinfo12[b][c]); }
								float value = 0.0f;
								if (mutualinfo1[b] > 0 && mutualinfo2[c] > 0) { value = (mutualinfo12[b][c] / (mutualinfo1[b] * mutualinfo2[c])); }
								if (value != 0) { disorientation = disorientation + (mutualinfo12[b][c] * logf(value)); }
							}
						}
						for (int32_t b = 0; b < featurecount1; b++)
						{
							for (int32_t c = 0; c < featurecount2; c++)
							{
								mutualinfo12[b][c] = 0.0f;
								mutualinfo1[b] = 0.0f;
								mutualinfo2[c] = 0.0f;
							}
						}
						disorientation = 1.0f / disorientation;
						misorients[k + oldxshift + halfDim0][j + oldyshift + halfDim1] = true;

						// compare the new shift with currently stored ones
						int64_t s = maxstoredshifts;
						while (s - 1 >= 0 && disorientation < mindisorientation[iter][s - 1])
						{
							s--;
						}

						// new shift is stored with index 's' in the arrays
						if (s < maxstoredshifts)
						{
							// lag the shifts already stored
							for (int64_t t = maxstoredshifts - 1; t > s; t--)
							{
								newxshift[iter][t] = newxshift[iter][t - 1];
								newyshift[iter][t] = newyshift[iter][t - 1];
								mindisorientation[iter][t] = mindisorientation[iter][t - 1];
							}
							// store the new shift
							newxshift[iter][s] = k + oldxshift;
							newyshift[iter][s] = j + oldyshift;
							mindisorientation[iter][s] = disorientation;
						}
					}
				}
			}
		}
		xshifts[iter] = xshifts[iter - 1] + newxshift[iter][0];
		yshifts[iter] = yshifts[iter - 1] + newyshift[iter][0];

		delete[] mutualinfo1;
		delete[] mutualinfo2;
		for (int32_t i = 0; i < featurecount1; i++)
		{
			delete mutualinfo12[i];
		}
		delete[] mutualinfo12;
		mutualinfo1 = NULL;
		mutualinfo2 = NULL;
		mutualinfo12 = NULL;
	}

	std::vector<uint64_t> curindex(dims[2], 0);

	// find corrected shifts
	if (xneedshifts.size() > 0)
	{
		QString ss = QObject::tr("Aligning Anisotropic Sections || Correcting shifts");
		notifyStatusMessage(getMessagePrefix(), getHumanLabel(), ss);

		std::vector<float> changedisorientation(dims[2], 0);
		std::vector<uint64_t> changeindex(dims[2], 0);
		std::vector<float> changeerror(dims[2], 0);

		std::vector<float> xshiftsest;	// cumulative x-shifts estimated from SEM images
		std::vector<float> yshiftsest;	// cumulative y-shifts estimated from SEM images

		float curerror = 0;
		float tolerance = 1.0f / static_cast<float>(dims[2]);

		// evaluate error between current shifts and desired shifts
		if (xneedshifts.size() == 1)      // error is computed as misagreement between slopes
		{
			curerror = compute_error1(dims[2], 0, xneedshifts[0], yneedshifts[0], newxshift, newyshift, curindex);
		}
		else if (xneedshifts.size() > 1)  // error is computed as misagreement with shifts estimated from SEM images
		{
			xshiftsest.resize(dims[2], 0);
			yshiftsest.resize(dims[2], 0);
			for (uint64_t iter = 1; iter < dims[2]; iter++)
			{
				xshiftsest[iter] = xshiftsest[iter - 1] + xneedshifts[iter - 1];
				yshiftsest[iter] = yshiftsest[iter - 1] + yneedshifts[iter - 1];
			}
			curerror = compute_error2(dims[2], 0, xshiftsest, yshiftsest, newxshift, newyshift, curindex);
		}

		// iterative selection of a candidate shift, recomputing of current candidates, evaluation of error
		if (curerror > tolerance)
		{
			float minchangedisorientation = 0;
			float minchangeerror = 0;
			int64_t minchangeindex = 0;
			int64_t minchangeiter = 0;
			float olderror = 0;
			float newerror = 0;
			uint64_t progInt = 0;

			do
			{
				QString ss = QObject::tr("Aligning Anisotropic Sections || Correcting Shifts || Iteration %1").arg(++progInt);;
				notifyStatusMessage(getMessagePrefix(), getHumanLabel(), ss);
				if (getCancel() == true)
				{
					return;
				}

				olderror = curerror;
				for (uint64_t iter = 1; iter < dims[2]; iter++)
				{
					float newminerror = std::numeric_limits<float>::max();
					float newmindisorientation = std::numeric_limits<float>::max();
					uint64_t newminindex = 0;
					for (uint64_t index = curindex[iter] + 1; index < maxstoredshifts; index++)
					{
						// recompute error for the configuration with this candidate changed
						if (xneedshifts.size() == 1)
						{
							newerror = compute_error1(iter, index, xneedshifts[0], yneedshifts[0], newxshift, newyshift, curindex);
						}
						else if (xneedshifts.size() > 1)
						{
							newerror = compute_error2(iter, index, xshiftsest, yshiftsest, newxshift, newyshift, curindex);
						}

						// compare the new error with the best current error
						if (newerror < curerror &&
							mindisorientation[iter][index] / mindisorientation[iter][0] < newmindisorientation)
						{
							newminerror = newerror;
							newminindex = index;
							newmindisorientation = mindisorientation[iter][index] / mindisorientation[iter][0];
						}
					}
					// assign best error, corresponding index and disorientation value for this slice
					changeerror[iter] = newminerror;
					changeindex[iter] = newminindex;
					changedisorientation[iter] = newmindisorientation;
				}

				// among all slices, find the best candidate (with minimum disorientation change)
				minchangedisorientation = std::numeric_limits<float>::max() - 1;
				minchangeerror = std::numeric_limits<float>::max();
				minchangeindex = 0;
				minchangeiter = 0;
				for (uint64_t iter = 1; iter < dims[2]; iter++)
				{
					if (changeerror[iter] < curerror &&
						(changedisorientation[iter] < minchangedisorientation ||
						(changedisorientation[iter] == minchangedisorientation &&
						llabs(newxshift[iter][changeindex[iter]]) + llabs(newyshift[iter][changeindex[iter]]) < llabs(newxshift[iter][minchangeindex]) + llabs(newyshift[iter][minchangeindex]))))
					{
						minchangeiter = iter;
						minchangeindex = changeindex[iter];
						minchangedisorientation = changedisorientation[iter];
						minchangeerror = changeerror[iter];
					}
				}

				if (minchangeerror < curerror && minchangeerror >= tolerance)
				{
					// assign the best candidate
					changedisorientation[minchangeiter] = minchangedisorientation;
					curindex[minchangeiter] = minchangeindex;
					// reassign current error
					curerror = minchangeerror;
				}

			} while (minchangedisorientation < std::numeric_limits<float>::max() - 1 && curerror < olderror && curerror > tolerance);
		}
	}


	if (getWriteAlignmentShifts() == true)
	{
		std::ofstream outFile;
		outFile.open(getAlignmentShiftFileName().toLatin1().data());
		for (uint64_t iter = 1; iter < dims[2]; iter++)
		{
			slice = (dims[2] - 1) - iter;
			xshifts[iter] = xshifts[iter - 1] + newxshift[iter][curindex[iter]];
			yshifts[iter] = yshifts[iter - 1] + newyshift[iter][curindex[iter]];
			outFile << slice << "	" << slice + 1 << "	" << newxshift[iter][curindex[iter]] << "	" << newyshift[iter][curindex[iter]] << "	" << xshifts[iter] << "	" << yshifts[iter] << "\n";
		}
		outFile.close();
	}

	m->getAttributeMatrix(getCellAttributeMatrixName())->removeAttributeArray(DREAM3D::CellData::FeatureIds);

}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AlignSectionsFeature::find_shifts(std::vector<int> &xshifts, std::vector<int> &yshifts)
{
  VoxelDataContainer* m = getVoxelDataContainer();
  //int64_t totalPoints = m->totalPoints();

  std::ofstream outFile;
  if (getWriteAlignmentShifts() == true) {
    outFile.open(getAlignmentShiftFileName().c_str());
  }
  size_t udims[3] = {0,0,0};
  m->getDimensions(udims);
#if (CMP_SIZEOF_SIZE_T == 4)
  typedef int32_t DimType;
#else
  typedef int64_t DimType;
#endif
  DimType dims[3] = {
    static_cast<DimType>(udims[0]),
    static_cast<DimType>(udims[1]),
    static_cast<DimType>(udims[2]),
  };

  float disorientation = 0;
  float mindisorientation = 100000000;
  int newxshift = 0;
  int newyshift = 0;
  int oldxshift = 0;
  int oldyshift = 0;
  float count = 0;
  int slice = 0;
//  int xspot, yspot;
  int refposition = 0;
  int curposition = 0;

  std::vector<std::vector<float> >  misorients;
  misorients.resize(dims[0]);
  for (DimType a = 0; a < dims[0]; a++)
  {
    misorients[a].resize(dims[1], 0.0);
  }
  for (DimType iter = 1; iter < dims[2]; iter++)
  {
    std::stringstream ss;
    ss << "Aligning Sections - Determining Shifts - " << ((float)iter/dims[2])*100 << " Percent Complete";
  //  notifyStatusMessage(ss.str());
    mindisorientation = 100000000;
    slice = static_cast<int>( (dims[2] - 1) - iter );
    oldxshift = -1;
    oldyshift = -1;
    newxshift = 0;
    newyshift = 0;
    for (DimType a = 0; a < dims[0]; a++)
    {
      for (DimType b = 0; b < dims[1]; b++)
      {
        misorients[a][b] = 0;
      }
    }
    while (newxshift != oldxshift || newyshift != oldyshift)
    {
      oldxshift = newxshift;
      oldyshift = newyshift;
      for (int j = -3; j < 4; j++)
      {
        for (int k = -3; k < 4; k++)
        {
          disorientation = 0;
          count = 0;
          if(misorients[k + oldxshift + size_t(dims[0] / 2)][j + oldyshift + (size_t)(dims[1] / 2)] == 0 && abs(k + oldxshift) < (dims[0] / 2)
              && (j + oldyshift) < (dims[1] / 2))
          {
            for (DimType l = 0; l < dims[1]; l = l + 4)
            {
              for (DimType n = 0; n < dims[0]; n = n + 4)
              {
                if((l + j + oldyshift) >= 0 && (l + j + oldyshift) < dims[1] && (n + k + oldxshift) >= 0 && (n + k + oldxshift) < dims[0])
                {
                  refposition = static_cast<int>( ((slice + 1) * dims[0] * dims[1]) + (l * dims[0]) + n );
                  curposition = static_cast<int>( (slice * dims[0] * dims[1]) + ((l + j + oldyshift) * dims[0]) + (n + k + oldxshift) );
                  if(m_GoodVoxels[refposition] != m_GoodVoxels[curposition]) disorientation++;
                count++;
                }
                else
                {

        }
              }
            }
            disorientation = disorientation/count;
            misorients[k + oldxshift + int(dims[0] / 2)][j + oldyshift + int(dims[1] / 2)] = disorientation;
            if(disorientation < mindisorientation)
            {
              newxshift = k + oldxshift;
              newyshift = j + oldyshift;
              mindisorientation = disorientation;
            }
          }
        }
      }
    }
    xshifts[iter] = xshifts[iter-1] + newxshift;
    yshifts[iter] = yshifts[iter-1] + newyshift;
    if (getWriteAlignmentShifts() == true) {
      outFile << slice << "	" << slice+1 << "	" << newxshift << "	" << newyshift << "	" << xshifts[iter] << "	" << yshifts[iter] << std::endl;
    }
  }
  if (getWriteAlignmentShifts() == true) {
    outFile.close();
  }
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AlignSectionsFeatureCentroid::find_shifts(std::vector<int64_t>& xshifts, std::vector<int64_t>& yshifts)
{
  DataContainer::Pointer m = getDataContainerArray()->getDataContainer(getDataContainerName());

  std::ofstream outFile;
  if (getWriteAlignmentShifts() == true)
  {
    outFile.open(getAlignmentShiftFileName().toLatin1().data());
  }
  size_t udims[3] = { 0, 0, 0 };
  m->getGeometryAs<ImageGeom>()->getDimensions(udims);
#if (CMP_SIZEOF_SIZE_T == 4)
  typedef int32_t DimType;
#else
  typedef int64_t DimType;
#endif
  DimType dims[3] =
  {
    static_cast<DimType>(udims[0]),
    static_cast<DimType>(udims[1]),
    static_cast<DimType>(udims[2]),
  };

  int64_t newxshift = 0;
  int64_t newyshift = 0;
  int64_t count = 0;
  DimType slice = 0;
  DimType point = 0;
  float xRes = m->getGeometryAs<ImageGeom>()->getXRes();
  float yRes = m->getGeometryAs<ImageGeom>()->getYRes();
  std::vector<float> xCentroid(dims[2], 0.0f);
  std::vector<float> yCentroid(dims[2], 0.0f);

  for (DimType iter = 0; iter < dims[2]; iter++)
  {
    count = 0;
    xCentroid[iter] = 0;
    yCentroid[iter] = 0;

    QString ss = QObject::tr("Aligning Sections || Determining Shifts || %1% Complete").arg(((float)iter / dims[2]) * 100);
    notifyStatusMessage(getMessagePrefix(), getHumanLabel(), ss);
    slice = static_cast<int>( (dims[2] - 1) - iter );
    for (DimType l = 0; l < dims[1]; l++)
    {
      for (DimType n = 0; n < dims[0]; n++)
      {
        point = ((slice) * dims[0] * dims[1]) + (l * dims[0]) + n;
        if (m_GoodVoxels[point] == true)
        {
          xCentroid[iter] = xCentroid[iter] + (float(n) * xRes);
          yCentroid[iter] = yCentroid[iter] + (float(l) * yRes);
          count++;
        }
      }
    }
    xCentroid[iter] = xCentroid[iter] / float(count);
    yCentroid[iter] = yCentroid[iter] / float(count);
  }
  for (DimType iter = 1; iter < dims[2]; iter++)
  {
    slice = (dims[2] - 1) - iter;
    if (m_UseReferenceSlice == true)
    {
      xshifts[iter] = static_cast<int64_t>((xCentroid[iter] - xCentroid[m_ReferenceSlice]) / xRes);
      yshifts[iter] = static_cast<int64_t>((yCentroid[iter] - yCentroid[m_ReferenceSlice]) / yRes);
    }
    else
    {
      xshifts[iter] = xshifts[iter - 1] + static_cast<int64_t>((xCentroid[iter] - xCentroid[iter - 1]) / xRes);
      yshifts[iter] = yshifts[iter - 1] + static_cast<int64_t>((yCentroid[iter] - yCentroid[iter - 1]) / yRes);
    }
    if (getWriteAlignmentShifts() == true)
    {
      outFile << slice << "	" << slice + 1 << "	" << newxshift << "	" << newyshift << "	" << xshifts[iter] << "	" << yshifts[iter] << " " << xCentroid[iter] << " " << yCentroid[iter] << std::endl;
    }
  }
  if (getWriteAlignmentShifts() == true)
  {
    outFile.close();
  }
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void AlignSections::writeFilterParameters(AbstractFilterParametersWriter* writer)
{
  writer->writeValue("AlignmentShiftFileName", getAlignmentShiftFileName());
  writer->writeValue("WriteAlignmentShifts", getWriteAlignmentShifts());
}