/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   Balasko, Jeno
 *   Delic, Adam
 *   Forstner, Matyas
 *   Gecse, Roland
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Janos Zoltan – initial implementation
 *
 ******************************************************************************/
#ifndef _Common_vector_HH
#define _Common_vector_HH

#ifdef __SUNPRO_CC
/**
 * Inclusion of the STL vector header file prevents the Sun Forte 6.2 C++
 * compiler from a mysterious internal error.
 */
#include <vector>
#include <stdio.h>
#endif

#include "error.h"
#include "../common/memory.h"
#include <string.h> // for memmove

/**
 * Container optimized to store elements sequentially,
 * and access them randomly, referenced by indices.
 *
 * Accessing an element has constant time cost.
 * Adding an element at the end has amortized constant time const.
 * Other operations (adding at the beginning, replacing/deleting elements)
 * have linear time cost.
 *
 * If there aren't elements in the buffer, then no space is allocated.
 * If there are, the size of the allocated buffer is the smallest power of 2
 *   that is not smaller than the number of elements (num_e).
 *
 * The container stores pointers to objects of type T; it doesn't own them.
 * It is the responsibility of the caller to create and destroy the objects
 * and supply the pointers to the container.
 *
 * \ingroup containers
 */
template<class T>
class vector {

private:

  size_t num_e;
  T **e_ptr;

  static const size_t initial_size = 1, increment_factor = 2;

  /** Copy constructor: DO NOT IMPLEMENT! */
  vector(const vector&);
  /** Copy assignment: DO NOT IMPLEMENT! */
  vector& operator=(const vector&);

public:

  static const size_t max_vector_length = static_cast<size_t>( -1 );

  /** Creates an empty vector. */
  vector() : num_e(0), e_ptr(NULL) { }

  /** Deallocates its memory. If the container is not empty,
   *  FATAL_ERROR occurs, so before destructing, clear() must be
   *  invoked explicitly.
   */
  ~vector() {
    if (num_e > 0) FATAL_ERROR("vector::~vector(): vector is not empty");
    Free(e_ptr);
  }

  /** Returns the number of elements in the container. */
  size_t size() const { return num_e; }

  /** Returns true if the container has no elements. */
  bool empty() const { return num_e == 0; }

  /** Erases the entire container. */
  void clear() {
    num_e = 0;
    Free(e_ptr);
    e_ptr = NULL;
  }

  /** Appends the \a elem to the end of vector. */
  void add(T *elem) {
    if (e_ptr == NULL) {
      e_ptr = static_cast<T**>(Malloc(initial_size * sizeof(*e_ptr)));
    } else {
      size_t max_e = initial_size;
      while (max_e < num_e) max_e *= increment_factor;
      if (max_e <= num_e) {
	if (max_e >= max_vector_length / increment_factor)
	    FATAL_ERROR("vector::add(): vector index overflow");
	e_ptr = static_cast<T**>
		(Realloc(e_ptr, max_e * increment_factor * sizeof(*e_ptr)));
      }
    }
    e_ptr[num_e++] = elem;
  }

  /** Inserts the \a elem to the beginning of vector. */
  void add_front(T *elem) {
    if (e_ptr == NULL) {
      e_ptr = static_cast<T**>(Malloc(initial_size * sizeof(*e_ptr)));
    } else {
      size_t max_e = initial_size;
      while (max_e < num_e) max_e *= increment_factor;
      if (max_e <= num_e) {
	if (max_e >= max_vector_length / increment_factor)
	    FATAL_ERROR("vector::add_front(): vector index overflow");
	e_ptr = static_cast<T**>
		(Realloc(e_ptr, max_e * increment_factor * sizeof(*e_ptr)));
      }
    }
    memmove(e_ptr + 1, e_ptr, num_e * sizeof(*e_ptr));
    num_e++;
    e_ptr[0] = elem;
  }
  
