/***************************************************************************
**
**  This file is part of QGpCoreTools.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2010-07-27
**  Copyright: 2010-2019
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef SORTEDVECTOR_H
#define SORTEDVECTOR_H

#include <QtCore>

#include "QGpCoreToolsDLLExport.h"

namespace QGpCoreTools {

template <class Key, class T>
class QGPCORETOOLS_EXPORT SortedVector : private QVector<T *>
{
public:
  SortedVector() {_n2=1;}

  bool isEmpty() const {return QVector<T *>::isEmpty();}
  int count() const {return QVector<T *>::count();}
  T * at(int index) const {return QVector<T *>::at(index);}

  void insert(T * o);
  void remove(int index);
  void clear(bool del=false);
  void reserve(int n) {QVector<T *>::reserve(n);}

  void append(T * o);
  void sort() {std::sort(QVector<T *>::begin(), QVector<T *>::end(), lessThan);}

  bool contains(const Key& k) const {return firstIndexOf(k)>-1;}
  int firstIndexOf(const Key& k) const;
  int firstIndexAfter(const Key& k) const;
  const QVector<T *>& vector() const {return *this;}
private:
  void updateN2() {while(_n2<count()) _n2=_n2 << 1;}
  static bool lessThan(const T * o1, const T * o2) {return o1->key()<o2->key();}

  int _n2;
};

//#define SORTED_VECTOR_TEST
#ifdef SORTED_VECTOR_TEST
bool testSortedVerctor();
#endif

template<class Key, class T>
int SortedVector<Key, T>::firstIndexOf(const Key& k) const
{
  /*
   For few elements, is it faster to check systematically?
                      n   integer comparisons
                      n   increments
                      n   object comparisons
                      3*n total
   With the more complex but faster technique:
                    2+l     object comparisons
                    1+2*l   integer comparisons
                    3+2*l   increments (including bit shifts)
                    With l=ceil(log2(n)), e.g. n=3 to 4   : l=2: 4  oc  5 ic   7 i total=16
                                                 5 to 8   : l=3: 5  oc  7 ic   9 i total=21
                                                 9 to 16  : l=4: 6  oc  9 ic  11 i total=26
                                                 17 to 32 : l=5: 7  oc 11 ic  13 i total=31
                                                 65 to 128: l=7: 9  oc 15 ic  17 i total=41
   The cost is lower only for n<6 to 7, according to the relative cost of comparing keys
   We assume that the majority of vectors have more than 6 or 7 elements, hence we avoid
   to check this threshold for all cases just to save minor time delays in some rare cases.
   */
  int n=count();
  int i=_n2-1;
  int step2=_n2 >> 1;
  while(step2>0) {
    if(i>=n) {
      i-=step2;
    } else {
      if(k<=at(i)->key()) {
        i-=step2;
      } else {
        i+=step2;
      }
    }
    step2=step2 >> 1;
  }
  if(i<n-1) {
    if(k==at(i)->key()) {
      return i;
    } else if(k==at(i+1)->key()) {
      return i+1;
    } else {
      return -1;
    }
  } else if(i<n) {
    if(k==at(i)->key()) {
      return i;
    } else {
      return -1;
    }
  } else {
    return -1;
  }
}

template<class Key, class T>
int SortedVector<Key, T>::firstIndexAfter(const Key& k) const
{
  int n=count();
  int i=_n2-1;
  int step2=_n2 >> 1;
  while(step2>0) {
    if(i>=n) {
      i-=step2;
    } else {
      if(k<at(i)->key()) {
        i-=step2;
      } else {
        i+=step2;
      }
    }
    step2=step2 >> 1;
  }
  if(i<n && k>=at(i)->key()) {
    return i+1;
  } else {
    return i;
  }
}

template<class Key, class T>
void SortedVector<Key, T>::insert(T * o)
{
  int i=firstIndexAfter(o->key());
  QVector<T *>::insert(i, o);
  updateN2();
}

template<class Key, class T>
void SortedVector<Key, T>::append(T * o)
{
  QVector<T *>::append(o);
  updateN2();
}

template<class Key, class T>
void SortedVector<Key, T>::remove(int index)
{
  QVector<T *>::remove(index);
  int n2=_n2 >> 1;
  if(n2>0 && n2>=count()) {
    _n2=n2;
  }
}

template<class Key, class T>
void SortedVector<Key, T>::clear(bool del)
{
  if(del) {
    qDeleteAll(*static_cast<QVector<T *> *>(this));
  }
  QVector<T *>::clear();
}

} // namespace QGpCoreTools

#endif // SORTEDVECTOR_H
