//
// LiDIA - a library for computational number theory
//   Copyright (c) 1995 by the LiDIA Group
//
// File        : base_vector.c
// Author      : Frank Lehmann (FL), Markus Maurer (MM) 
//               Thomas Papanikolaou (TP), Patrick Theobald (PT)
// Last change : FL/MM, Feb 15 1995, initial version
//               FL/MM, Mar  8 1995
//               FL/MM, May 10 1995, added get_data_address 
//                                   clean-up
//		 FL/MM, May 15 1995, added typedef size_type and
//				     changed type of corresponding variables
//               FL/MM, May 23 1995, removed #define LiDIA_VECTOR_NO_RANGE_CHECK
//		 FL/MM, Jul  6 1995, changed internal size_type of base_vector
//				     to the global LiDIA-type lidia_size_t
//               FL/MM, Jul 24 1995, added 'const' to T* parameters of constructors
//		 FL/MM, Aug 30 1995, changed typedef int lidia_size_t to
//				     "#define ..."
//		 FL/MM, Aug 30 1995, changed "error_handler" to "lidia_error_handler"
//		 FL/MM, Sep 22 1995, removed "#define lidia_size_t ..." and
//				     "#ifdef LiDIA_VECTOR_INTERNAL_DATA_ACCESS ..."
//		 FL/MM, Sep 29 1995, changed "debug_handler" to "debug_handler_l"
//               PT   , Oct 28 1995, new template concept
//               PT   , Mar 12 1996, bug in concat fixed: c.concat(c,c)
//		 MM/PT, Apr 12 1996, bug in concat fixed: length = b.length + c.length
//

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:base_vector.h>
#include <LiDIA:arith.inl>
#include <LiDIA:xerror.h>
#else
#include <LiDIA/base_vector.h>
#include <LiDIA/arith.inl>
#include <LiDIA/xerror.h>
#endif

/**
 ** debug defines / error defines
 **/

extern const char *PRT;
extern const char *vector_error_msg[];

#define DV_BV LDBL_VECTOR         /* Debug value   */
#define DM_BV "base_vector"       /* Debug message / Error message */
#define LBV_ERROR vector_error_msg

/**
 ** debug level
 **
 **   0 : constructors / destructor
 **   1 : input / output      
 **   2 : access functions
 **   3 : divide / concat / assign
 **   4 : swap functions
 **   5 : structur functions
 **   6 : assignments
 **   7 : reverse functions
 **   8 : stream handling
 **   9 : remove / insert
 **/

template < class T >
T * base_vector < T >::
copy_data(T *d, const T *vd, lidia_size_t l) 
{
  debug_handler_l(DM_BV, "in member - function "
		  "copy_data(T *, T *, lidia_size_t)", DV_BV);
  
  for (register lidia_size_t i = 0; i < l; i++)
    d[i] = vd[i];
  return d;
}	

/**
 ** constructors
 **/

template < class T >
base_vector < T >::base_vector ()    
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector()", DV_BV);

  length = 0;
  allocated = 0;
  mode = FIXED;
  dyn_exp_ratio = DEFAULT_RATIO;
  data = nil;
}

/*
template < class T >
base_vector < T >::base_vector(char md)
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(char)", DV_BV);
  
  length = 0;
  allocated = 0;
  mode = md;
  dyn_exp_ratio = DEFAULT_RATIO;
  data = nil;
}

template < class T >
base_vector < T >::base_vector(lidia_size_t all)
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(lidia_size_t)", DV_BV);
  
  length = 0;
  allocated = all;
  mode = FIXED;
  dyn_exp_ratio = DEFAULT_RATIO;
  data = nil;

  if (all > 0)
    {
      data = new T[all];
      memory_handler(data, DM_BV, "base_vector :: "
		     "out of memory");
    }
  else 
    if (all < 0)
      lidia_error_handler_para(all, "all", "all > 0",
			       "base_vector < T >::"
			       "base_vector(lidia_size_t all)",
			       DM_BV, LBV_ERROR[0]);
}
*/

