/*
** mg_vec.c for  in 
** 
** Made by vianney rancurel
** Login   <vianney@epita.fr>
** 
** Started on  Wed Aug 25 12:13:21 1999 vianney rancurel
** Last update Thu Oct 28 20:15:32 1999 
*/
#include "mg.h"

/* allocates at least n elts.
   Should not be called directly.
   Returns 0 or various errors from alloc_procs */
t_status		vec_alloc(vec,n)
t_vec			*vec;
int			n;
{
  t_status		status;	 
  size_t		real_size;
  VOID_PTR		ptr;
  
  if (n < vec->base)
    n = vec->base;
  if ((status = vec->alloc_algorithm_proc(vec->allocated * sizeof (VOID_PTR),
					  n * sizeof (VOID_PTR),
					  &real_size)) != 0)
    return (status);
  if (vec->elts == NULL)
    {
      if ((ptr = vec->alloc_proc(real_size,
				 vec->comment,
				 "vec_alloc:elts",
				 &status)) == NULL)
	return (status);
    }
  else
    {
      if ((ptr = vec->realloc_proc(vec->elts,
				   real_size,
				   vec->comment,
				   "vec_alloc:elts",
				   &status)) == NULL)
	return (status);
    }
  vec->elts = ptr;
  vec->allocated = real_size / sizeof (VOID_PTR);
  return (0);
}

/* instantiates a new vector.
   A vector (t_vec) is a container,
   by opposition to an array (t_arr), it stores reference of objects
   instead of storing objects themselves.
   The now parameter allows the user to speculate on the fact that elts
   will never be "really" allocated, by example empty slots of a hash table.
   Returns a new vector structure, if NULL status is filled */
t_vec			*vec_new(base,
				 now,
				 alloc_algorithm_proc,
				 alloc_proc,
				 realloc_proc,
				 free_proc,
				 comment,
				 status)
int			base;			/* Base allocation (in units)*/
t_boolean		now;			/* Allocate base elts now */
t_alloc_algorithm_proc	alloc_algorithm_proc;	/* Alloc. strategy */
t_alloc_proc		alloc_proc;		/* Alloc method */
t_realloc_proc		realloc_proc;		/* Realloc method */
t_free_proc		free_proc;		/* Free method */
char			*comment;		/* Major comment */
t_status		*status;		/* Filled if NULL */
{
  t_vec			*vec;

  if ((vec = alloc_proc(sizeof (t_vec),
			comment,
			"vec_new:vec",
			status)) == NULL)
    return (NULL);
  vec->base = base;
  vec->elts = NULL;
  vec->count = 0;
  vec->allocated = 0;
  vec->alloc_algorithm_proc = alloc_algorithm_proc;
  vec->alloc_proc = alloc_proc; 
  vec->realloc_proc = realloc_proc;
  vec->free_proc = free_proc;
  vec->comment = comment;
  if (now)
    if (((*status) = vec_alloc(vec,base)) != 0)
      {
	vec->free_proc(vec,
		       comment,
		       "vec_new:vec");
	return (NULL);
      }
  return (vec);
}

/* destroys the elts but not the vector structure.
   After this action, the vector is empty and reusable */
VOID_FUNC		vec_destroy(vec)
t_vec			*vec;
{
  if (vec->elts)
    vec->free_proc(vec->elts,
		   vec->comment,
		   "*:elts");
  vec->elts = NULL;
  vec->count = 0;
  vec->allocated = 0;
}

/* destroys the elts and the vector structure */
VOID_FUNC		vec_delete(vec)
t_vec			*vec;
{
  vec_destroy(vec);
  vec->free_proc(vec,
		 vec->comment,
		 "*:vec");
}

/* adds an elt.
   Returns 0 if OK. Might return various errors */
t_status		vec_add(vec,elt)
t_vec			*vec;
VOID_PTR		elt;
{
  t_status		status;

  if (vec->allocated < (vec->count + 1))
    if ((status = vec_alloc(vec,vec->count + 1)) != 0)
      return (status);
  vec->elts[vec->count++] = elt;
  return (0);
}

/* "remove" the elt sepecified by index.
   Elt is not really removed but elts are moved backwards. 
   Note that there is no check of the validity of idx */
VOID_FUNC		vec_rm(vec,idx)
t_vec			*vec;
int			idx;
{
  FBCOPY(vec->elts + idx + 1,
	 vec->elts + idx,
	 (vec->count - idx - 1) * sizeof (VOID_PTR));
  vec->count--;
}

/* returns the index of elt, might return -1 if not found */
int			vec_index(vec,elt)
t_vec			*vec;
VOID_PTR		elt;
{
  int			idx;
  
  idx = 0;
  while (idx < vec->count)
    {
      if (vec->elts[idx] == elt)
	return (idx);
      idx++;
    }
  return (-1);
}

/* reverses a vector.
   elt0 becomes eltn, elt1 become eltn-1, the possible central elt is
   of course unchanged */
VOID_FUNC		vec_reverse(vec)
t_vec			*vec;
{
  VEC_FOR(vec,VOID_PTR elt)
    {
      VOID_PTR		tmp;

      if (VEC_IDX >= (VEC_COUNT(vec) - VEC_IDX - 1))
	break ;
      tmp = VEC_AT(vec,VEC_IDX);
      VEC_AT(vec,VEC_IDX) = VEC_AT(vec,VEC_COUNT(vec) - VEC_IDX - 1);
      VEC_AT(vec,VEC_COUNT(vec) - VEC_IDX - 1) = tmp;
    }
  VEC_ENDFOR;
}

#ifdef HAVE_QSORT
/* sorts a vector in using qsort(3). */
VOID_FUNC		vec_sort(vec,cmp)
t_vec			*vec;
t_vec_cmp_proc		cmp;
{
  qsort(vec->elts,vec->count,sizeof (VOID_PTR),cmp);
}
#endif

#ifdef DEBUG
/* shows a vector structure.
   This is a debug function. */
VOID_FUNC		vec_show(vec)
t_vec			*vec;
{
  fprintf(stderr,"base=%d\n",vec->base);
  fprintf(stderr,"count=%d\n",vec->count);
  fprintf(stderr,"allocated=%d\n",vec->allocated);
  fprintf(stderr,"elts=%p\n",vec->elts);
}
#endif
