//
// LiDIA - a library for computational number theory
//   Copyright (c) 1995 by the LiDIA Group
//
// File        : sort_vector.h 
// Author      : Frank Lehmann (FL), Markus Maurer (MM) 
//               Thomas Papanikolaou (TP), Patrick Theobald (PT)
// Last change : FL/MM, Feb 15 1995, initial version
//               FL/MM, May 10 1995, clean-up
//		 FL/MM, May 15 1995, changed type of size variables to base_vector<T>::size_type
//		 FL/MM, May 27 1995, moved protected members "sort_dir", "el_cmp"
//				     from sort_vector to base_vector; changed member
//				     operator=; removed friend swap()
//		 FL/MM, Jul  6 1995, replaced base_vector<T>::size_type by lidia_size_t
//               FL/MM, Jul 24 1995, added 'const' to T* parameters of constructors
//		 FL/MM, Aug 30 1995, changed "error_handler" to "lidia_error_handler"
//               PT     Oct 28 1995, new template concept
//

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// *
// *    File        :  sort_vector.h
// *
// *    Description :  -) definition of template class 'sort_vector'
// *                      to allow use of sortable vectors
// *
// *                   -) virtual base class is 'base_vector'
// *
// *                   -) uses template class 'comparator'
// *
// *    Notes       :  *) any class MUST support the operators
// *                                  <, <=, and  == 
// *                      to allow its use in the template
// *
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:sort_vector.h>
#include <LiDIA:arith.inl>
#include <LiDIA:xerror.h>
#else
#include <LiDIA/sort_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_SV LDBL_VECTOR + 10   /* Debug value   */
#define DM_SV "sort_vector"      /* Debug message / Error message */
#define LSV_ERROR vector_error_msg

/**
 ** debug level
 **
 **   0 : assignment
 **   1 : sort - direction
 **   2 : sort - functions
 **   3 : insert into a sort vector
 **   4 : delete elements 
 **   5 : lex. compare
 **   6 : delete copies
 **/

/**
 ** assignment 
 **/

template < class T >
sort_vector < T > & sort_vector < T >::
operator = (const sort_vector < T > & v)
{
  debug_handler_l(DM_SV, "in operator "
		  "= (const sort_vector < T > &)", DV_SV);

  if (0) cerr << PRT;
  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;
    }
  return *this;
}

/**
 ** sort - direction 
 **/

template < class T >
void sort_vector < T >::
set_sort_direction(char sd)
{
  debug_handler_l(DM_SV, "in member - function "
		  "set_sort_dir(char)", DV_SV + 1);
  
  if ( sd != SORT_VECTOR_UP && sd != SORT_VECTOR_DOWN)
    lidia_error_handler_para(sd, "sd", "sd == SORT_VECTOR_UP or sd == SORT_VECTOR_DOWN",
			     "void sort_vector < T >::"
			     "set_sort_direction(char sd)",
			     DM_SV, LSV_ERROR[10]);
  else
    {
      sort_dir = sd;
      el_cmp = nil;
    }
}

template < class T >
void sort_vector < T >:: 
set_sort_direction(int (*cmp)(const T & a, const T & b))
{
  debug_handler_l(DM_SV, "in member - function "
		  "set_sort_dir(int (*cmp)(const T &, const T &)", DV_SV + 1);

  sort_dir = SORT_VECTOR_CMP;
  el_cmp = cmp;
}

/**
 ** sort-function with compare-function as parameter  
 **/