template < class T >
base_vector < T >::base_vector(lidia_size_t all, char md)
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(lidia_size_t, char)", DV_BV);
  
  length = 0;
  allocated = all;
  mode = md;
  dyn_exp_ratio = DEFAULT_RATIO;
  data = nil;
  
  if (all > 0)
    {
      data = new T[all];
      memory_handler(data, DM_BV, "base_vector :: "
		     "out of memory" );
    }
  else 
    if (all < 0)
      lidia_error_handler_para(all, "all", "all > 0",
			       "base_vector < T >::"
			       "base_vector(lidia_size_t all, char md)",
			       DM_BV, LBV_ERROR[0]);
}

template < class T >
base_vector < T >::base_vector(lidia_size_t all, lidia_size_t len) 
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(lidia_size_t, lidia_size_t)", DV_BV);
  
  length = (len < all) ? len : all;
  allocated = all;
  mode = FIXED;
  dyn_exp_ratio = DEFAULT_RATIO;
  data = nil;
  
  if (all > 0)
    {
      data = new T[all];
      memory_handler(data, DM_BV, "base_vector :: "
		     "out of memory");
    }
  else 
    if ( all < 0 )
      lidia_error_handler_para(all, "all", "all > 0",
			       "base_vector < T >::"
			       "base_vector(lidia_size_t all, lidia_size_t len)",
			       DM_BV, LBV_ERROR[0]);
}

template < class T >
base_vector < T >::base_vector(lidia_size_t all, lidia_size_t len, char md)
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(lidia_size_t, lidia_size_t, char)", DV_BV);
  
  length = (len < all) ? len : all;
  allocated = all;
  mode = md;
  dyn_exp_ratio = DEFAULT_RATIO;
  data = nil;
  
  if (all > 0)
    {
      data = new T[all];
      memory_handler(data, DM_BV, "base_vector :: "
		     "out of memory");
    }
  else 
    if (all < 0)
      lidia_error_handler_para(all, "all", "all > 0",
			       "base_vector < T >::"
			       "base_vector(lidia_size_t all, lidia_size_t len, char md)",
			       DM_BV, LBV_ERROR[0]);
}

template < class T >
base_vector < T >::base_vector(const base_vector < T > &v)
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(base_vector < T > &)", DV_BV);

  length = v.length;
  allocated = v.length;
  mode = FIXED;
  dyn_exp_ratio = DEFAULT_RATIO;
  
  if (allocated > 0)
    {
      data = new T[allocated];
      memory_handler(data, DM_BV, "base_vector :: "
		     "out of memory");
      
      copy_data(data, v.data, length);
    }
  else
    data = nil;
}

template < class T >
base_vector < T >::base_vector(const base_vector < T > & v, char md)
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(base_vector < T > &, char)", DV_BV);
  
  length = v.length;
  allocated = v.length;
  mode = md;
  dyn_exp_ratio = DEFAULT_RATIO;
  
  if (allocated > 0)
    {
      data = new T[allocated];
      memory_handler(data, DM_BV, "base_vector :: "
		     "out of memory");
      
      copy_data(data, v.data, length);
    }
  else
    data = nil;
}

template < class T >
base_vector < T >::base_vector(const T *v, lidia_size_t len)
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(const T *, lidia_size_t)", DV_BV);
  
  length = len;
  allocated = len;
  mode = FIXED;  
  dyn_exp_ratio = DEFAULT_RATIO;
  data = nil;
  
  if (allocated > 0)
    {
      data = new T[allocated];    
      memory_handler(data, DM_BV, "base_vector :: "
		     "out of memory");
      
      copy_data(data, v, length);
    }
  else 
    if (allocated < 0)
      lidia_error_handler_para(allocated, "allocated", "allocated > 0",
			       "base_vector < T >::"
			       "base_vector(const T *v, lidia_size_t len)",
			       DM_BV, LBV_ERROR[6]);
}

