// This may look like C code, but it is really -*- C++ -*-
/* 
Copyright (C) 1988, 1992, 1993 Free Software Foundation
    originally written by Doug Lea (dl@rocky.oswego.edu)

This file is part of the GNU C++ Library.  This library is free
software; you can redistribute it and/or modify it under the terms of
the GNU Library General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.  This library 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifdef __GNUG__
#pragma implementation
#endif
#include <stream.h>
#include <builtin.h>
#include "Vec.h"

// error handling


void default_Vec_error_handler(const char* msg)
{
  cerr << "Fatal Vec error. " << msg << "\n";
  exit(1);
}

one_arg_error_handler_t Vec_error_handler = default_Vec_error_handler;

one_arg_error_handler_t set_Vec_error_handler(one_arg_error_handler_t f)
{
  one_arg_error_handler_t old = Vec_error_handler;
  Vec_error_handler = f;
  return old;
}

void BaseVec::error(const char* msg)
{
  (*Vec_error_handler)(msg);
}

void BaseVec::range_error()
{
  (*Vec_error_handler)("Index out of range.");
}

#ifdef TODO
<T>Vec::<T>Vec(<T>Vec& v)
{
  s = new <T> [len = v.len];
  <T>* top = &(s[len]);
  <T>* t = s;
  <T>* u = v.s;
  while (t < top) *t++ = *u++;
}

<T>Vec::<T>Vec(int l, <T&> fill_value)
{
  s = new <T> [len = l];
  <T>* top = &(s[len]);
  <T>* t = s;
  while (t < top) *t++ = fill_value;
}
#endif

void BaseVec::assign(const BaseVec& v)
{
  if (this != &v)
  {
    delete [] s;
    len = v.len;
    int item_size = item_size();
    s = new char [len * item_size];
    char * top = (char*)s + len * item_size;
    char * t = (char*)s;
    char * u = (char*)v.s;
    while (t < top)
      {
	init_item(t, u);
	t += item_size;
	u += item_size;
      }
  }
  return *this;
}

#ifdef TODO
void <T>Vec::apply(<T>Procedure f)
{
  <T>* top = &(s[len]);
  <T>* t = s;
  while (t < top) (*f)(*t++);
}

// can't just realloc since there may be need for constructors/destructors
void <T>Vec::resize(int newl)
{
  <T>* news = new <T> [newl];
  <T>* p = news;
  int minl = (len < newl)? len : newl;
  <T>* top = &(s[minl]);
  <T>* t = s;
  while (t < top) *p++ = *t++;
  delete [] s;
  s = news;
  len = newl;
}

<T>Vec concat(<T>Vec & a, <T>Vec & b)
{
  int newl = a.len + b.len;
  <T>* news = new <T> [newl];
  <T>* p = news;
  <T>* top = &(a.s[a.len]);
  <T>* t = a.s;
  while (t < top) *p++ = *t++;
  top = &(b.s[b.len]);
  t = b.s;
  while (t < top) *p++ = *t++;
  return <T>Vec(newl, news);
}


<T>Vec combine(<T>Combiner f, <T>Vec& a, <T>Vec& b)
{
  int newl = (a.len < b.len)? a.len : b.len;
  <T>* news = new <T> [newl];
  <T>* p = news;
  <T>* top = &(a.s[newl]);
  <T>* t = a.s;
  <T>* u = b.s;
  while (t < top) *p++ = (*f)(*t++, *u++);
  return <T>Vec(newl, news);
}

<T> <T>Vec::reduce(<T>Combiner f, <T&> base)
{
  <T> r = base;
  <T>* top = &(s[len]);
  <T>* t = s;
  while (t < top) r = (*f)(r, *t++);
  return r;
}

<T>Vec reverse(<T>Vec& a)
{
  <T>* news = new <T> [a.len];
  if (a.len != 0)
  {
    <T>* lo = news;
    <T>* hi = &(news[a.len - 1]);
    while (lo < hi)
    {
      <T> tmp = *lo;
      *lo++ = *hi;
      *hi-- = tmp;
    }
  }
  return <T>Vec(a.len, news);
}

void BaseVec::reverse()
{
  if (len != 0)
  {
    int item_size = sizeof_item();
    char* lo = s;
    char* hi = s + (len - 1) * item_size;
    while (lo < hi)
    {
#ifdef __GNUC__
      char tmp[item_size];
#else
      char *tmp = (char*)alloca(item_size);
#endif
      init_item(tmp, lo);
      assign_item(lo, hi);
      assign_item(hi, tmp);
      lo += item_size;
      hi -= item_size;
      /* FIXME:  Destroy tmp */
    }
  }
}

int <T>Vec::index(<T&> targ)
{
  for (int i = 0; i < len; ++i) if (<T>EQ(targ, s[i])) return i;
  return -1;
}

<T>Vec map(<T>Mapper f, <T>Vec& a)
{
  <T>* news = new <T> [a.len];
  <T>* p = news;
  <T>* top = &(a.s[a.len]);
  <T>* t = a.s;
  while(t < top) *p++ = (*f)(*t++);
  return <T>Vec(a.len, news);
}

int operator == (<T>Vec& a, <T>Vec& b)
{
  if (a.len != b.len)
    return 0;
  <T>* top = &(a.s[a.len]);
  <T>* t = a.s;
  <T>* u = b.s;
  while (t < top) if (!(<T>EQ(*t++, *u++))) return 0;
  return 1;
}

