// Copyright 2002 The Trustees of Indiana University.

// Use, modification and distribution is subject to the Boost Software 
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//  Boost.MultiArray Library
//  Authors: Ronald Garcia
//           Jeremy Siek
//           Andrew Lumsdaine
//  See http://www.boost.org/libs/multi_array for documentation.

//
// Trying to diagnose problems under visual

#include "boost/config.hpp"
#include "boost/array.hpp"
#include "boost/limits.hpp"
#include <algorithm>
#include <utility>

typedef int index;
typedef std::size_t size_type;

  template <typename Index,typename SizeType>
  class index_range {
  public:

    index_range()
    {
      start_ = from_start();
      finish_ = to_end();
      stride_ = 1;
      degenerate_ = false;
    }

    explicit index_range(Index pos)
    {
      start_ = pos;
      finish_ = pos;
      stride_ = 1;
      degenerate_ = true;
    }

    explicit index_range(Index start, Index finish, Index stride=1)
      : start_(start), finish_(finish), stride_(stride),
        degenerate_(start_ == finish_)
    { }


    // These are for chaining assignments to an index_range
    index_range& start(Index s) {
      start_ = s;
      degenerate_ = (start_ == finish_);
      return *this;
    }

    index_range& finish(Index f) {
      finish_ = f;
      degenerate_ = (start_ == finish_);
      return *this;
    }

    index_range& stride(Index s) { stride_ = s; return *this; }

    Index start() const
    { 
      return start_; 
    }

    Index get_start(Index low_index_range = 0) const
    { 
      if (start_ == from_start())
        return low_index_range;
      return start_; 
    }

    Index finish() const
    {
      return finish_;
    }

    Index get_finish(Index high_index_range = 0) const
    {
      if (finish_ == to_end())
        return high_index_range;
      return finish_;
    }

    unsigned int size(Index recommended_length = 0) const
    {
      if ((start_ == from_start()) || (finish_ == to_end()))
        return recommended_length;
      else 
        return (finish_ - start_) / stride_;
    }

    Index stride() const { return stride_; }

    bool is_ascending_contiguous() const
    {
      return (start_ < finish_) && is_unit_stride();
    }

    void set_index_range(Index start, Index finish, Index stride=1)
    {
      start_ = start;
      finish_ = finish;
      stride_ = stride;
    }

    static index_range all() 
    { return index_range(from_start(), to_end(), 1); }

    bool is_unit_stride() const
    { return stride_ == 1; }

    bool is_degenerate() const { return degenerate_; }

    index_range operator-(Index shift) const
    { 
      return index_range(start_ - shift, finish_ - shift, stride_); 
    }

    index_range operator+(Index shift) const
    { 
      return index_range(start_ + shift, finish_ + shift, stride_); 
    }

    Index operator[](unsigned i) const
    {
      return start_ + i * stride_;
    }

    Index operator()(unsigned i) const
    {
      return start_ + i * stride_;
    }

    // add conversion to std::slice?

  private:
    static Index from_start()
      { return std::numeric_limits<Index>::min(); }

    static Index to_end()
      { return std::numeric_limits<Index>::max(); }
  public:
    Index start_, finish_, stride_;
    bool degenerate_;
  };

  // Express open and closed interval end-points using the comparison
  // operators.

  // left closed
  template <typename Index, typename SizeType>
  inline index_range<Index,SizeType>
  operator<=(Index s, const index_range<Index,SizeType>& r)
  {
    return index_range<Index,SizeType>(s, r.finish(), r.stride());
  }

  // left open
  template <typename Index, typename SizeType>
  inline index_range<Index,SizeType>
  operator<(Index s, const index_range<Index,SizeType>& r)
  {
    return index_range<Index,SizeType>(s + 1, r.finish(), r.stride());
  }

  // right open
  template <typename Index, typename SizeType>
  inline index_range<Index,SizeType>
  operator<(const index_range<Index,SizeType>& r, Index f)
  {
    return index_range<Index,SizeType>(r.start(), f, r.stride());
  }

  // right closed
  template <typename Index, typename SizeType>
  inline index_range<Index,SizeType>
  operator<=(const index_range<Index,SizeType>& r, Index f)
  {
    return index_range<Index,SizeType>(r.start(), f + 1, r.stride());
  }

//
// range_list.hpp - helper to build boost::arrays for *_set types
//

/////////////////////////////////////////////////////////////////////////
// choose range list begins
//

struct choose_range_list_n {
  template <typename T, std::size_t NumRanges>
  struct bind {
    typedef boost::array<T,NumRanges> type;
  };
};

struct choose_range_list_zero {
  template <typename T, std::size_t NumRanges>
  struct bind {
    typedef boost::array<T,1> type;
  };
};


template <std::size_t NumRanges>
struct range_list_gen_helper {
  typedef choose_range_list_n choice;
};

template <>
struct range_list_gen_helper<0> {
  typedef choose_range_list_zero choice;
};

template <typename T, std::size_t NumRanges>
struct range_list_generator {
private:
  typedef typename range_list_gen_helper<NumRanges>::choice Choice;
public:
  typedef typename Choice::template bind<T,NumRanges>::type type;
};

//
// choose range list ends
/////////////////////////////////////////////////////////////////////////

//
// Index_gen.hpp stuff
//

template <int NumRanges, int NumDims>
struct index_gen {
private:
  typedef index Index;
  typedef size_type SizeType;
  typedef index_range<Index,SizeType> range;
public:
  typedef typename range_list_generator<range,NumRanges>::type range_list;
  range_list ranges_;

  index_gen() { }

  template <int ND>
  explicit index_gen(const index_gen<NumRanges-1,ND>& rhs,
            const index_range<Index,SizeType>& range)
  {
    std::copy(rhs.ranges_.begin(),rhs.ranges_.end(),ranges_.begin());
    *ranges_.rbegin() = range;
  }

  index_gen<NumRanges+1,NumDims+1>
  operator[](const index_range<Index,SizeType>& range) const
  {
    index_gen<NumRanges+1,NumDims+1> tmp;
    std::copy(ranges_.begin(),ranges_.end(),tmp.ranges_.begin());
    *tmp.ranges_.rbegin() = range;
    return tmp;
  }

  index_gen<NumRanges+1,NumDims>
  operator[](Index idx) const
  {
    index_gen<NumRanges+1,NumDims> tmp;
    std::copy(ranges_.begin(),ranges_.end(),tmp.ranges_.begin());
    *tmp.ranges_.rbegin() = index_range<Index,SizeType>(idx);
    return tmp;
  }    
};


template <int NDims, int NRanges>
void accept_gen(index_gen<NRanges,NDims>& indices) {
  // do nothing
}

template <typename X, typename Y, int A, int B>
class foo { };

class boo {

  template <int NDims, int NRanges>
  void operator[](index_gen<NRanges,NDims>& indices) {

  }
};

template <typename X, typename Y, int A1, int A2>
void take_foo(foo<X,Y,A1,A2>& f) { }

int main() {

  index_gen<0,0> indices;
  typedef index_range<index,size_type> range;

  take_foo(foo<int,std::size_t,1,2>());

  indices[range()][range()][range()];
  accept_gen(indices);
  accept_gen(index_gen<0,0>());
  accept_gen(indices[range()][range()][range()]);
  
  boo b;
  b[indices[range()][range()][range()]];

  return 0;
}