template < class T >
base_vector < T >::base_vector(const T *v, lidia_size_t len, char md)
{
  debug_handler_l(DM_BV, "in constructor "
		  "base_vector(const T *, lidia_size_t, char)", DV_BV);
  
  length = len;
  allocated = len;
  mode = md;
  dyn_exp_ratio = DEFAULT_RATIO;
  data = nil;
  
  if (allocated > 0)
    {
      data = new T[allocated];
      memory_handler(data, DM_BV, "base_vector :: "
		     "out of memory");
      
      copy_data(data, v, length);
    }
  else 
    if (allocated < 0)
      lidia_error_handler_para(allocated, "allocated", "allocated > 0",
			       "base_vector < T >::"
			       "base_vector(const T *v, lidia_size_t len, char md)",
			       DM_BV, LBV_ERROR[6]);
}

/**
 ** destructor  
 **/

template < class T >
base_vector < T >::~base_vector()
{
  debug_handler_l(DM_BV, "in destructor "
		  "~base_vector()", DV_BV);
  
  if (allocated) 
    delete[] data;
}

/**
 **  Input / Output  
 **/

template < class T >
void base_vector < T >::
read(istream & in)
{
  debug_handler_l(DM_BV, "in member - function "
		  "read(istream &)", DV_BV + 1);
  
  register lidia_size_t n = 0; 
  lidia_size_t sz = 4;
  
#ifdef __GNUG__
  
  register lidia_size_t i;
  
  if (in.flags() & ios::bin)  /* binary-mode for stream 'in' */
    {
      in >> sz;
      
      set_capacity(sz);
      length = sz;
      
      for (i = 0; i < length; i++)
	in >> data[i];
      return;
    }
  
#endif

  char c;
  
  T *vbuf = new T[sz];
  memory_handler(vbuf, DM_BV, "read :: "
		 "out of memory");
  in >> ws >> c;
  if (c != '[')
    lidia_error_handler_para(DM_BV, "read :: "
			     "[ expected");
  in >> ws >> c;
  while (c != ']')
    {
      in.putback(c);
      in >> vbuf[n];
      n++;

      if (n == sz)
	{
	  debug_handler_l(DM_BV, "read :: "
			  "doubling input size", DV_BV);
	  
	  lidia_size_t osz = sz;
	  
	  sz *= 2;
	  
	  T *nbuf = new T[sz];
	  memory_handler(nbuf, DM_BV, "read :: "
			 "out of memory");
	  
	  copy_data(nbuf, vbuf, osz);
	  delete[] vbuf;
	  vbuf = nbuf;
	}
      in >> ws >> c;
    }
  
  set_capacity(n);
  length = n;  
  copy_data(data, vbuf, n);
  delete[] vbuf;
}

template < class T >
void base_vector < T >::
write(ostream & out) const
{
  debug_handler_l (DM_BV, "in member - function "
		   "write(ostream &)", DV_BV + 1);
  
  register lidia_size_t i;
  
#ifdef __GNUG__
  
  if (out.flags() & ios::bin)  // binary-mode for stream 'out'
    {
      out << length;
      
      for (i = 0; i < length; i++)
	out << data[i] << " " << flush;
      return;
    }
  
#endif
  
  out << "[ ";
  
  for ( i = 0 ; i < length ; i++ )
    out << data[i] << " ";
  
  out << "]" << flush ;
}

/**
 ** structur functions: reading and modifying the variables of the vector  
 **/

template < class T >
void base_vector < T >::
set_capacity(lidia_size_t all)
{
  debug_handler_l(DM_BV, "in member - function "
		  "set_capacity(lidia_size_t)", DV_BV + 5);       
  
  if (all != allocated)
    if ( all == 0 )
      {
	delete[] data;
	data = nil;
	  
	length = 0;
	allocated = 0;
      }
    else 
      if (all > 0)
	{
	  T *tmp = new T[all];
	  memory_handler(tmp, DM_BV , "set_capacity :: "
			 "out of memory");
	  
	  length = (all < length) ? all : length;
	  copy_data(tmp, data, length);

	  if (data != nil) 
	    delete[] data;
	      
	  data = tmp;
	  allocated = all;
	}
      else
	lidia_error_handler_para(all, "all", "all > 0",
				 "void base_vector < T >::"
				 "set_capacity(lidia_size_t all)",
				 DM_BV, LBV_ERROR[0]);
}

