// This is core/vnl/vnl_scalar_join_iterator.h
#ifndef vnl_scalar_join_iterator_h_
#define vnl_scalar_join_iterator_h_
//:
// \file
// \brief  Database join on matrix columns
// \author Andrew W. Fitzgibbon, Oxford RRG
// \date   27 Dec 96
//
//  vnl_scalar_join_iterator implements a fast database join on columns
//  of matrices of scalars.  "Scalar" here really means that the
//  objects have comparison operators.  The cost is O(n log n) where
//  n is the number of rows, all for the two sorts in the ctor.
//
//  CAVEAT: The current implementation fudges multiple occurrences
//  of the same key in the source column.  For example,
// \verbatim
//    join  1 3 and  3 5 on columns 2 and 1 respectively
//          2 3      3 6
// \endverbatim
//  should give
// \verbatim
//          1 3 3 5
//          1 3 3 6
//          2 3 3 5
//          2 3 3 6
// \endverbatim
//  and it doesn't.  Contact awf if you need this to work.
//
// \verbatim
//  Modifications
//   LSB (Manchester) Documentation Tidied
//   Feb.2002 - Peter Vanroose - brief doxygen comment placed on single line
// \endverbatim
//
//-----------------------------------------------------------------------------

#include <list>
#ifdef _MSC_VER
#  include <vcl_msvc_warnings.h>
#endif
#include "vnl_matrix.h"
#include "vnl/vnl_export.h"

template <class T>
class vnl_scalar_join_iterator_indexed_pair;

//: Database join on matrix columns.
//  vnl_scalar_join_iterator implements a fast database join on columns
//  of matrices of scalars.  "Scalar" here really means that the
//  objects have comparison operators.  The cost is O(n log n) where
//  n is the number of rows, all for the two sorts in the ctor.
//
//  CAVEAT: The current implementation fudges multiple occurrences
//  of the same key in the source column.  For example,
// \verbatim
//  join  1 3 and  3 5 on columns 2 and 1 respectively
//        2 3      3 6
// \endverbatim
//  should give
// \verbatim
//        1 3 3 5
//        1 3 3 6
//        2 3 3 5
//        2 3 3 6
// \endverbatim
//  and it doesn't.  Contact awf if you need this to work.

template <class T>
class VNL_EXPORT vnl_scalar_join_iterator
{
private:
protected:
  unsigned n1;
  unsigned n2;
  std::list<vnl_scalar_join_iterator_indexed_pair<T>> * pI1;
  std::list<vnl_scalar_join_iterator_indexed_pair<T>> * pI2;
  std::list<vnl_scalar_join_iterator_indexed_pair<T>> & I1;
  std::list<vnl_scalar_join_iterator_indexed_pair<T>> & I2;
  typename std::list<vnl_scalar_join_iterator_indexed_pair<T>>::iterator index1;
  typename std::list<vnl_scalar_join_iterator_indexed_pair<T>>::iterator index2;

public:
  //: Initialize this iterator to the join of relation1(:,column1) and relation2(:,column2).
  // The algorithm sorts an array of pointers to each row and
  // traversal of the iterator runs through these to produce the join.
  // After construction the row1() and row2() methods indicate the first pair.
  vnl_scalar_join_iterator(const vnl_matrix<T> & relation1,
                           unsigned column1,
                           const vnl_matrix<T> & relation2,
                           unsigned column2);

  ~vnl_scalar_join_iterator();


  //: Return true if all pairs have been seen.
  explicit
  operator bool() const
  {
    return (!done()) ? true : false;
  }

  //: Return false if all pairs have been seen.
  bool
  operator!() const
  {
    return (!done()) ? false : true;
  }

  //: Advance to the next pair.  This is prefix ++.
  inline vnl_scalar_join_iterator<T> &
  operator++()
  {
    next();
    return *this;
  }

  bool
  done() const;
  void
  next();

  //: Return the index of the current row in the first relation.
  unsigned
  row1() const;
  //: Return the index of the current row in the second relation.
  unsigned
  row2() const;

private:
  // Postfix ++ is private as it would be costly to implement.
  vnl_scalar_join_iterator<T>
  operator++(int);
};

//: Helper class to hold the sorted arrays of indices.
template <class T>
class VNL_EXPORT vnl_scalar_join_iterator_indexed_pair
{
public:
  const T * object;
  int original_index;

  vnl_scalar_join_iterator_indexed_pair() = default;
  vnl_scalar_join_iterator_indexed_pair(const T * object_, int original_index_)
    : object(object_)
    , original_index(original_index_)
  {}

  bool
  operator==(const vnl_scalar_join_iterator_indexed_pair<T> & that) const;
  bool
  operator<(const vnl_scalar_join_iterator_indexed_pair<T> & that) const;
};

#endif // vnl_scalar_join_iterator_h_
