#include <string>
#include <iostream>
#include <iomanip>
#include <cmath>

#include "JTools/JMultiMap.hh"
#include "JTools/JMap.hh"

#include "JLang/JManip.hh"

#include "Jeep/JParser.hh"
#include "Jeep/JMessage.hh"

namespace {

  using namespace JPP;

  /**
   * Test function.
   *
   * \param  x           x
   * \param  y           y
   * \param  z           z
   * \return             value
   */
  inline double f1(const double x,
		   const double y,
		   const double z)
  {
    return x*100 + y*10 + z;
  }

  /**
   * Test function.
   *
   * \param  key         key
   * \return             value
   */
  inline double f1(const JMultiKey<3, const double>& key)
  {
    return f1(key.first, key.second.first, key.second.second.first);
  }
}


/**
 * \file
 *
 * Example program to test JTOOLS::JMultiMap.
 * \author mdejong
 */
int main(int argc, char **argv)
{
  using namespace std;
  using namespace JPP;

  double      precision;
  int         debug;

  try {

    JParser<> zap("Example program to test multi-dimensional map.");

    zap['e'] = make_field(precision)        = 1.0e-10;
    zap['d'] = make_field(debug)            = 3;

    zap(argc, argv);
  }
  catch(const exception &error) {
    FATAL(error.what() << endl);
  }


  const double xmin = -1.0;
  const double xmax = +1.0;
  const double nx   = 3;
   
  typedef JMAPLIST<JMap,
		   JMap,
		   JMap>::maplist                JMaplist_t;

  typedef JMultiMap<double, double, JMaplist_t>  JMultimap_t;

  JMultimap_t buffer;

  for (double x = xmin; x <= xmax; x += (xmax - xmin)/(nx - 1)) {
    for (double y = xmin; y <= xmax; y += (xmax - xmin)/(nx - 1)) {
      for (double z = xmin; z <= xmax; z += (xmax - xmin)/(nx - 1)) {
	buffer[x][y][z] = f1(x, y, z);
      }
    }
  }

  const JFormat_t format(6, 1, std::ios::fixed);

  setFormat< JMultiKey<3, const double> >(format);
  setFormat< JMultiKey<2, const double> >(format);
  setFormat< JMultiKey<1, const double> >(format);

  DEBUG("i->[second]*->(first|second)" << endl);
    
  for (JMultimap_t::super_const_iterator i = buffer.super_begin(); i != buffer.super_end(); ++i) {
    DEBUG(""
	  << format << i->first                  << ' ' 
	  << format << i->second->first          << ' ' 
	  << format << i->second->second->first  << ' ' 
	  << format << i->second->second->second << endl);

    ASSERT(fabs(f1(i->first,
		   i->second->first,
		   i->second->second->first) - i->second->second->second) <= precision, "Test iterator equality");
  }

  DEBUG("i->[second]*->(first|second)" << endl);

  for (JMultimap_t::super_const_reverse_iterator i = buffer.super_rbegin(); i != buffer.super_rend(); ++i) {
    DEBUG(""
	  << format << i->first                  << ' ' 
	  << format << i->second->first          << ' ' 
	  << format << i->second->second->first  << ' ' 
	  << format << i->second->second->second << endl);

    ASSERT(fabs(f1(i->first,
		   i->second->first,
		   i->second->second->first) - i->second->second->second) <= precision, "Test iterator equality");
  }

  DEBUG("*i.[second]*.(first|second)" << endl);

  for (JMultimap_t::super_const_reverse_iterator i = buffer.super_rbegin(); i != buffer.super_rend(); ++i) {
    DEBUG(""
	  << format << (*i).first                << ' ' 
	  << format << (*i).second.first         << ' ' 
	  << format << (*i).second.second.first  << ' ' 
	  << format << (*i).second.second.second << endl);

    ASSERT(fabs(f1(i->first,
		   i->second->first,
		   i->second->second->first) - i->second->second->second) <= precision, "Test iterator equality");
  }

  DEBUG("i.getKey() i.getValue()" << endl);

  for (JMultimap_t::super_const_iterator i = buffer.super_begin(); i != buffer.super_end(); ++i) {

    DEBUG(i.getKey() << ' ' << format << i.getValue() << endl);

    ASSERT(fabs(f1(i.getKey()) - i.getValue()) <= precision, "Test iterator equality");
  }

  return 0;
}