template < class T >
void base_vector < T >::
kill()
{
  debug_handler_l(DM_BV, "in member - function "
		  "kill()", DV_BV + 5);
  
  if (allocated)
    {
      delete[] data;
      data = nil;
      
      length = 0;
      allocated = 0;
    }
}

template < class T >
void base_vector < T >::
set_size(lidia_size_t len)
{
  debug_handler_l(DM_BV, "in member - function "
		  "set_size(lidia_size_t)", DV_BV + 5);
  
  if (len < 0)
    lidia_error_handler_para(len, "len", "len > 0",
			     "void base_vector < T >::"
			     "set_size(lidia_size_t len)",
			     DM_BV, LBV_ERROR[0]);
  else 
    if (len <= allocated)
      length = len ;
    else 
      if (mode == EXPAND)
	{
	  /***  realloc 'data' to provide access to expanding vectors  ***/
	  set_capacity((lidia_size_t)(dyn_exp_ratio * len));
	  length = len;
	}
      else
	lidia_error_handler_para(mode, "mode", "mode == EXPAND",
				 "void base_vector < T >::"
				 "set_size(lidia_size_t len)", 
				 DM_BV, LBV_ERROR[7]);
}

/**
 ** access functions  
 **/

template < class T >
T & base_vector < T >:: 
operator [](lidia_size_t i)
{
  debug_handler_l(DM_BV, "in operator "
		  "[] (lidia_size_t)", DV_BV + 2);
  
  if (i < 0)
    lidia_error_handler_para(i, "i", "i >= 0", 
			     "T & base_vector < T >::"
			     "operator [](lidia_size_t i)",
			     DM_BV, LBV_ERROR[3]);
  else
    if (i < allocated)
      length = (i + 1 > length) ? ( i + 1 ) : length;
    else 
      if (mode == EXPAND)
	{
	  set_capacity((lidia_size_t)(dyn_exp_ratio * (i + 1)));
	  length =  i + 1;
	}
      else
	lidia_error_handler_para(i, "i", "i < allocated",
				 mode, "mode", "mode == EXPAND",
				 "T & base_vector < T >::"
				 "operator [](lidia_size_t i)",
				 DM_BV, LBV_ERROR[3]);
  return data[i];
}

template < class T >
const T & base_vector < T >::
operator [](lidia_size_t i) const
{
  debug_handler_l(DM_BV, "in operator "
		  "[] (lidia_size_t) const", DV_BV + 2);
  
  if (i < 0 || i >= length)
    lidia_error_handler_para(i, "i", "0 <= i < length",
			     "const T & base_vector < T >::"
			     "operator [](lidia_size_t i) const",
			     DM_BV, LBV_ERROR[3]);
  return data[i];      
}

template < class T >
const T & base_vector < T >::
member(lidia_size_t i) const
{
  debug_handler_l(DM_BV, "in meber - function "
		  "member(lidia_size_t)", DV_BV + 2);
  
  if (i < 0 || i >= length)
    lidia_error_handler_para(i, "i", "0 <= i < length",
			     "T & base_vector < T >::"
			     "member(lidia_size_t i) const",
			     DM_BV, LBV_ERROR[3]);
  return data[i];      
}

template < class T >
void base_vector < T >::
set_data(const T *d, lidia_size_t l)
{
  debug_handler_l(DM_BV, "in member - function "
		  "set_data(const T *, lidia_size_t)", DV_BV + 2);
  
  set_capacity(l);
  length = l;

  register lidia_size_t i;
  for (i = 0; i < l; i++)
    data[i] = d[i];
}

template < class T >
T *base_vector < T >::
get_data() const
{
  debug_handler_l(DM_BV, "in base_vector :: "
		  "get_data()", DV_BV + 2);
  
  T *d = new T[length];
  memory_handler(d, DM_BV, "get_data :: "
		 "memory exhausted");
  
  register lidia_size_t i;  
  for (i = 0; i < length; i++)
    d[i] = data[i];  
  return d;
}

/**
 ** assignment 
 **/

template < class T >
base_vector< T > & base_vector < T >::
operator = (const base_vector < T > & v)
{
  debug_handler_l(DM_BV, "in operator "
		  "= (const base_vector < T > &)", DV_BV + 6);

  assign(v);
  return *this;
}