  /** Inserts the elem to a specified position in the vector. */
  void insert(T* elem, size_t pos) {
    if (e_ptr == NULL) {
      e_ptr = static_cast<T**>(Malloc(initial_size * sizeof(*e_ptr)));
    }
    else {
      size_t max_e = initial_size;
      while (max_e < num_e) {
        max_e *= increment_factor;
      }
      if (max_e <= num_e) {
        if (max_e >= max_vector_length / increment_factor) {
          FATAL_ERROR("vector::insert(): vector index overflow");
        }
        e_ptr = static_cast<T**>
          (Realloc(e_ptr, max_e * increment_factor * sizeof(*e_ptr)));
      }
    }
    memmove(e_ptr + pos + 1, e_ptr + pos, (num_e - pos) * sizeof(*e_ptr));
    num_e++;
    e_ptr[pos] = elem;
  }

  /** Returns the <em>n</em>th element. The index of the first element is
   * zero. If no such index, then FATAL_ERROR occurs. */
  T* operator[](size_t n) const {
    if (n >= num_e)
      FATAL_ERROR("vector::operator[](size_t) const: index overflow");
    return e_ptr[n];
  }

  /** Returns the <em>n</em>th element. The index of the first element is
   * zero. If no such index, then FATAL_ERROR occurs. */
  T*& operator[](size_t n) {
    if (n >= num_e)
      FATAL_ERROR("vector::operator[](size_t): index overflow");
    return e_ptr[n];
  }

  /** Replaces \a n elements beginning from position \a pos
   * with elements in \a v. If \a pos+n > size() then FATAL_ERROR occurs.
   * If \a v == NULL then deletes the elements.
   */
  void replace(size_t pos, size_t n, const vector *v = NULL) {
    if (pos > num_e) FATAL_ERROR("vector::replace(): position points over " \
      "the last element");
    else if (n > num_e - pos) FATAL_ERROR("vector::replace(): not enough " \
      "elements after the start position");
    else if (v == this) FATAL_ERROR("vector::replace(): trying to replace " \
      "the original vector");
    size_t v_len = v != NULL ? v->num_e : 0;
    if (v_len > max_vector_length - num_e + n)
      FATAL_ERROR("vector::replace(): resulting vector size exceeds maximal " \
	"length");
    size_t new_len = num_e - n + v_len;
    if (new_len > num_e) {
      size_t max_e = initial_size;
      if (e_ptr == NULL) {
	while (max_e < new_len) max_e *= increment_factor;
	e_ptr = static_cast<T**>(Malloc(max_e * sizeof(*e_ptr)));
      } else {
	while (max_e < num_e) max_e *= increment_factor;
	if (new_len > max_e) {
	  while (max_e < new_len) max_e *= increment_factor;
	  e_ptr = static_cast<T**>(Realloc(e_ptr, max_e * sizeof(*e_ptr)));
	}
      }
    }
    if (pos + n < num_e && v_len != n) memmove(e_ptr + pos + v_len,
	e_ptr + pos + n, (num_e - pos - n) * sizeof(*e_ptr));
    if (v_len > 0) memcpy(e_ptr + pos, v->e_ptr, v_len * sizeof(*e_ptr));
    if (new_len < num_e) {
      if (new_len == 0) {
	Free(e_ptr);
	e_ptr = NULL;
      } else {
	size_t max_e = initial_size;
	while (max_e < num_e) max_e *= increment_factor;
	size_t max_e2 = initial_size;
	while (max_e2 < new_len) max_e2 *= increment_factor;
	if (max_e2 < max_e)
	  e_ptr = static_cast<T**>(Realloc(e_ptr, max_e2 * sizeof(*e_ptr)));
      }
    }
    num_e = new_len;
  }

  /**
   * Copies a part of the vector to a new vector. The part is
   * specified by the starting position (<em>pos</em>) and the number
   * of elements (<em>n</em>) to copy. If <em>n</em> is greater than
   * the number of elements beginning from the given <em>pos</em>,
   * then the returned vector contains less elements.
   *
   * \note The pointers are copied, the objects they refer to will not
   * be duplicated.
   */
  vector* subvector(size_t pos = 0, size_t n = max_vector_length) const
  {
    if (pos > num_e) FATAL_ERROR("vector::subvector(): position points " \
      "over last vector element");
    if (n > num_e - pos) n = num_e - pos;
    vector *tmp_vector = new vector;
    if (n > 0) {
      size_t max_e = initial_size;
      while (max_e < n) max_e *= increment_factor;
      tmp_vector->e_ptr = static_cast<T**>(Malloc(max_e * sizeof(*e_ptr)));
      memcpy(tmp_vector->e_ptr, e_ptr + pos, n * sizeof(*e_ptr));
      tmp_vector->num_e = n;
    }
    return tmp_vector;
  }

}; // class vector