template < class T >
void sort_vector < T >::
sort(int (*cmp)(const T & a, const T & b), lidia_size_t l, lidia_size_t r)
{
  debug_handler_l(DM_SV, "in member - function "
		  "sort(int (*cmp)(const T &, const T &), lidia_size_t, lidia_size_t)", DV_SV + 2);

  r = (r == -1) ? length - 1 : r;

  register lidia_size_t gap, i, j, len;

  if (length == 0 && l == 0 && r == -1)  /* empty vector and default - cut */
    return;

  if (l < 0 || l >= length || r < 0 || r >= length || l > r)
    lidia_error_handler_para(l, "l", "0 <= l < length",
			     r, "r", "0 <= r < length",
			     "void sort_vector < T >::"
			     "sort(int (*cmp)(const T & a, const T & b), lidia_size_t l, lidia_size_t r)",
			     DM_SV, LSV_ERROR[11]);

  T *ptr = &data[l];
  len = r - l + 1;                      /* length of selected cut */

  for (gap = len / 2; gap > 0; gap /= 2)
    for (i = gap; i < len; i++)
      for (j = i - gap; j >= 0; j -= gap)
	{
	  if (cmp (ptr[j], ptr[j + gap]) < 0)  
	    break;
	  ::swap(ptr[j], ptr[j + gap]);
	}
}

/**
 ** sort-functions using comparator - functions  
 **/

template < class T >
void sort_vector < T >::
sort(char sort_direction, lidia_size_t l, lidia_size_t r)
{
  debug_handler_l(DM_SV, "in member - function "
		  "sort(char, lidia_size_t, lidia_size_t)", DV_SV + 2);

  sort_direction = (sort_direction == SORT_VECTOR_DEF) ? sort_dir : sort_direction;
  r = (r == -1) ? length - 1 : r;
  
  if ( length == 0 && l == 0 && r == -1 )  /* empty vector and default-cut */
    return;
  
  if ( l < 0 || l >= length || r < 0 || r >= length || l > r )
    lidia_error_handler_para(l, "l", "0 <= l < length",
			     r, "r", "0 <= r < length",
			     "void sort_vector < T >::"
			     "sort(char sort_direction, lidia_size_t l, lidia_size_t r)",
			     DM_SV, LSV_ERROR[11]);
    
  /* call of corresponding function */
  switch(sort_direction)
    {
    case SORT_VECTOR_UP: 
      sort_up(l, r);
      break;
	  
    case SORT_VECTOR_DOWN: 
      sort_down(l, r);
      break;
	  
    case SORT_VECTOR_CMP: 
      sort(el_cmp, l, r);
      break;
	  
    default: 
      lidia_error_handler_para(sort_direction, "sort_direction", "sort_direction in "
			       "{SORT_VECTOR_UP, SORT_VECTOR_DOWN, SORT_VECTOR_CMP}",
			       "void sort_vector < T >::"
			       "sort(char sort_direction, lidia_size_t l, lidia_size_t r)",
			       DM_SV, LSV_ERROR[10]);
    }
}

template < class T >
void sort_vector < T >::
sort_up(lidia_size_t l, lidia_size_t r)
{
  debug_handler_l(DM_SV, "in member - function "
		  "sort_up(lidia_size_t, lidia_size_t)", DV_SV + 2);

  lidia_size_t gap, i, j;
  T *ptr = & data[l];
  r = r - l + 1;                       /* length of selected cut */
  
  for (gap = r / 2; gap > 0; gap /= 2)
    for (i = gap; i < r; i++)
      for (j = i - gap; j >= 0; j -= gap)
	{
	  if (comparator < T >::less_equal(ptr[j], ptr[j + gap]) > 0)  
	    break;
	  ::swap(ptr[j], ptr[j + gap]);
	}
}

template < class T >
void sort_vector < T >::
sort_down(lidia_size_t l, lidia_size_t r)
{
  debug_handler_l(DM_SV, "in member - function "
		  "sort_down(lidia_size_t, lidia_size_t)", DV_SV + 2);
  
  register lidia_size_t gap, i, j;
  T *ptr = & data[l];
  r = r - l + 1;                       /* length of selected cut */

  for (gap = r / 2; gap > 0; gap /= 2)
    for (i = gap; i < r; i++)
      for (j = i - gap; j >= 0; j -= gap)
	{
	  if (comparator< T >::greater_equal(ptr[j], ptr[j + gap]) > 0)  
	    break ;
	  ::swap(ptr[j], ptr[j + gap]);
	}
}

/**
 ** function for linear search in a vector  
 **/

template < class T >
bool sort_vector < T >::
linear_search(const T & x, lidia_size_t & i) const
{
  debug_handler_l(DM_SV, "in member - function "
		  "search(const T &, lidia_size_t &)", DV_SV + 2);

  for (i = 0; i < length && !comparator< T >::equal(x, data[i]); i++);
  return (i < length) ? true : false;
}