template <class T >
void base_vector < T >::
assign(lidia_size_t at, const base_vector< T > & v, lidia_size_t from, lidia_size_t to)
{
  debug_handler_l(DM_BV, "in member - function "
		  "assign(lidia_size_t, base_vector, lidia_size_t, lidia_size_t)", DV_BV + 3);
  
  register lidia_size_t i, j;
  
  if (at < 0 || from < 0 || from >= v.length || to < 0 || to >= v.length || to < from)
    lidia_error_handler_para(DM_BV, "assign :: "
			"invalid indices");
  
  register lidia_size_t n = to - from + 1;  /* this value is positive */
  register lidia_size_t new_len = (length < at + n) ? ( at + n ) : length;
  
  if (this != &v)                    /* v aliases current instance */
    {
      set_size(new_len);
      for (i = from, j = at; i <= to; i++, j++)
	data[j] = v.data[i];
    }
  else
    {
      set_size(new_len);
      T *tmp = new T[n];
      memory_handler(tmp, DM_BV, "assign :: "
		     "out of memory");
	  
      for (i = from, j = 0; i <= to; i++, j++)
	tmp[j] = v.data[i];
	  
      for (i = 0, j = at; i < n; i++, j++)
	data[j] = tmp[i];
	  
      delete[] tmp;
    }
}

template < class T >
void base_vector < T >::
assign(const base_vector < T > &v)  
{
  debug_handler_l(DM_BV, "in member - function "
		  "assign(const base_vector < T > &)", DV_BV + 6);
  
  if (this != &v)
    {
      set_capacity(v.length);
      length = v.length;
      
      copy_data(data, v.data, length);
      
      sort_dir = v.sort_dir;
      el_cmp = v.el_cmp;
    }
}

/**
 ** reverse function 
 **/

template < class T >
void base_vector < T >::reverse()
{
  debug_handler_l(DM_BV, "in member - function "
		  "reverse()", DV_BV + 7);
  
  register lidia_size_t i, j;
  
  for (i = 0, j = length - 1; i < j; i++, j--)
    ::swap(data[i], data[j]);

}

template < class T >
void base_vector < T >::
reverse(const base_vector< T > & b)
{
  debug_handler_l(DM_BV, "in member - function "
		  "reverse(const base_vector< T > &)", DV_BV + 7);
  
  register lidia_size_t i, j;
  
  if (this == &b)
    for (i = 0, j = length - 1; i < j; i++, j--)
      ::swap(data[i], data[j]);
  else
    {	
      set_capacity(b.length);
      length = b.length;
      
      for (i = 0; i < length; i++)
	data[i] = b.data[length - i - 1];
    }
}

/**
 ** swap functions
 **/

template < class T >
void base_vector < T >::
swap(base_vector < T > & b)
{
  debug_handler_l(DM_BV, "in member - function "
		  "swap(base_vector < T > &)", DV_BV + 4);
  

  int (*e) (const T & a, const T & b);	
  
  ::swap(length, b.length);
  ::swap(allocated, b.allocated);
  ::swap(mode, b.mode);
  ::swap(dyn_exp_ratio, b.dyn_exp_ratio);

  T *d = data;
  data = b.data;
  b.data = d;

  char s = sort_dir;
  sort_dir = b.sort_dir;
  b.sort_dir = s;

  e = el_cmp;
  el_cmp = b.el_cmp;
  b.el_cmp = e;
}  


/**
 ** concat function
 **/

