//
//  Sequence.h
//
//  Qdos - Sequence template class
//
//  David Burgess
//  September 1999
//

#ifndef QSEQUENCE_H
#define QSEQUENCE_H

#include <iomanip>
#include <iostream>
#include <vector>
#include <algorithm>

#include "Exception.h"

namespace QDOS {

class SequenceException : public Exception {
public: 
  SequenceException( ExceptionMsg m = "" )
    : Exception( m << "[SequenceException]" ){;} };
class SeqNotSameSize : public SequenceException {
public:
  SeqNotSameSize( ExceptionMsg m  = "")
    : SequenceException( m << " [SeqNotSameSize]" ){;} };
class SubSeqBadArgs : public SequenceException {
public:
  SubSeqBadArgs( ExceptionMsg m = "" )
    : SequenceException( m << " [SubSeqBadArgs]" ){;} };
class BadSeqSize : public SequenceException {
public:
  BadSeqSize( ExceptionMsg m = "" )
    : SequenceException( m << " [BadSeqSize]" ){;} };
class SeqBadRangeArgs : public SequenceException {
public:
  SeqBadRangeArgs( ExceptionMsg m ="" )
    : SequenceException( m << " [SeqBadRangeArgs]" ){;} };
class SeqNoElements : public SequenceException {
public:
  SeqNoElements( ExceptionMsg m = "" )
    : SequenceException( m << " [SeqNoElements]" ){;} };
class SeqBadIndex : public SequenceException {
public:
  SeqBadIndex( ExceptionMsg m = "" )
    : SequenceException( m << " [SeqBadIndex]" ){;} };


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ class SequenceMask

class SequenceMask: public vector<bool>
{
public:
  SequenceMask( int num=0 ) : vector<bool>(num) {;}
  SequenceMask( int num, const bool& val ) : vector<bool>(num,val) {;}
  SequenceMask( const vector<bool>& seq ) : vector<bool>(seq) {;}

// assignment functions
  SequenceMask& assign( const bool& val )
   { for(register int i=0; i<size(); i++) (*this)[i] = val;  return *this;    }
  SequenceMask& assign( const vector<bool>& seq )
   { //clear(); 
     resize(seq.size());
     for(register int i=0; i<size(); i++) (*this)[i] = seq[i];
     return *this; }

// assignment operators
  SequenceMask& operator=( const vector<bool>& seq ){ return assign( seq ); }
  SequenceMask& operator=( const bool& val ){ return assign( val ); }

// inequality operators
  bool operator==( const vector<bool>& a )
  { if( size()!=a.size() ) return false;
    for(register int i=0;i<size();i++) if( (*this)[i] != a[i] ) return false;
    return true; }
  bool operator!=( const vector<bool>& a )
  { return !( *this==a ); }
  SequenceMask& operator&=(const vector<bool>& a)
  { for(register int i=0;i<size();i++) (*this)[i]=(*this)[i] && a[i%a.size()]; }
  SequenceMask& operator|=(const vector<bool>& a)
  { for(register int i=0;i<size();i++) (*this)[i]=(*this)[i] || a[i%a.size()]; }

  int num_true(void) const
  { int n=0; for(register int i=0;i<size();i++) if((*this)[i])n++; return n;}
  int num_false(void) const
  { return size()-num_true(); }
  