/******  functions for binary search in sorted vectos  *****/

/******  NOTE : binary search can only be applied to a vector
 ******         which is sorted in an appropriate order
 ******/

template < class T >
bool sort_vector < T >::
bin_search(const T & x, lidia_size_t & pos, int (*cmp)(const T & a, const T & b), lidia_size_t l,lidia_size_t r) const
{
  debug_handler_l(DM_SV, "in member - function "
		  "bin_search(const T &, lidia_size_t, int (*cmp)(const T &, const T &), "
		  "lidia_size_t, lidia_size_t )", DV_SV + 2);
  
  register int c;

  if (r==-1) r = length-1;

  if ( length == 0 && l == 0 && r == -1 )  /* empty vector and default-cut */
    {
      pos = 0;
      return false;
    }

  if (l < 0 || l >= length || r < 0 || r >= length || l > r)
    lidia_error_handler_para(l, "l", "0 <= l < length",
			     r, "r", "0 <= r < length",
			     "bool sort_vector < T >::"
			     "bin_search(const T & x, lidia_size_t & pos, int (*cmp)(const T & a, const T & b),"
			     "lidia_size_t l,lidia_size_t r) const",
			     DM_SV, LSV_ERROR[11]);

  /* pick center element */
  pos = (l + r) / 2;
  while (l <= r)
    {
      c = cmp(x, data[pos]);
      if (c == 0)
	{
	  /* search for first occurence of x */
	  for (;pos > 0 && cmp(x, data[pos - 1]) == 0; pos--);
	  return true;
	}
      else 
	if (c < 0)        /* x must occur before data[pos] */
	  r = pos - 1;    /* cont. search in data[l],...,data[pos-1] */
	else              /* x must occur after data[pos] */
	  l = pos + 1;    /* cont. search in data[pos+1],...,data[r] */
      pos = (l + r) / 2;
    }
  pos = r + 1;  
  return false;
}

template < class T >
bool sort_vector < T >::
bin_search(const T & x, lidia_size_t & pos, char sort_direction, lidia_size_t l, lidia_size_t r) const
{
  debug_handler_l(DM_SV, "in member - function "
		  "bin_search(const T &, lidia_size_t, char, lidia_size_t, lidia_size_t)", DV_SV + 2);
  
  register bool rc = false;

  if (sort_direction == SORT_VECTOR_DEF) sort_direction = sort_dir ;
  if (r == -1) r = length - 1;

  if ( length == 0 && l == 0 && r == -1 )  /* empty vector and default-cut */
    {
      pos = 0;        
      return false;
    }

  if (l < 0 || l >= length || r < 0 || r >= length || l > r)
    lidia_error_handler_para (l, "l", "0 <= l < length",
			      r, "r", "0 <= r < length",
			      "bool sort_vector < T >::"
			      "bin_search(const T & x, lidia_size_t & pos, char sort_direction, lidia_size_t l,"
			      "lidia_size_t r) const",
			      DM_SV, LSV_ERROR[11]);
  
  switch (sort_direction)
    {
    case SORT_VECTOR_UP:             /* vector is sorted in ascending order */
      rc = bin_search_up(x, pos, l, r);
      break;

    case SORT_VECTOR_DOWN:           /* vector is sorted in descending order */
      rc = bin_search_down(x, pos, l, r);
      break;
      
    case SORT_VECTOR_CMP: 
      rc = bin_search(x, pos, el_cmp, l, r);
      break;
      
    default: 
      lidia_error_handler_para(sort_direction, "sort_direction", "sort_direction in "
			       "{SORT_VECTOR_UP, SORT_VECTOR_DOWN, SORT_VECTOR_CMP}",
			       "bool sort_vector < T >::"
			       "bin_search(const T & x, lidia_size_t &pos, char sort_direction, "
			       "lidia_size_t l, lidia_size_t r) const",
			       DM_SV, LSV_ERROR[10]);
    }
  return rc;
}

