#include <config.h>
#include <sarray/Range.h>
#include <sarray/RangeImp.h>

#include <algorithm>
#include <stdexcept>
#include <sstream>
#include <map>

using std::map;
using std::pair;
using std::string;
using std::ostringstream;

struct ltrangeimp
{
  bool operator()(RangeImp const *arg1, RangeImp const *arg2) const
  {
    return *arg1 < *arg2;
  }
};

map<RangeImp const*, long, ltrangeimp> &impMap()
{
  static map<RangeImp const*, long, ltrangeimp> _impmap;
  return _impmap;
}

inline long &ref(RangeImp const *p) { return impMap().find(p)->second; }

static RangeImp const *getPtr(Index const &lower, Index const &upper)
{
  RangeImp * newimp = new RangeImp(lower, upper);  
  map<RangeImp const*, long, ltrangeimp> &impmap = impMap();
  map<RangeImp const*, long, ltrangeimp>::iterator p = impmap.find(newimp);
  if (p == impmap.end()) {
    impmap.insert(pair<RangeImp*,long>(newimp,0));
    return newimp;
  }
  else {
    delete newimp;
    return p->first;
  }
}

Range::Range()
  : _p(getPtr(Index(0),Index(0)))
{
    ref(_p)++;
}

Range::Range(Index const &lower, Index const &upper)
  : _p(getPtr(lower,upper))
{
  ref(_p)++;
}

Range::Range(Range const &rhs)
  : _p(rhs._p)
{
  ref(_p)++;
}

Range::Range(Index const &index)
  : _p(getPtr(index,index))
{
  ref(_p)++;
}

Range &Range::operator=(Range const &rhs)
{
  if (_p == rhs._p)
    return *this; //Nothing to do

  if (--ref(_p) == 0) {
    impMap().erase(_p);
    delete _p;
  }
  _p = rhs._p;
  ref(_p)++;
  return *this;
}

Range::~Range()
{
  if (--ref(_p) == 0) {
    impMap().erase(_p);
    delete _p;
  }
}

bool Range::operator==(Range const &range) const
{ 
  return _p == range._p; 
}

bool Range::operator!=(Range const &range) const
{ 
  return _p != range._p; 
}

unsigned long Range::length() const 
{ 
  return _p->length(); 
}

bool Range::contains(Range const &range) const 
{ 
  return _p->contains(*range._p); 
}

long Range::leftOffset(Index const &index) const 
{ 
  return _p->leftOffset(index); 
}

Index Range::leftIndex(long n) const
{
  return _p->leftIndex(n);
}

long Range::rightOffset(Index const &index) const 
{ 
  return _p->rightOffset(index); 
}

Index Range::rightIndex(long n) const
{
  return _p->rightIndex(n);
}

Index const &Range::dim(bool drop) const 
{ 
  return _p->dim(drop); 
}

unsigned int Range::ndim(bool drop) const 
{ 
  return _p->ndim(drop); 
}

Index const & Range::lower() const 
{ 
  return _p->lower(); 
}

Index const & Range::upper() const 
{ 
  return _p->upper(); 
}

bool Range::operator<(Range const &rhs) const { 
  return *_p < *rhs._p; 
}

string print(Range const &range)
{
  ostringstream ostr;
  ostr << "[";
  for (unsigned int i = 0; i < range.ndim(false); ++i) {
    if (i > 0)
      ostr << ",";
    if (range.lower()[i] == range.upper()[i]) {
      ostr << range.lower()[i];
    }
    else {
      ostr << range.lower()[i] << ":" << range.upper()[i];
    }
  }
  ostr << "]";
  return ostr.str();
}


syntax highlighted by Code2HTML, v. 0.9.1