  void diag_print(void) const
    { cout << "SequenceMask size: " << size()
           << " num_true: " << num_true()
           << " num_false: " << num_false() << endl;
      for(register int i=0; i<size(); i++)
      cout << "[" << i << "]: <<" << (*this)[i] << ">>" << endl; }
};

inline SequenceMask operator!( const SequenceMask& a )
{ SequenceMask m = a;
  for(register int i=0;i<m.size();i++) m[i] = !(m[i]);
  return m;
}

inline SequenceMask operator&&( const SequenceMask& a, const SequenceMask& b )
{ SequenceMask m(a); return m &= b; }
  
inline SequenceMask operator||( const SequenceMask& a, const SequenceMask& b )
{ SequenceMask m(a); return m |= b; }

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Sequence

// Sequence<T> is a vector<T> with additional operations
// such as subsequencing, addition of scalar T to all elements
// etc.
// class T must properly support the copy constructor and assignment
//  operator
// For arithmetic operations T must support the corresponding operations
// eg for Seqence<T>::operator+=( const T& ) to work properly,
// then class T must have operator+= defined.

template <class T> class Sequence : public vector<T>
{
public:

// constructors
  Sequence( int n=0, const T& val = T() ) : vector<T>(n,val) {;}
  Sequence( const vector<T>& vec ) : vector<T>(vec) {;}
//  ~Sequence(){cout<<"Sequence<T> destructor"<<endl;}

// element access with index checking
  T& operator[](int i)
  { if( i<0 || i>=this->size() )
      throw SeqBadIndex( ExceptionMsg()<<"Sequence index: "<<i<<" size: "<<this->size() );
    return vector<T>::operator[](i); }
  const T& operator[](int i) const
  { if( i<0 || i>=this->size() )
      throw SeqBadIndex( ExceptionMsg()<<"Sequence index: "<<i<<" size: "<<this->size() );
    return vector<T>::operator[](i); }
// element access without index checking
  T& elt(int i)
  { return vector<T>::operator[](i); }
  const T& elt(int i) const
  { return vector<T>::operator[](i); }

// assignment operators
  Sequence& operator=( const vector<T>& seq )  { return assign( seq ); }
  Sequence& operator=( const T& v )  { return assign( v ); }
// assign functions
  Sequence& assign( const T& val )
  { for(register int i=0; i<this->size(); i++) elt(i) = val;
    return *this;
  }
  Sequence& assign( int n, const T& val )
  { //clear(); 
     this->resize( n ); for(register int i=0; i<n; i++) elt(i) = val;
     return *this;
  }

// assign from vector of any class that has converion R to T defined
  template <class R> Sequence& assign( const vector<R>& vec )
   { //clear();
     resize( vec.size() );
     for(register int i=0; i<this->size(); i++) (*this)[i] = vec[i];
     return *this; }
// assign from value of any class that has converion R to T defined
  template <class R> Sequence& assign( int n, const R& val )
   {// clear();
    this->resize( n ); for(register int i=0; i<n; i++) (*this)[i] = val;
     return *this; }

// computed assignment operator for += (ONLY)

  Sequence& operator+=( const T& val )
    { for(register int i=0; i<this->size(); i++) elt(i) += val; return *this; }

  Sequence& operator+=( const double val )
    { for(register int i=0; i<this->size(); i++) elt(i) += val; return *this; }

// new subsequence function - AJA - about 30% faster for large sequences

  void get_subsequence( Sequence& seq,int start, int num ) const
    { 
      seq.clear();
      if( start<0 || num<-1 ) throw SeqBadRangeArgs();
      //Sequence* seq = new Sequence;
      if( this->size()==0 || start>=this->size() || num==0 ) return;
      if( num == -1 ) num=this->size(); // -1 means all elements to end
      int end = start+num-1;
      if( end >= this->size() ) end=this->size()-1; // limit to available elements
	  seq.assign(num, elt(start)); // initialise sequence to the right length with values
      for(register int i=start+1; i<=end; i++ ) seq[i-start] = elt(i) ;
    }
   
    
  void get_subsequence( Sequence& seq, const vector<bool>& mask) const
  { //Sequence* seq = new Sequence;
    seq.clear();
    if( mask.size()==0 || this->size()==0 ) return;
	
	// count size of sub_sequence (avoiding push_back)	
	int count = 0;
    for(register int i=0; i<this->size(); i++ ) if( mask[ i%mask.size() ] ) count++;
	
    seq.assign(count, elt(0)); // create sequence with holding values
	count = 0;
    for(register int i=0; i<this->size(); i++ )
    {
		if( mask[ i%mask.size() ] ) 
		{
			seq[count] = elt(i) ;
			count++;
		}
    }
  }

  Sequence& erase( const vector<bool>& mask )
  { 
    vector<T>& v = *this;
    // find first erase marker
    vector<bool>::const_iterator itr=find(mask.begin(),mask.end(),true);   
    // if none found return sequence unchanged
    if (itr==mask.end())
        return *this;
    // otherwise get offset in mask
    int start_ind=0;
    start_ind+=distance(mask.begin(),itr);
    // if first erase marker is after size of current sequence (i.e. mask
    // has more elts) then return current sequence unmarked
    if(start_ind>=this->size())
        return *this;
    for(register int i=this->size()-1; i>=start_ind;  )
    {
        int j;
        if(mask[i%mask.size()])
        { 
            // erase blocks to avoid unecessary copying
            j=i;
            while(j>=start_ind && mask[j%mask.size()]) j--;
            v.erase(v.begin()+j+1,v.begin()+i+1);
            i=j-1;
        }
        else i--;
    }         
   return *this; 
  }
  
// inequality nested functor classes for generating Sequence masks
  class InRange
  { T a;
    T b;
    public:
    InRange( const T& aa, const T& bb ): a(aa), b(bb) {;}
    bool operator()( const Sequence& s, int i ) const
      { return s[i]>= a && s[i] <= b; }   };
  class LT
  { T a;
    public:
    LT( const T& aa ): a(aa) {;}
    bool operator()( const Sequence& s, int i ) const  { return s[i] < a; }  };
  class LE
  { T a;
    public:
    LE( const T& aa ): a(aa) {;}
    bool operator()( const Sequence& s, int i ) const  { return s[i] <= a; }  };
  class GT
  { T a;
    public:
    GT( const T& aa ): a(aa) {;}
    bool operator()( const Sequence& s, int i ) const  { return s[i] > a; } };
  class GE
  { T a;
    public:
    GE( const T& aa ): a(aa) {;}
    bool operator()( const Sequence& s, int i ) const  { return s[i] >= a; } };
  class EQ
  { T a;
    public:
    EQ( const T& aa ): a(aa) {;}
    bool operator()( const Sequence& s, int i ) const  { return s[i] == a; } };
  class NEQ
  { T a;
    public:
    NEQ( const T& aa ): a(aa) {;}
    bool operator()( const Sequence& s, int i ) const  { return s[i] != a; } };
  
 template <class _SelectFunction> SequenceMask get_mask( _SelectFunction f ) const
  { SequenceMask m(this->size());
    for(register int i=0; i<this->size(); i++ ) m[i]= f( *this, i );
    return m; }

  void diag_print(void) const
    { cout << "Sequence<T> size: " << this->size() << endl;
      for(register int i=0; i<this->size(); i++)
      cout << "[" << i << "]: <<" << elt(i) << ">>" << endl; }
    
};  // end class Sequence

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

template<class T> ostream& operator<< (ostream& os,const Sequence<T>& seq)
{
   int wid=os.width();
   for(register int i=0;i<seq.size();i++)
       os << setw(wid) << seq[i] << endl;
   return os;   
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

template <class T> class NumericSequence : public Sequence<T>
{
public:

// constructors
  NumericSequence( int n=0, const T& val =T() ) : Sequence<T>(n,val) {;}
  NumericSequence( const vector<T>& vec ) : Sequence<T>(vec) {;}

// --- computed assignments: template member functions

  template <class R> NumericSequence& add_assign( const R& v )
  { try{
    for(register int i=0;i<this->size();i++) this->elt(i) += v; }
    catch( Exception& e )
    { e.msg()<<"[NumericSequence<T>::add_assign(R)]"; throw e; }
    return *this; }
  template <class R> NumericSequence& add_assign( const vector<R>& v )
  { try{
    if( v.size()!=this->size() )throw SeqNotSameSize();
    for(register int i=0;i<this->size();i++) this->elt(i) += v[i]; }
    catch( Exception& e )
    { e.msg()<<"[NumericSequence<T>::add_assign(vector<R>)]"; throw e; }
    return *this; }

  template <class R> NumericSequence& sub_assign( const R& v )
  { try{
    for(register int i=0;i<this->size();i++) this->elt(i) -= v; }
    catch( Exception& e )
    { e.msg()<<"[NumericSequence<T>::sub_assign(R)]"; throw e; }
    return *this; }
  template <class R> NumericSequence& sub_assign( const vector<R>& v )
  { try{
    if( v.size()!=this->size() )throw SeqNotSameSize();
    for(register int i=0;i<this->size();i++) this->elt(i) -= v[i]; }
    catch( Exception& e )
    { e.msg()<<"[NumericSequence<T>::sub_assign(vector<R>)]"; throw e; }
    return *this; }
  template <class R> NumericSequence& mul_assign( const R& v )
  { try{
    for(register int i=0;i<this->size();i++) this->elt(i) *= v; }
    catch( Exception& e )
    { e.msg()<<"[NumericSequence<T>::mul_assign(R)]"; throw e; }
    return *this; }
  template <class R> NumericSequence& mul_assign( const vector<R>& v )
  { try{
    if( v.size()!=this->size() )throw SeqNotSameSize();
    for(register int i=0;i<this->size();i++) this->elt(i) *= v[i]; }
    catch( Exception& e )
    { e.msg()<<"[NumericSequence<T>::mul_assign(vector<R>)]"; throw e; }
    return *this; }
  template <class R> NumericSequence& div_assign( const R& v )
  { try{
    for(register int i=0;i<this->size();i++) this->elt(i) /= v; }
    catch( Exception& e )
    { e.msg()<<"[NumericSequence<T>::div_assign(R)]"; throw e; }
    return *this; }
  template <class R> NumericSequence& div_assign( const vector<R>& v )
  { try{
    if( v.size()!=this->size() )throw SeqNotSameSize();
    for(register int i=0;i<this->size();i++) this->elt(i) /= v[i]; }
    catch( Exception& e )
    { e.msg()<<"[NumericSequence<T>::div_assign(vector<R>)]"; throw e; }
    return *this; }

// computed assignment operators
  NumericSequence& operator+=(const T& v ) { add_assign( v ); }
  NumericSequence& operator+=( const vector<T>& v ) { add_assign( v ); }
  NumericSequence& operator-=(const T& v ) { sub_assign( v ); }
  NumericSequence& operator-=( const vector<T>& v ) { sub_assign( v ); }
  NumericSequence& operator*=(const T& v ) { mul_assign( v ); }
  NumericSequence& operator*=( const vector<T>& v ) { mul_assign( v ); }
  NumericSequence& operator/=(const T& v ) { div_assign( v ); }
  NumericSequence& operator/=( const vector<T>& v ) { div_assign( v ); }

// assign regular set of values
  NumericSequence& assign_regular( int num, const T& s, const T& step )
  { if(num<0)throw BadSeqSize();
     //clear(); 
     this->resize(num);
     (*this)[0]=s;
     for(register int i=1; i<num; i++) 
         (*this)[i] = (*this)[i-1] + step;
     return *this;
   }
   
  NumericSequence& assign_regular( int num, const T& s, double step )
  { if(num<0)throw BadSeqSize();
     //clear(); 
     this->resize(num);
     (*this)[0]=s;
     for(register int i=1; i<num; i++) 
         (*this)[i] = (*this)[i-1] + step;
     return *this;
   }
// monotonicity test functions
  bool is_monotonic_increasing() const
  { if( this->size()<2 )return false;
    for(register int i=1;i<this->size();i++) if( this->elt(i) < this->elt(i-1) ) return false;
    return true;
  }
  bool is_strictly_monotonic_increasing() const
  { if( this->size()<2 )return false;
    for(register int i=1;i<this->size();i++) if( this->elt(i) <= this->elt(i-1) ) return false;
    return true;
  }
  bool is_monotonic_decreasing()
  { if( this->size()<2 )return false;
    for(register int i=1;i<this->size();i++) if( this->elt(i) > this->elt(i-1) ) return false;
    return true; }
  bool is_strictly_monotonic_decreasing()
  { if( this->size()<2 )return false;
    for(register int i=1;i<this->size();i++) if( this->elt(i) >= this->elt(i-1) ) return false;
    return true;
  }

// minimum and maximum functions
  void prepare_range_indices( int start, int num, int& end ) const
    { if( start<0 || num<-1 ) throw SeqBadRangeArgs();
      if( num == -1 ) num=this->size(); // -1 means all elements to end
      end = start+num;
      if( end >= this->size() ) end=this->size()-1; // limit to available elements
    }
  T min_value( int start=0, int num=-1 ) const
    { if( this->size()==0 || start>=this->size() || num==0 ) throw SeqNoElements();
      int end;
      prepare_range_indices( start, num, end );
      if( end-start+1 == 1 ) return (*this)[start];  // only one element 
      T x = (*this)[start];
      for(register int i=start+1; i<=end; i++ )
        if( (*this)[i] < x ) x=(*this)[i];
      return x;
    }
  int min_value_index( int start=0, int num=-1 ) const
    { if( this->size()==0 || start>=this->size() || num==0 ) throw SeqNoElements();
      int end;
      prepare_range_indices( start, num, end );
      if( end-start+1 == 1 ) return start;  // only one element 
      int idx = start;
      for(register int i=start+1; i<=end; i++ )
        if( (*this)[i] < (*this)[idx] ) idx = i;
      return idx;
    }
  T max_value( int start=0, int num=-1 ) const
    { if( this->size()==0 || start>=this->size() || num==0 ) throw SeqNoElements();
      int end;
      prepare_range_indices( start, num, end );
      if( end-start+1 == 1 ) return (*this)[start];  // only one element 
      T x = (*this)[start];
      for(register int i=start+1; i<=end; i++ )
        if( (*this)[i] > x ) x=(*this)[i];
      return x;
    }
  int max_value_index( int start=0, int num=-1 ) const
    { if( this->size()==0 || start>=this->size() || num==0 ) throw SeqNoElements();
      int end;
      prepare_range_indices( start, num, end );
      if( end-start+1 == 1 ) return start;  // only one element 
      int idx = start;
      for(register int i=start+1; i<=end; i++ )
        if( (*this)[i] > (*this)[idx] ) idx = i;
      return idx;
    }
  T min_value( const vector<bool>& mask )
    { int first_true=-1;
      for(register int i=0; i<mask.size(); i++ ) if(mask[i]){ first_true=i; break;}
      if( first_true == -1 ) throw SeqNoElements();
      T x = (*this)[first_true];
      for(register int i=0; i<this->size(); i++ )
        if( mask[ i%mask.size() ] ) if( (*this)[i] < x ) x=(*this)[i];
      return x;
    }
  int min_value_index( const vector<bool>& mask )
    { int first_true=-1;
      for(register int i=0; i<mask.size(); i++ ) if(mask[i]){ first_true=i; break;}
      if( first_true == -1 ) throw SeqNoElements();
      int idx = first_true;
      for(register int i=0; i<this->size(); i++ )
        if( mask[ i%mask.size() ] ) if( (*this)[i] < (*this)[idx] ) idx = i;
      return idx;
    }
  T max_value( const vector<bool>& mask )
    { int first_true=-1;
      for(register int i=0; i<mask.size(); i++ ) if(mask[i]){ first_true=i; break;}
      if( first_true == -1 ) throw SeqNoElements();
      T x = (*this)[first_true];
      for(register int i=0; i<this->size(); i++ )
        if( mask[ i%mask.size() ] ) if( (*this)[i] > x ) x=(*this)[i];
      return x;
    }
  int max_value_index( const vector<bool>& mask )
    { int first_true=-1;
      for(register int i=0; i<mask.size(); i++ ) if(mask[i]){ first_true=i; break;}
      if( first_true == -1 ) throw SeqNoElements();
      int idx = first_true;
      for(register int i=0; i<this->size(); i++ )
        if( mask[ i%mask.size() ] ) if( (*this)[i] > (*this)[idx] ) idx = i;
      return idx;
    }

  void diag_print(void) const
    { cout << "NumericSequence<T> size: " << this->size() << endl;
      Sequence<T>::diag_print(); }

}; // end NumericSequence

} // end namespace QDOS


#endif // #ifndef QSEQUENCE_H