template < class T >
bool sort_vector < T >::
bin_search_up(const T & x, lidia_size_t & pos, lidia_size_t l, lidia_size_t r) const
{
  debug_handler_l(DM_SV, "in member - function "
		  "bin_search_up(const T &, lidia_size_t &, lidia_size_t, lidia_size_t)", DV_SV + 2);
  
  /* pick center element */
  pos = (l + r) / 2;
  while (l <= r)
    {
      if (comparator< T >::less_equal(x, data[pos]))
	if (comparator< T >::equal(data[pos], x))
	  {
	    while (pos > 0 && comparator< T >::equal(data[pos-1], x))
	      pos--;
	    
	    return true;
	  }
	else                          /* x < data[pos] */
	  r = pos - 1;                /* cont. search in data[l],...,data[pos-1] */
      else                            /* x > data[pos] */
	l = pos + 1;                  /* cont. search in data[pos+1],...,data[r] */
      pos = (l + r) / 2;
    }
  
  pos = r + 1;
  return false;
}

template < class T >
bool sort_vector < T >:: 
bin_search_down(const T & x, lidia_size_t & pos, lidia_size_t l, lidia_size_t r) const
{
  debug_handler_l(DM_SV, "in member - function "
		  "bin_search_down(const T &, lidia_size_t &, lidia_size_t, lidia_size_t)", DV_SV + 2);
  
  pos = (l + r) / 2;
  
  while (l <= r)
    {
      if (comparator< T >::greater_equal(x, data[pos]))
	if (comparator< T >::equal(data[pos], x))
	  {
	    while (pos > 0 && comparator< T >::equal(data[pos - 1], x))
	      pos--;
	      
	    return true;
	  }
	else 
	  r = pos - 1;   /* cont. search in data[l],...,data[pos-1] */
      else   
	l = pos + 1;     /* cont. search in data[pos+1],...,data[r] */
      pos = ( l + r ) / 2 ;
    }
  pos = r + 1 ;
  return false ;
}

/**
 ** insertion into a sorted vector  
 **/

