/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  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
 *
 *         https://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.
 *
 *=========================================================================*/

// Software Guide : BeginLatex
//
// \index{Statistics!Order statistics}
// \index{Statistics!Sorting}
// \index{Statistics!Insert sort}
// \index{Statistics!Heap sort}
// \index{Statistics!Introspective sort}
// \index{Statistics!Quick select}
//
// \index{itk::Statistics::Subsample}
// \index{itk::Statistics::InsertSort}
// \index{itk::Statistics::HeapSort}
// \index{itk::Statistics::IntrospectiveSort}
// \index{itk::Statistics::QuickSelect}
//
// Sometimes we want to sort the measurement vectors in a sample. The sorted
// vectors may reveal some characteristics of the sample.  The \emph{insert
// sort}, the \emph{heap sort}, and the \emph{introspective sort} algorithms
// \cite{Musser1997} for samples are implemented in ITK. To learn pros and
// cons of each algorithm, please refer to \cite{Duda2000}. ITK also
// offers the \emph{quick select} algorithm.
//
// Among the subclasses of the \subdoxygen{Statistics}{Sample}, only the
// class \subdoxygen{Statistics}{Subsample} allows users to change the order
// of the measurement vector. Therefore, we must create a Subsample to do any
// sorting or selecting.
//
// We include the header files for the \subdoxygen{Statistics}{ListSample}
// and the \code{Subsample} classes.
//
// Software Guide : EndLatex


// Software Guide : BeginCodeSnippet
#include "itkListSample.h"
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// The sorting and selecting related functions are in the include file
// \code{itkStatisticsAlgorithm.h}. Note that all functions in this file
// are in the \code{itk::Statistics::Algorithm} namespace.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkStatisticsAlgorithm.h"
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// We need another header for measurement vectors. We are going to use
// the \doxygen{Vector} class which is a subclass of the \doxygen{FixedArray}
// in this example.
//
// We define the types of the measurement vectors, the sample, and the
// subsample.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkVector.h"
// Software Guide : EndCodeSnippet

using MeasurementType = int;
using MeasurementVectorType = itk::Vector<MeasurementType, 2>;
using SampleType = itk::Statistics::ListSample<MeasurementVectorType>;
using SubsampleType = itk::Statistics::Subsample<SampleType>;

// Software Guide : BeginLatex
//
// We define two functions for convenience. The first one clears the content
// of the subsample and fill it with the measurement vectors from the
// sample.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
void
initializeSubsample(SubsampleType * subsample, SampleType * sample)
{
  subsample->Clear();
  subsample->SetSample(sample);
  subsample->InitializeWithAllInstances();
}
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// The second one prints out the content of the subsample using the
// Subsample's iterator interface.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
void
printSubsample(SubsampleType * subsample, const char * header)
{
  std::cout << std::endl;
  std::cout << header << std::endl;
  SubsampleType::Iterator iter = subsample->Begin();
  while (iter != subsample->End())
  {
    std::cout << "instance identifier = " << iter.GetInstanceIdentifier()
              << " \t measurement vector = " << iter.GetMeasurementVector()
              << std::endl;
    ++iter;
  }
}
// Software Guide : EndCodeSnippet

int
main()
{
  // Software Guide : BeginLatex
  //
  // The following code snippet will create a ListSample object
  // with two-component int measurement vectors and put the measurement
  // vectors: [5,5] - 5 times, [4,4] - 4 times, [3,3] - 3 times, [2,2] -
  // 2 times,[1,1] - 1 time into the \code{sample}.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  auto sample = SampleType::New();

  MeasurementVectorType mv;
  for (unsigned int i = 5; i > 0; --i)
  {
    for (unsigned int j = 0; j < 2; ++j)
    {
      mv[j] = (MeasurementType)i;
    }
    for (unsigned int j = 0; j < i; ++j)
    {
      sample->PushBack(mv);
    }
  }
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginLatex
  //
  // We create a Subsample object and plug-in the \code{sample}.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  auto subsample = SubsampleType::New();
  subsample->SetSample(sample);
  initializeSubsample(subsample, sample);
  printSubsample(subsample, "Unsorted");
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginLatex
  //
  // The common parameters to all the algorithms are the
  // Subsample object (\code{subsample}), the dimension
  // (\code{activeDimension}) that will be considered for the sorting or
  // selecting (only the component belonging to the dimension of the
  // measurement vectors will be considered), the beginning index, and
  // the ending index of the measurement vectors in the
  // \code{subsample}. The sorting or selecting algorithms are applied
  // only to the range specified by the beginning index and the ending
  // index. The ending index should be the actual last index plus one.
  //
  // The \doxygen{InsertSort} function does not require any other optional
  // arguments. The following function call will sort the all measurement
  // vectors in the \code{subsample}. The beginning index is \code{0}, and
  // the ending index is the number of the measurement vectors in the
  // \code{subsample}.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  constexpr int activeDimension = 0;
  itk::Statistics::Algorithm::InsertSort<SubsampleType>(
    subsample, activeDimension, 0, subsample->Size());
  printSubsample(subsample, "InsertSort");
  // Software Guide : EndCodeSnippet


  // Software Guide : BeginLatex
  //
  // We sort the \code{subsample} using the heap sort algorithm. The
  // arguments are identical to those of the insert sort.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  initializeSubsample(subsample, sample);
  itk::Statistics::Algorithm::HeapSort<SubsampleType>(
    subsample, activeDimension, 0, subsample->Size());
  printSubsample(subsample, "HeapSort");
  // Software Guide : EndCodeSnippet


  // Software Guide : BeginLatex
  //
  // The introspective sort algorithm needs an additional argument that
  // specifies when to stop the introspective sort loop and sort the fragment
  // of the sample using the heap sort algorithm. Since we set the threshold
  // value as \code{16}, when the sort loop reach the point where the number
  // of measurement vectors in a sort loop is not greater than \code{16}, it
  // will sort that fragment using the insert sort algorithm.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  initializeSubsample(subsample, sample);
  itk::Statistics::Algorithm::IntrospectiveSort<SubsampleType>(
    subsample, activeDimension, 0, subsample->Size(), 16);
  printSubsample(subsample, "IntrospectiveSort");
  // Software Guide : EndCodeSnippet


  // Software Guide : BeginLatex
  //
  // We query the median of the measurements along the
  // \code{activeDimension}. The last argument tells the algorithm that we
  // want to get the \code{subsample->Size()/2}-th element along the
  // \code{activeDimension}. The quick select algorithm changes the order of
  // the measurement vectors.
  //
  // Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  initializeSubsample(subsample, sample);
  const SubsampleType::MeasurementType median =
    itk::Statistics::Algorithm::QuickSelect<SubsampleType>(subsample,
                                                           activeDimension,
                                                           0,
                                                           subsample->Size(),
                                                           subsample->Size() /
                                                             2);
  std::cout << std::endl;
  std::cout << "Quick Select: median = " << median << std::endl;
  // Software Guide : EndCodeSnippet

  return EXIT_SUCCESS;
}