template < class T >
void base_vector < T >::
concat(const base_vector < T > &b, const base_vector < T > &c)
{
  debug_handler_l(DM_BV, "in member - function "
		  "concat(base_vector < T > &, const base_vector < T > &)", DV_BV + 3);
  
  register lidia_size_t i, j, l, oldlength;
  
  if (this != &b && this != &c)
    {
      l = b.length + c.length;
      set_capacity(l);
      length = l;
      
      for (i = 0 ;i < b.length ;i++)
	data[i] = b.data[i];
      
      j = b.length;
      
      for (i = 0; i < c.length; i++ ,j++)
	data[j] = c.data[i];
    }
  else 
    {
      if (this == &b)
	{
	  j = length ;
	  oldlength = c.length;
	  l = length + c.length;
	  set_capacity(l);
	  
	  for (i = 0; i < oldlength; i++, j++)
	    data[j] = c.data[i];
	}	
      else 
	{
	  base_vector < T > tmp(b.length + c.length, FIXED);
	  
	  for (i = 0; i < b.length; i++)
	    tmp.data[i] = b.data[i];
	  
	  j = b.length ;
	  
	  for (i = 0; i < c.length; i++, j++)
	    tmp.data[j] = c.data[i];
	  
	  /* exchange pointers instead of copying */
	  T *p = data;
	  data = tmp.data;
	  tmp.data = p;
	  
	  tmp.allocated = allocated;
	}
    }
  allocated = length = b.length + c.length;
}

/**
 ** shift functions
 **/

template < class T >
void base_vector < T >::
shift_left(lidia_size_t pos, lidia_size_t num)
{
  debug_handler_l(DM_BV, "in member - function "
		  "shift_left(lidia_size_t, lidia_size_t)", DV_BV + 3);
  
  register lidia_size_t i;
  register lidia_size_t old_len = length;
  
  if (pos < num)
    lidia_error_handler_para (pos, "pos", "pos >= num",
			      "void base_vector < T >::"
			      "shift_left(lidia_size_t pos, lidia_size_t num)",
			      DM_BV, LBV_ERROR[3]);
  else
    {
      set_size(old_len - num);
      for (i = pos; i < old_len; i++)
	data[i - num] = data[i];
    }
} 

template < class T >
void base_vector < T >::
shift_right(lidia_size_t pos, lidia_size_t num)
{
  debug_handler_l(DM_BV, "in member - function "
		  "shift_right(lidia_size_t, lidia_size_t)", DV_BV + 3);
  
  register lidia_size_t i;
  register lidia_size_t old_len = length;
  
  if (pos < 0)
    lidia_error_handler_para (pos, "pos", "pos >= 0",
			      "void base_vector < T >::"
			      "shift_right(lidia_size_t pos, lidia_size_t num)",
			      DM_BV, LBV_ERROR[3]);
  else
    {
      set_size(old_len + num);
      for (i = old_len - 1; i >= pos; i--)
	data[i + num] = data[i];
    }
} 

/**
 ** remove function
 **/

template < class T >
void base_vector < T >:: 
remove_from(lidia_size_t pos, lidia_size_t l)
{
  debug_handler_l(DM_BV, "in member - function "
		  "remove_from(lidia_size_t, lidia_size_t)", DV_BV + 9);
  
  register lidia_size_t i;
  
  if (l <= 0 || pos < 0 || pos + l > length)
    lidia_error_handler_para (l, "l", "0 < l",
			      pos, "pos", "0 <= pos and pos + l <= length",
			      "void sort_vector < T >::"
			      "remove_from(lidia_size_t pos, lidia_size_t l)",
			      DM_BV, LBV_ERROR[12]);
  
  for (i = pos; i < length - l; i++)
    data[i] = data[i + l];
  set_size(length - l);
}

/**
 ** insert function
 **/

template < class T >
void base_vector < T >::
insert_at(const T & x, lidia_size_t pos)
{
  debug_handler_l(DM_SV, "in member - function "
		  "insert_at (const T &, lidia_size_t)", DV_BV + 9);
  
  register lidia_size_t i, l = length;
  
  if (pos < 0)
    lidia_error_handler_para(pos, "pos", "pos >= 0",
			     "void base_vector < T >::"
			     "insert_at(const T & x, lidia_size_t pos)",
			     DM_BV , LBV_ERROR[1]);
           
  set_size((l+1) > (pos + 1) ? l + 1 : pos + 1);
  
  /* if size has been increased, insert at position 'pos' */
  for (i = length - 1; i > pos; i--)
    data[i] = data[i - 1];
  data[pos] = x;
}

#undef DV_BV
#undef DM_BV 
#undef LBV_ERROR








