/*FVECTOR_____________________________________________________________________
Function Object vector class
by Martin Robinson
____________________philosophy
original data is never to be modified
method only intended to be getter function
new object will reinterpret old one by simulating modification
(hello) + (world) != (helloWorld)
hello == hello
world(hello) = helloworld
(hello) stay intact,
(world) keep hello as ref and add world to the string when asked
http://en.wikipedia.org/wiki/Functional_programming
______________________________________________________________________________
PREPROCESSOR________________________________________________________________*/

#pragma once
#ifndef _FVECTOR
#define _FVECTOR

template <typename T>
const T& dummy()
{
    static T _dummy = 0;
    return _dummy;
}

/*BASE________________________________________________________________________
____________________________________________________________________________*/

template <typename T>
class baseFvector{
public:
    virtual const T& at(const int)const;          //virtual return character AT index
    virtual int size()const;                    //virtual return size of the array
    virtual const baseFvector * undo()const;             //virtual return previous version 
                                                 //of the string
    //non virtual function that rely on virtual function

};

template <typename T>
const T& baseFvector<T>::at(const int index)const
{
    return dummy<T>();
};

template <typename T>
int baseFvector<T>::size()const
{
    return 0;
}

template <typename T>
const baseFvector<T> * baseFvector<T>::undo()const
{
    return 0;
}

/*DECLARE_VECTOR______________________________________________________________
____________________________________________________________________________*/

template <typename T>
class nubaseFvector : public baseFvector<T>{
    T _data;
    int _size;
    void init(int,const T&);
public:
    nubaseFvector(int);
    nubaseFvector(int,const T&);
    const T& at(const int)const;
    int size()const;
};

template <typename T>
void nubaseFvector<T>::init(int size, const T& data)
{
    _size = size;
    _data = data;
}

template <typename T>
nubaseFvector<T>::nubaseFvector(int size)
{
    init(size,0);
}

template <typename T>
nubaseFvector<T>::nubaseFvector(int size, const T& data)
{
    init(size,data);
}

template <typename T>
const T& nubaseFvector<T>::at(const int index)const
{
    if (index<0 || index >= _size)
        return dummy<T>();
    return _data;
}

template <typename T>
int nubaseFvector<T>::size()const
{
    return _size;
}

/*REFERENCE_TO_AN_ARRAY_______________________________________________________
____________________________________________________________________________*/

template <typename T>
class refbaseFvector : public baseFvector<T>{
    const T * _arr;                          //string pointer
    int _size;                                  //size of the string
    void init(const T*, int);                     //initialise
public:
    refbaseFvector(const T*, int);                       //constructor
    const T& at(const int)const;                  //return char at index
    int size()const;                            //return size
};

template <typename T>
void refbaseFvector<T>::init(const T* arr, int size)
{
    _arr = arr;
    _size = size;
}

template <typename T>
refbaseFvector<T>::refbaseFvector(const T* arr, int size)
{
    init(arr,size);
}

template <typename T>
const T& refbaseFvector<T>::at(const int index)const
{
    if (index < 0 || index >= size())
        return dummy<T>();
    return *(_arr+index);
}

template <typename T>
int refbaseFvector<T>::size()const
{
    return _size;
}


/*RESIZE______________________________________________________________________
____________________________________________________________________________*/

template <typename T>
class resizebaseFvector : public baseFvector<T>{
    const baseFvector<T> * _undo;
    int _size;
    T _data;
    void init(const baseFvector<T> *,int, T);
public:
    resizebaseFvector(const baseFvector<T> *,int, T);
    resizebaseFvector(const baseFvector<T> *,int);
    resizebaseFvector(const baseFvector<T> &,int, T);
    resizebaseFvector(const baseFvector<T> &,int);
    const T & at(const int)const;
    int size()const;
    const baseFvector<T> * undo()const;
};

template <typename T>
void resizebaseFvector<T>::init(const baseFvector<T> * undo, int nuSize, T data)
{
    _undo = undo;
    _size = nuSize;
    _data = data;
}

template <typename T>
resizebaseFvector<T>::resizebaseFvector(const baseFvector<T> * undo, int nuSize, T data)
{
    init(undo,nuSize, data);
}

template <typename T>
resizebaseFvector<T>::resizebaseFvector(const baseFvector<T> * undo, int nuSize)
{
    init(undo,nuSize, 0);
}

template <typename T>
resizebaseFvector<T>::resizebaseFvector(const baseFvector<T> & undo, int nuSize, T data)
{
    init(&undo,nuSize, data);
}

template <typename T>
resizebaseFvector<T>::resizebaseFvector(const baseFvector<T> & undo, int nuSize)
{
    init(&undo,nuSize, 0);
}

template <typename T>
const T & resizebaseFvector<T>::at(const int index)const
{
    if (index >= 0)
    {
        if (index < _undo->size() && index < _size)
            return _undo->at(index);
        else if (index < _size)
            return _data;
    }
    return dummy<T>();
}

template <typename T>
int resizebaseFvector<T>::size()const
{
    return _size;
}

template <typename T>
const baseFvector<T> * resizebaseFvector<T>::undo()const
{
    return _undo;
}

/*INSERT______________________________________________________________________
____________________________________________________________________________*/

