/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkExhaustiveOptimizer_h
#define itkExhaustiveOptimizer_h

#include "itkIntTypes.h"
#include "itkSingleValuedNonLinearOptimizer.h"
#include "ITKOptimizersExport.h"

namespace itk
{
/** \class ExhaustiveOptimizer
 * \brief Optimizer that fully samples a grid on the parametric space.
 *
 * This optimizer is equivalent to an exahaustive search in a discrete grid
 * defined over the parametric space. The grid is centered on the initial
 * position. The subdivisions of the grid along each one of the dimensions
 * of the parametric space is defined by an array of number of steps.
 *
 * A typical use is to plot the metric space to get an idea of how noisy it
 * is. An example is given below, where it is desired to plot the metric
 * space with respect to translations along x, y and z in a 3D registration
 * application:
 *     Here it is assumed that the transform is Euler3DTransform.
 *
 * \code
 *  OptimizerType::StepsType steps( m_Transform->GetNumberOfParameters() );
 *  steps[0] = 10;
 *  steps[1] = 10;
 *  steps[2] = 10;
 *  m_Optimizer->SetNumberOfSteps( steps );
 *  m_Optimizer->SetStepLength( 2 );
 * \endcode
 *
 * The optimizer throws IterationEvents after every iteration. We use this to plot
 * the metric space in an image as follows:
 *
 * \code
 *  if( itk::IterationEvent().CheckEvent(& event ) )
 *  {
 *    IndexType index;
 *    index[0] = m_Optimizer->GetCurrentIndex()[0];
 *    index[1] = m_Optimizer->GetCurrentIndex()[1];
 *    index[2] = m_Optimizer->GetCurrentIndex()[2];
 *    image->SetPixel( index, m_Optimizer->GetCurrentValue() );
 *  }
 * \endcode
 *
 * The image size is expected to be 11 x 11 x 11.
 *
 * If you wish to use different step lengths along each parametric axis,
 * you can use the SetScales() method. This accepts an array, each element
 * represents the number of subdivisions per step length. For instance scales
 * of [0.5 1 4] along with a step length of 2 will cause the optimizer
 * to search the metric space on a grid with x,y,z spacing of [1 2 8].
 *
 * Physical dimensions of the grid are influenced by both the scales and
 * the number of steps along each dimension, a side of the region is
 * stepLength*(2*numberOfSteps[d]+1)*scaling[d].
 *
 * \ingroup Numerics Optimizers
 * \ingroup ITKOptimizers
 */
class ITKOptimizers_EXPORT ExhaustiveOptimizer:
  public SingleValuedNonLinearOptimizer
{
public:
  /** Standard "Self" typedef. */
  typedef ExhaustiveOptimizer            Self;
  typedef SingleValuedNonLinearOptimizer Superclass;
  typedef SmartPointer< Self >           Pointer;
  typedef SmartPointer< const Self >     ConstPointer;

  typedef Array< SizeValueType > StepsType;
  /** Method for creation through the object factory. */
  itkNewMacro(Self);

  /** Run-time type information (and related methods). */
  itkTypeMacro(ExhaustiveOptimizer, SingleValuedNonLinearOptimizer);

  virtual void    StartOptimization(void) ITK_OVERRIDE;

  void StartWalking();

  void ResumeWalking();

  void StopWalking();

  itkSetMacro(StepLength, double);
  itkSetMacro(NumberOfSteps, StepsType);
  itkGetConstReferenceMacro(StepLength, double);
  itkGetConstReferenceMacro(NumberOfSteps, StepsType);
  itkGetConstReferenceMacro(CurrentValue, MeasureType);
  itkGetConstReferenceMacro(MaximumMetricValue, MeasureType);
  itkGetConstReferenceMacro(MinimumMetricValue, MeasureType);
  itkGetConstReferenceMacro(MinimumMetricValuePosition, ParametersType);
  itkGetConstReferenceMacro(MaximumMetricValuePosition, ParametersType);
  itkGetConstReferenceMacro(CurrentIndex, ParametersType);
  itkGetConstReferenceMacro(MaximumNumberOfIterations, SizeValueType);

  /** Get the reason for termination */
  virtual const std::string GetStopConditionDescription() const ITK_OVERRIDE;

protected:
  ExhaustiveOptimizer();
  virtual ~ExhaustiveOptimizer() {}
  virtual void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE;

  /** Advance to the next grid position. */
  void AdvanceOneStep();

  void IncrementIndex(ParametersType & param);

protected:
  MeasureType m_CurrentValue;

  StepsType m_NumberOfSteps;

  SizeValueType m_CurrentIteration;

  bool m_Stop;

  unsigned int m_CurrentParameter;

  double m_StepLength;

  ParametersType m_CurrentIndex;

  SizeValueType m_MaximumNumberOfIterations;

  MeasureType m_MaximumMetricValue;

  MeasureType m_MinimumMetricValue;

  ParametersType m_MinimumMetricValuePosition;

  ParametersType m_MaximumMetricValuePosition;

private:
  ExhaustiveOptimizer(const Self &) ITK_DELETE_FUNCTION;
  void operator=(const Self &) ITK_DELETE_FUNCTION;

  std::ostringstream m_StopConditionDescription;
};
} // end namespace itk

#endif