void <T>Vec::fill(<T&> val, int from, int n)
{
  int to;
  if (n < 0)
    to = len - 1;
  else
    to = from + n - 1;
  if ((unsigned)from > (unsigned)to)
    range_error();
  <T>* t = &(s[from]);
  <T>* top = &(s[to]);
  while (t <= top) *t++ = val;
}

<T>Vec <T>Vec::at(int from, int n)
{
  int to;
  if (n < 0)
  {
    n = len - from;
    to = len - 1;
  }
  else
    to = from + n - 1;
  if ((unsigned)from > (unsigned)to)
    range_error();
  <T>* news = new <T> [n];
  <T>* p = news;
  <T>* t = &(s[from]);
  <T>* top = &(s[to]);
  while (t <= top) *p++ = *t++;
  return <T>Vec(n, news);
}

<T>Vec merge(<T>Vec & a, <T>Vec & b, <T>Comparator f)
{
  int newl = a.len + b.len;
  <T>* news = new <T> [newl];
  <T>* p = news;
  <T>* topa = &(a.s[a.len]);
  <T>* as = a.s;
  <T>* topb = &(b.s[b.len]);
  <T>* bs = b.s;

  for (;;)
  {
    if (as >= topa)
    {
      while (bs < topb) *p++ = *bs++;
      break;
    }
    else if (bs >= topb)
    {
      while (as < topa) *p++ = *as++;
      break;
    }
    else if ((*f)(*as, *bs) <= 0)
      *p++ = *as++;
    else
      *p++ = *bs++;
  }
  return <T>Vec(newl, news);
}

static int gsort(<T>*, int, <T>Comparator);
 
void <T>Vec::sort (<T>Comparator compar)
{
  gsort(s, len, compar);
}


// Based on msort, written by Mike Haertel, September 1988, the GNU C library.

//#include <stdlib.h>
//#include <string.h>

typedef int (*compare_func)(void *, void*);
static void
msort_with_tmp (b, n, cmp, t)
     void **b;
     size_t n;
     compare_func cmp;
     void **t;
{
  void **tmp;
  void **b1, **b2;
  size_t n1, n2;

  if (n <= 2)
    {
      if (n == 2 && (*cmp) (b[0], b[1]) > 0)
	{
	  void *tmp = b[0]; b[0] = b[1]; b[1] = tmp;
	}
      return;
    }

  n1 = n / 2;
  n2 = n - n1;
  b1 = b;
  b2 = b + n1;

  msort_with_tmp (b1, n1, cmp, t);
  msort_with_tmp (b2, n2, cmp, t);

  tmp = t;

  while (n1 > 0 && n2 > 0)
    {
      if ((*cmp) (*b1, *b2) <= 0)
	{
	  *tmp++ = *b1++;
	  --n1;
	}
      else
	{
	  *tmp++ = *b2++;
	  --n2;
	}
    }
  for ( ; n1 > 0; n1--)
      *tmp++ = *b1++;
  for ( ; n2 > 0; n2--)
      *tmp++ = *b2++;
  for ( ; n > 0; n--)
      *b++ = *t++;
}

static void BaseVec::sort(BaseComparator cmp)
{
  size_t el_size = sizeof_item();
  size_t total_elems = len;
  void **pointers, **temp;
  void *swap_tmp;
  size_t i;
  i = sizeof(void*) > sizeof(size_t) ? sizeof(void*) : sizeof(size_t);
  i *= totals_elems;
  if (i < el_size)
    i = el_size;
#define MANY (total_elems > 200)
  if (MANY)
    {
      pointers = (void*)malloc(i);
      temp = (void*)malloc(i);
    }
  else
    {
      pointers = (void*)alloca(i);
      temp = (void*)alloca(i);
    }
  if (pointer == NULL || temp == NULL)
    {
      abort();
    }
  size_t *indexes = (size_t*)temp;
  for (i = 0; i < total_elems; i++)
    pointers[i] = (void*)((char*)s + i * el_size);
  
  msort_with_tmp (pointers, total_elems, cmp, temp);

  /* convert pointer to index */
  for (i = 0; i < total_elems; i++)
    indexes[i] = ((char*)pointers[i] - (char*)s)/el_size;

  swap_tmp = (void*)pointers;

  /* Now finally move the elements into their final places. */
  for (i = 0; i < total_length; i++)
    {
      if (indexes[i] != i)
	{
	  /* Move the ending i'th element into place, after saving the
	     original i'th element */
	  // *(T*)swap_tmp = base_ptr[i]; -- using T::T
	  init_item(swap_tmp, (char*)s + i * el_size);
	  size_t j = i;
	  size_t next_j = indexes[j];
	  while (next_j != i)
	    {
	      // base_ptr[j] = base_ptr[next_j]; -- using T::operator= 
	      assign_item ((char*)s + j * el_size,
			   (char*)s + next_j * el_size);
	      j = next_j;
	      next_j = indexes[j];
	      /* This is to mark j as no longer needing moving */
	      indexes[j] = j;
	    }
	  // base_ptr[j] = *(T*)swap_tmp; -- using T::operator=
	  assign_item ((char*)s + j * el_size, swap_tmp);
	  // destroy *(T*)swap_tmp; -- using T::~T
	  destroy_item(swap_tmp);
	}
    }

  if (MANY)
    {
      free (pointers);
      free (temp);
    }
}