template < class T >
void sort_vector < T >::
insert(const T & x, int (*cmp)(const T & a, const T & b), lidia_size_t l, lidia_size_t r)
{
  debug_handler_l(DM_SV, "in member - function "
		  "insert(const T &, int (*cmp)(const T &, const T &), lidia_size_t, lidia_size_t)", 
		  DV_SV + 3);

  lidia_size_t pos, i, found;

  r = (r == -1) ? length - 1 : r;
  if (length == 0 && l == 0 && r == -1)  /* empty vector and default-cut */
    {
      set_size(1);
      data[0] = x;
      return;
    }
  
  if (l < 0 || l >= length || r < 0 || r >= length || l > r)
    lidia_error_handler_para(l, "l", "0 <= l < length",
			     r, "r", "0 <= r < length and r >= l",
			     "void sort_vector < T >::"
			     "insert(const T & x, int (*cmp)(const T & a, const T & b), lidia_size_t l, "
			     "lidia_size_t r)",
			     DM_SV, LSV_ERROR[11]);
  
  /* examine, where to put element x */
  found = bin_search(x, pos, cmp, l, r);
  set_size(length + 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;
}

template < class T >
void sort_vector < T >::
insert(const T & x, char sort_direction, lidia_size_t l, lidia_size_t r)
{
  debug_handler_l(DM_SV, "in member - function "
		  "insert(const T &, char, lidia_size_t, lidia_size_t)", DV_SV + 3);

  register lidia_size_t i;
  register bool found;
  lidia_size_t pos;
  
  r = (r == -1) ? length - 1 : r;
  if (length == 0 && l == 0 && r == -1)  /* empty vector and default-cut */
    {
      set_size(1);
      data[0] = x;
      return;
    }
  
  if (l < 0 || l >= length || r < 0 || r >= length || l > r)
    lidia_error_handler_para(l, "l", "0 <= l < length",
			     r, "r", "0 <= r < length and r >= l",
			     "void sort_vector < T >::"
			     "insert(const T & x, char sort_direction, lidia_size_t l, lidia_size_t r)", 
			     DM_SV , LSV_ERROR[11]);
  
  /* examine, where to put element x */
  found = bin_search(x, pos, sort_direction, l, r);
  set_size(length + 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;
}

/**
 ** deleting  elements
 **/

template < class T >
bool sort_vector < T >:: 
remove(const T & x, int (*cmp)(const T & a, const T & b), lidia_size_t l, lidia_size_t r)
{
  debug_handler_l(DM_SV, "in member - function "
		  "remove ( T, cmp, lidia_size_t, lidia_size_t )", DV_SV + 4);

  register lidia_size_t i; 
  register bool found;
  lidia_size_t pos;

  r = (r == -1) ? length - 1 : r;
  if ( l < 0 || l >= length || r < 0 || r >= length || l > r )
    lidia_error_handler_para(l, "l", "0 <= l < length",
			     r, "r", "0 <= r < length and l <= r",
			     "bool sort_vector < T >::"
			     "remove(const T & x, int (*cmp)(const T & a, const T & b), lidia_size_t l, "
			     "lidia_size_t r)",
			     DM_SV, LSV_ERROR[13]);

  /* determine first occurence of x in the vector */
  found = bin_search(x, pos, cmp, l, r);

  /* if x could be found, remove it from position 'pos' */
  if (found)
    {
      for (i = pos; i < length-1; i++)
	data[i] = data[i + 1];
      set_size(length - 1);
      return true;
    }
  else
    return false;
}

template < class T >
bool sort_vector < T >::
remove(const T &x, char sort_direction, lidia_size_t l, lidia_size_t r)
{
  debug_handler_l(DM_SV, "in member - function "
		  "remove(const T &, char, lidia_size_t, lidia_size_t)", DV_SV + 4);

  register lidia_size_t i; 
  register bool found;
  lidia_size_t pos;

  r = (r == -1) ? length - 1 : r;
  if ( l < 0 || l >= length || r < 0 || r >= length || l > r )
    lidia_error_handler_para(l, "l", "0 <= l < length",
			     r, "r", "0 <= r < length and l <= r",
			     "bool sort_vector < T >::"
			     "remove(const T &x, char sort_direction, lidia_size_t l, lidia_size_t r)",
			     DM_SV, LSV_ERROR[13]);

  /* determine first occurence of x in the vector */
  found = bin_search(x, pos, sort_direction, l, r);

  /* if x could be found, remove it from position 'pos' */
  if (found)
    {
      for (i = pos; i < length - 1; i++)
	data[i] = data[i + 1];
      set_size(length - 1);
      return true;
    }
  else
    return false;
}

/**
 ** lex. compare
 **/

template < class T >
int sort_vector < T >::
lex_compare(sort_vector < T > &w) const 
{
  debug_handler_l(DM_SV, "in member - function "
		  "lex_compare(sort_vector < T > &)", DV_SV + 5);
  
  register lidia_size_t i, minl = (length < w.length ) ? length : w.length;
  
  for (i = 0; i < minl && comparator < T >::equal(data[i], w.data[i]); i++);
  if (i < minl)
    if (comparator < T >::less_equal(data[i], w.data[i])) 
      return (comparator < T >::equal(data[i], w.data[i])) ? 0 : -1;
    else
      return 1;
  else
    if (i < w.length) 
      return -1;
    else
      return (i < length) ? 1 : 0 ;         
}

/** 
 ** delete_copies:
 ** removes the copies of elements of this;
 ** assumes, that the vector is sorted;
 **/

template < class T >
void sort_vector < T >::
delete_copies()
{
  debug_handler_l(DM_SV, "in member - function "
		  "delete_copies()", DV_SV + 6);
  
  register lidia_size_t i, j;
  int ident;

  for (i = 0, j = 0; i < length; j++)
    {
      data[j] = data[i];
      i++;
      ident = 1;
      while ( i < length && ident)
	{
	  ident = comparator < T >::equal(data[j],data[i]);
	  i++; 
	}
      if (!ident) 
	i--;
    }
  length = j;
}

#undef DV_SV 
#undef DM_SV
#undef LSV_ERROR 