/**
 * Container to store simple types (can have constructor, but it should be fast)
 * that are simple and small, stores the objects and not the pointers (copy 
 * constructor must be implemented). The capacity is increased to be always the
 * power of two, the container capacity is never decreased. An initial
 * capacity value can be supplied to the constructor, this can be used to avoid
 * any further memory allocations during the life of the container.
 */
template<class T>
class dynamic_array {
private:
  size_t count;
  size_t capacity; // 0,1,2,4,8,...
  T* data;

  void clean_up();
  void copy_content(const dynamic_array& other);
public:

  dynamic_array() : count(0), capacity(0), data(NULL) {}
  // to avoid reallocations and copying
  dynamic_array(size_t init_capacity) : count(0), capacity(init_capacity), data(NULL) 
    { if (capacity>0) data = new T[capacity]; }
  dynamic_array(const dynamic_array& other) : count(0), capacity(0), data(NULL) { copy_content(other); }
  dynamic_array& operator=(const dynamic_array& other) { clean_up(); copy_content(other); return *this; }
  ~dynamic_array() { clean_up(); }

  bool operator==(const dynamic_array& other) const;
  bool operator!=(const dynamic_array& other) const { return (!(*this==other)); }

  /** Returns the number of elements in the container. */
  size_t size() const { return count; }

  /** Erases the entire container. */
  void clear() { clean_up(); }

  /** Appends the \a elem to the end of vector. */
  void add(const T& elem);

  /** Removes an element that is at index \a idx */
  void remove(size_t idx);

  /** Returns the <em>n</em>th element. The index of the first element is
   * zero. If no such index, then FATAL_ERROR occurs. */
  const T& operator[](size_t n) const {
    if (n>=count) FATAL_ERROR("dynamic_array::operator[] const: index overflow");
    return data[n];
  }

  /** Returns the <em>n</em>th element. The index of the first element is
   * zero. If no such index, then FATAL_ERROR occurs. */
  T& operator[](size_t n) {
    if (n>=count) FATAL_ERROR("dynamic_array::operator[]: index overflow");
    return data[n];
  }
}; // class dynamic_array

template<class T>
void dynamic_array<T>::clean_up()
{
  delete[] data;
  data = NULL;
  capacity = 0;
  count = 0;
}

template<class T>
void dynamic_array<T>::copy_content(const dynamic_array<T>& other)
{
  capacity = other.capacity;
  count = other.count;
  if (capacity>0) {
    data = new T[capacity];
    for (size_t i=0; i<count; i++) data[i] = other.data[i];
  }
}

template<class T>
bool dynamic_array<T>::operator==(const dynamic_array<T>& other) const
{
  if (count!=other.count) return false;
  for (size_t i=0; i<count; i++) if (!(data[i]==other.data[i])) return false;
  return true;
}

template<class T>
void dynamic_array<T>::add(const T& elem)
{
  if (data==NULL) {
    capacity = 1;
    count = 1;
    data = new T[1];
    data[0] = elem;
  } else {
    if (count<capacity) {
      data[count] = elem;
      count++;
    } else {
      // no more room, need to allocate new memory
      if (capacity==0) FATAL_ERROR("dynamic_array::add()");
      capacity *= 2;
      T* new_data = new T[capacity];
      for (size_t i=0; i<count; i++) new_data[i] = data[i];
      delete[] data;
      data = new_data;
      data[count] = elem;
      count++;
    }
  }
}

template<class T>
void dynamic_array<T>::remove(size_t idx)
{
  if (idx>=count) FATAL_ERROR("dynamic_array::remove(): index overflow");
  for (size_t i=idx+1; i<count; i++) data[i-1] = data[i];
  count--;
}

#endif // _Common_vector_HH
