// This is core/vnl/vnl_scalar_join_iterator.hxx
#ifndef vnl_scalar_join_iterator_hxx_
#define vnl_scalar_join_iterator_hxx_
//:
// \file
//
// \author Andrew W. Fitzgibbon, Oxford RRG
// \date   27 Dec 96
//
//-----------------------------------------------------------------------------

#include <list>
#include <iostream>
#include "vnl_scalar_join_iterator.h"
#ifdef _MSC_VER
#  include <vcl_msvc_warnings.h>
#endif

#define VNL_SCALAR_JOIN_ITERATOR_INSTANTIATE(T) \
template class VNL_EXPORT vnl_scalar_join_iterator_indexed_pair<T >;\
template class VNL_EXPORT vnl_scalar_join_iterator<T >; \
template VNL_EXPORT std::ostream& operator<<(std::ostream& s, const vnl_scalar_join_iterator_indexed_pair<T >& p);\

#include <cassert>
#include <vnl/vnl_matrix.h>

// Helper class to hold the sorted arrays of indices.

template <class T>
bool vnl_scalar_join_iterator_indexed_pair<T>::operator ==
    (const vnl_scalar_join_iterator_indexed_pair<T>& that) const
{
  return (*that.object) == (*object);
}

template <class T>
bool vnl_scalar_join_iterator_indexed_pair<T>::operator <
    (const vnl_scalar_join_iterator_indexed_pair<T>& that) const
{
  return (*object) < (*that.object);
}

template <class T>
std::ostream& operator<<(std::ostream& s,
                        const vnl_scalar_join_iterator_indexed_pair<T>& p)
{
  return s << p.original_index << ' ' << *(p.object) << '\n';
}

template <class T>
vnl_scalar_join_iterator<T>::vnl_scalar_join_iterator
    (const vnl_matrix<T>& relation1, unsigned column1,
     const vnl_matrix<T>& relation2, unsigned column2):
  n1(relation1.rows()),
  n2(relation2.rows()),
  pI1(new std::list<vnl_scalar_join_iterator_indexed_pair<T > >(n1)),
  pI2(new std::list<vnl_scalar_join_iterator_indexed_pair<T > >(n2)),
  I1(*pI1),
  I2(*pI2)
{
  // Sort on appropriate columns
  {
    for (unsigned i = 0; i < n1; ++i)
      I1.push_back(vnl_scalar_join_iterator_indexed_pair<T>(&relation1(i, column1), i));
    I1.sort();
  }
  {
    for (unsigned i = 0; i < n2; ++i)
      I2.push_back(vnl_scalar_join_iterator_indexed_pair<T>(&relation2(i, column2), i));
    I2.sort();
  }

  // Initialize for iteration
  index1 = I1.begin();
  index2 = I2.begin();

  // Loop to first
  for (;;) {
    T star1 = *(*index1).object;
    T star2 = *(*index2).object;
    if (star1 == star2)
      return;

    if (star1 > star2)
      ++index2;
    else
      ++index1;
  }
}

//: Destructor
template <class T>
vnl_scalar_join_iterator<T>::~vnl_scalar_join_iterator()
{
  delete pI1;
  delete pI2;
}

template <class T>
bool vnl_scalar_join_iterator<T>::done() const
{
  return (index1 == I1.end()) || (index2 == I2.end());
}

//: Increment the iterator to point to the next pair of rows.
template <class T>
void vnl_scalar_join_iterator<T>::next()
{
  T obj1 = *(*index1).object;
  // increment i2, check if still valid/same
  if (++index2 == I2.end()) return;

  T nextobj2 = *(*index2).object;
  if (obj1 == nextobj2)
    return; // Found another match

  // nextobj2 must not be < obj1
  assert(!(nextobj2 < obj1));

  // So, objects are different (in fact, obj1 > obj2 right now), lockstep until
  // they match or we're done.
  while (!done()) {
    T obj1 = *(*index1).object;
    T obj2 = *(*index2).object;

    if (obj1 == obj2) {
      // If they're equal, hack back along obj2's array to find the start of the
      // stretch of equal ones.  This allows join
      //      1 3     3 5
      //      2 3     3 6
      // to return the kronecker product of the sets by iteration.
      // No that's going to be a hack.  Will be fixed RSN.
      return;
    }

    if (obj1 > obj2)
      ++index2;
    else
      ++index1;
  }
}

template <class T>
unsigned vnl_scalar_join_iterator<T>::row1() const
{
  return (*index1).original_index;
}

template <class T>
unsigned vnl_scalar_join_iterator<T>::row2() const
{
  return (*index2).original_index;
}

//: Postfix ++ should not be used. Only present for instantiation purposes.
template <class T>
vnl_scalar_join_iterator<T> vnl_scalar_join_iterator<T>::operator++(int)
{
  std::cerr << "This should not happen! postfix ++ called\n";
  return *this;
}

#endif // vnl_scalar_join_iterator_hxx_