template <typename T>
class insertRefbaseFvector : public baseFvector<T>{
    const baseFvector<T> * _undo;
    const T * _arr;
    int _size,
        _from,
        _to;
    void init(const baseFvector<T> * ,const T *, int , int , int );
public:
    insertRefbaseFvector(const baseFvector<T> * ,const T *, int , int , int );
    insertRefbaseFvector(const baseFvector<T> * ,const T *, int , int);
    insertRefbaseFvector(const baseFvector<T> & ,const T *, int , int , int );
    insertRefbaseFvector(const baseFvector<T> & ,const T *, int , int);
 
    const T& at(const int)const;
    int size()const;
    const baseFvector<T> * undo()const;
};

template <typename T>
void insertRefbaseFvector<T>::init(const baseFvector<T> * undo, const T * arr, int size, int from, int to)
{
    _undo = undo;
    _arr = arr;
    _size = size;
    _from = from;
    _to = to;
}

template <typename T>
insertRefbaseFvector<T>::insertRefbaseFvector(const baseFvector<T> * undo, const T * arr, int size, int from, int to)
{
    init(undo, arr, size, from, to); 
}

template <typename T>
insertRefbaseFvector<T>::insertRefbaseFvector(const baseFvector<T> * undo, const T * arr, int size, int pos)
{
    init(undo, arr, size, pos, pos); 
}

template <typename T>
insertRefbaseFvector<T>::insertRefbaseFvector(const baseFvector<T> & undo, const T * arr, int size, int from, int to)
{
    init(&undo, arr, size, from, to); 
}

template <typename T>
insertRefbaseFvector<T>::insertRefbaseFvector(const baseFvector<T> & undo, const T * arr, int size, int pos)
{
    init(&undo, arr, size, pos, pos); 
}

template <typename T>
const T& insertRefbaseFvector<T>::at(const int index)const
{
    if (index < 0)
        return dummy<T>();
    if (index < _from)
        return _undo->at(index);
    if (index < _from+_size)
        return *(_arr + index-_from);
    if (index < _undo->size() - _to + _from + _size)
        return _undo->at(index - _size - _from + _to);
    return dummy<T>();
}

template <typename T>
int insertRefbaseFvector<T>::size()const
{
    return _size + _from - _to + _undo->size();
}

template <typename T>
const baseFvector<T> * insertRefbaseFvector<T>::undo()const
{
    return _undo;
}

/*TRIM________________________________________________________________________
____________________________________________________________________________*/

template <typename T>
class trimbaseFvector : public baseFvector<T>{
    const baseFvector<T> * _undo;
    int _from,
        _to;
    void init(const baseFvector<T> *, int , int );
public:
    trimbaseFvector(const baseFvector<T> *, int , int );
    trimbaseFvector(const baseFvector<T> &, int , int );
    const T& at(const int)const;
    int size()const;
    const baseFvector<T> * undo()const;
};

template <typename T>
void trimbaseFvector<T>::init(const baseFvector<T> * undo, int from, int to)
{
    _undo = undo;
    _from = from;
    _to = to;
}

template <typename T>
trimbaseFvector<T>::trimbaseFvector(const baseFvector<T> * undo, int from, int to)
{
    init(undo, from, to); 
}

template <typename T>
trimbaseFvector<T>::trimbaseFvector(const baseFvector<T> & undo, int from, int to)
{
    init(&undo, from, to); 
}

template <typename T>
const T& trimbaseFvector<T>::at(const int index)const
{
    if (index < 0)
        return dummy<T>();
    if (index < _from)
        return _undo->at(index);
    if (index < size())
        return _undo->at(index + _to - _from);
    return dummy<T>();
}

template <typename T>
int trimbaseFvector<T>::size()const
{
    return _undo->size() - _to + _from;
}

template <typename T>
const baseFvector<T> * trimbaseFvector<T>::undo()const
{
    return _undo;
}

/*SUB_________________________________________________________________________
____________________________________________________________________________*/

template <typename T>
class subbaseFvector : public baseFvector<T>{
    const baseFvector<T> * _undo;
    int _from,
        _to;
    void init(const baseFvector<T> *, int , int );
public:
    subbaseFvector(const baseFvector<T> *, int , int );
    subbaseFvector(const baseFvector<T> &, int , int );
    const T& at(const int)const;
    int size()const;
    const baseFvector<T> * undo()const;
};

template <typename T>
void subbaseFvector<T>::init(const baseFvector<T> * undo, int from, int to)
{
    _undo = undo;
    _from = from;
    _to = to;
}

template <typename T>
subbaseFvector<T>::subbaseFvector(const baseFvector<T> * undo, int from, int to)
{
    init(undo, from, to); 
}

template <typename T>
subbaseFvector<T>::subbaseFvector(const baseFvector<T> & undo, int from, int to)
{
    init(&undo, from, to); 
}

template <typename T>
const T& subbaseFvector<T>::at(const int index)const
{
    if (index < 0)
        return dummy<T>();
    if (index < size())
        return _undo->at(index + _from);
    return dummy<T>();
}

template <typename T>
int subbaseFvector<T>::size()const
{
    return _to - _from;
}

template <typename T>
const baseFvector<T> * subbaseFvector<T>::undo()const
{
    return _undo;
}

/*____________________________________________________________________________
____________________________________________________________________________*/

template <typename T>
struct fvector{
    typedef baseFvector<T> base;
    typedef nubaseFvector<T> nu;
    typedef refbaseFvector<T> ref;
    typedef resizebaseFvector<T> resize;
    typedef insertRefbaseFvector<T> insert;
    typedef trimbaseFvector<T> trim;
    typedef subbaseFvector<T> sub;
};

/*____________________________________________________________________________
____________________________________________________________________________*/
#endif
