/*
 *  multirange.h
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef MULTIRANGE_H
#define MULTIRANGE_H

#include <vector>
#include <utility>
#include "nest.h"

namespace nest {

/**
 * Class for sequences of ranges acting like a compressed vector.
 */
  class Multirange
  {
  public:
    typedef std::pair<index,index> Range;
    typedef std::vector<Range> RangeVector;

    class iterator {
    public:
      iterator(RangeVector::const_iterator iter, index n);
      index operator*() const;
      bool operator!=(const iterator& other) const;
      iterator& operator++();
      iterator operator++(int);

    private:
      RangeVector::const_iterator pair_iter_;
      index n_;
    };

    Multirange();
    void push_back(index x);
    void clear();
    index operator[](index n) const;
    index size() const;
    bool empty() const;
    iterator begin() const;
    iterator end() const;

  private:
    RangeVector ranges_;
    index size_;
  };

  inline
  Multirange::Multirange():
    ranges_(), size_(0)
  {
  }

  inline
  void Multirange::push_back(index x)
  {
    if ((not ranges_.empty()) && (ranges_.back().second+1 == x)) {
      ++ranges_.back().second;
    } else {
      ranges_.push_back(Range(x,x));
    }
    ++size_;
  }

  inline
  void Multirange::clear()
  {
    ranges_.clear();
    size_ = 0;
  }

  inline
  index Multirange::size() const
  {
    return size_;
  }

  inline
  bool Multirange::empty() const
  {
    return size_ == 0;
  }

  inline
  Multirange::iterator::iterator(RangeVector::const_iterator iter, index n):
    pair_iter_(iter), n_(n)
  {
  }

  inline
  bool Multirange::iterator::operator!=(const iterator& other) const
  {
    return (other.pair_iter_ != pair_iter_) || (other.n_ != n_);
  }

  inline
  index Multirange::iterator::operator*() const
  {
    return pair_iter_->first + n_;
  }

  inline
  Multirange::iterator& Multirange::iterator::operator++()
  {
    ++n_;
    if (n_ > pair_iter_->second-pair_iter_->first) {
      ++pair_iter_;
      n_ = 0;
    }
    return *this;
  }

  inline
  Multirange::iterator Multirange::iterator::operator++(int)
  {
    iterator tmp=*this;
    ++(*this);
    return tmp;
  }

  inline
  Multirange::iterator Multirange::begin() const
  {
    return Multirange::iterator(ranges_.begin(),0);
  }

  inline
  Multirange::iterator Multirange::end() const
  {
    return Multirange::iterator(ranges_.end(),0);
  }


} // namespace

#endif