/* Copyright (C) 2021 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

#ifndef _PERFAN_VEC_H
#define _PERFAN_VEC_H

#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>

// This package implements a vector of items.

#define Destroy(x)      if (x) { (x)->destroy(); delete (x); (x) = NULL; }
#define VecSize(x)      ((x) ? (x)->size() : 0)

void destroy (void *vec); // Free up the "two-dimension" Vectors

typedef int (*CompareFunc)(const void*, const void*);
typedef int (*ExtCompareFunc)(const void*, const void*, const void*);
typedef int (*SearchFunc)(char*, char*);

extern "C"
{
  typedef int (*StdCompareFunc)(const void*, const void*);
}

enum Search_type
{
  LINEAR,
  BINARY,
  HASH
};

enum Direction
{
  FORWARD,
  REVERSE
};

enum VecType
{
  VEC_VOID = 0,
  VEC_INTEGER,
  VEC_CHAR,
  VEC_BOOL,
  VEC_DOUBLE,
  VEC_LLONG,
  VEC_VOIDARR,
  VEC_STRING,
  VEC_INTARR,
  VEC_BOOLARR,
  VEC_LLONGARR,
  VEC_STRINGARR,
  VEC_DOUBLEARR
};

template <class ITEM> void
qsort (ITEM *, size_t, ExtCompareFunc, void *);

template <typename ITEM> class Vector
{
public:

  Vector ()
  {
    count = 0;
    data = NULL;
    limit = 0;
    sorted = false;
  };

  Vector (long sz);

  virtual
  ~Vector ()
  {
    free (data);
  }

  void append (const ITEM item);
  void addAll (Vector<ITEM> *vec);
  Vector<ITEM> *copy ();        // Return a copy of "this".

  ITEM
  fetch (long index)
  {
    return data[index];
  }

  ITEM
  get (long index)
  {
    return data[index];
  }

  // Return the first index in "this" that equals "item".
  // Return -1 if "item" is not found.
  long find (const ITEM item);
  long find_r (const ITEM item);

  // Insert "item" into "index"'th slot of "this",
  // moving everything over by 1.
  void insert (long index, const ITEM item);

  // Insert "item" after locating its appropriate index
  void incorporate (const ITEM item, CompareFunc func);

  // Remove and return the "index"'th item from "this",
  // moving everything over by 1.
  ITEM remove (long index);

  // Swap two items in "this",
  void swap (long index1, long index2);

  long
  size ()
  {
    return count;
  }

  // Store "item" into the "index"'th slot of "this".
  void store (long index, const ITEM item);

  void
  put (long index, const ITEM item)
  {
    store (index, item);
  }

  // Sort the vector according to compare
  void
  sort (CompareFunc compare, void *arg = NULL)
  {
    qsort (data, count, (ExtCompareFunc) compare, arg);
    sorted = true;
  }

  // Binary search, vector must be sorted
  long bisearch (long start, long end, void *key, CompareFunc func);
  void destroy ();      // delete all vector elements (must be pointers!)

  void
  reset ()
  {
    count = 0;
    sorted = false;
  }

  bool
  is_sorted ()
  {
    return sorted;
  }

  virtual VecType
  type ()
  {
    return VEC_VOID;
  }

  virtual void
  dump (const char * /* msg */)
  {
    return;
  }

private:

  void resize (long index);

  ITEM *data;   // Pointer to data vector
  long count;   // Number of items
  long limit;   // Vector length (power of 2)
  bool sorted;
};

template<> VecType Vector<int>::type ();
template<> VecType Vector<unsigned>::type ();
template<> VecType Vector<char>::type ();
template<> VecType Vector<bool>::type ();
template<> VecType Vector<double>::type ();
template<> VecType Vector<long long>::type ();
template<> VecType Vector<uint64_t>::type ();
template<> VecType Vector<void*>::type ();
template<> VecType Vector<char*>::type ();
template<> VecType Vector<Vector<int>*>::type ();
template<> VecType Vector<Vector<char*>*>::type ();
template<> VecType Vector<Vector<long long>*>::type ();
template<> void Vector<char *>::destroy ();

#define KILOCHUNK   1024
#define MEGACHUNK   1024*1024
#define GIGACHUNK   1024*1024*1024

// A standard looping construct:
#define Vec_loop(ITEM, vec, index, item) \
if (vec != NULL) \
    for (index = 0, item = ((vec)->size() > 0) ? (vec)->fetch(0) : (ITEM)0; \
	 index < (vec)->size(); \
	 item = (++index < (vec)->size()) ? (vec)->fetch(index) : (ITEM)0)

template <typename ITEM>
Vector<ITEM>::Vector (long sz)
{
  count = 0;
  limit = sz > 0 ? sz : KILOCHUNK; // was 0;
  data = limit ? (ITEM *) malloc (sizeof (ITEM) * limit) : NULL;
  sorted = false;
}

template <typename ITEM> void
Vector<ITEM>
::resize (long index)
{
  if (index < limit)
    return;
  if (limit < 16)
    limit = 16;
  while (index >= limit)
    {
      if (limit > GIGACHUNK)
	limit += GIGACHUNK; // Deoptimization for large experiments
      else
	limit = limit * 2;
    }
  data = (ITEM *) realloc (data, limit * sizeof (ITEM));
}

template <typename ITEM> void
Vector<ITEM>::append (const ITEM item)
{
  // This routine will append "item" to the end of "this".
  if (count >= limit)
    resize (count);
  data[count++] = item;
}

template <typename ITEM> void
Vector<ITEM>::addAll (Vector<ITEM> *vec)
{
  if (vec)
    for (int i = 0, sz = vec->size (); i < sz; i++)
      append (vec->fetch (i));
}

template <typename ITEM> Vector<ITEM> *
Vector<ITEM>::copy ()
{
  // This routine will return a copy of "this".
  Vector<ITEM> *vector;
  vector = new Vector<ITEM>;
  vector->count = count;
  vector->limit = limit;
  vector->data = (ITEM *) malloc (sizeof (ITEM) * limit);
  (void) memcpy ((char *) vector->data, (char *) data, sizeof (ITEM) * count);
  return vector;
}

template <typename ITEM> long
Vector<ITEM>::find (const ITEM match_item)
{
  for (long i = 0; i < size (); i++)
    if (match_item == get (i))
      return i;
  return -1;
}

template <typename ITEM> long
Vector<ITEM>::find_r (const ITEM match_item)
{
  for (long i = size () - 1; i >= 0; i--)
    if (match_item == get (i))
      return i;
  return -1;
}

template <typename ITEM> void
Vector<ITEM>::insert (long index, const ITEM item)
{
  // This routine will insert "item" into the "index"'th slot of "this".
  // An error occurs if "index" > size().
  // "index" is allowed to be equal to "count" in the case that
  // you are inserting past the last element of the vector.
  // In that case, the bcopy below becomes a no-op.
  assert (index >= 0);
  assert (index <= count);
  append (item);
  (void) memmove (((char *) (&data[index + 1])), (char *) (&data[index]),
		  (count - index - 1) * sizeof (ITEM));
  data[index] = item;
}

template <typename ITEM> ITEM
Vector<ITEM>::remove (long index)
{
  // This routine will remove the "index"'th item from "this" and
  // return it.  An error occurs if "index" >= size();.
  assert (index >= 0);
  assert (index < count);
  ITEM item = data[index];
  for (long i = index + 1; i < count; i++)
    data[i - 1] = data[i];
  count--;
  // Bad code that works good when ITEM is a pointer type
  data[count] = item;
  return data[count];
}

template <typename ITEM> void
Vector<ITEM>::swap (long index1, long index2)
{
  ITEM item;
  item = data[index1];
  data[index1] = data[index2];
  data[index2] = item;
}

template <typename ITEM> void
Vector<ITEM>::store (long index, const ITEM item)
{
  if (index >= count)
    {
      resize (index);
      memset (&data[count], 0, (index - count) * sizeof (ITEM));
      count = index + 1;
    }
  data[index] = item;
}

// This routine performs a binary search across
// the entire vector, with "start" being the low boundary.
// It is assumed that the vector is SORTED in
// ASCENDING ORDER by the same criteria as the
// compare function.
// If no match is found, -1 is returned.
template <typename ITEM> long
Vector<ITEM>::bisearch (long start, long end, void *key, CompareFunc compare)
{
  ITEM *itemp;
  if (end == -1)
    end = count;
  if (start >= end)
    return -1; // start exceeds limit
  itemp = (ITEM *) bsearch ((char *) key, (char *) &data[start],
			  end - start, sizeof (ITEM), (StdCompareFunc) compare);
  if (itemp == (ITEM *) 0)
    return -1; // not found
  return (long) (itemp - data);
}

template <typename ITEM> void
Vector<ITEM>::incorporate (const ITEM item, CompareFunc compare)
{
  long lt = 0;
  long rt = count - 1;
  while (lt <= rt)
    {
      long md = (lt + rt) / 2;
      if (compare (data[md], item) < 0)
	lt = md + 1;
      else
	rt = md - 1;
    }
  if (lt == count)
    append (item);
  else
    insert (lt, item);
}

#define QSTHRESH 6

template <typename ITEM> void
qsort (ITEM *base, size_t nelem, ExtCompareFunc qcmp, void *arg)
{
  for (;;)
    {
      // For small arrays use insertion sort
      if (nelem < QSTHRESH)
	{
	  for (size_t i = 1; i < nelem; i++)
	    {
	      ITEM *p = base + i;
	      ITEM *q = p - 1;
	      if (qcmp (q, p, arg) > 0)
		{
		  ITEM t = *p;
		  *p = *q;
		  while (q > base && qcmp (q - 1, &t, arg) > 0)
		    {
		      *q = *(q - 1);
		      --q;
		    }
		  *q = t;
		}
	    }
	  return;
	}

      ITEM *last = base + nelem - 1;
      ITEM *mid = base + nelem / 2;
      // Sort the first, middle, and last elements
      ITEM *a1 = base, *a2, *a3;
      if (qcmp (base, mid, arg) > 0)
	{
	  if (qcmp (mid, last, arg) > 0)
	    { // l-m-b
	      a2 = last;
	      a3 = last;
	    }
	  else if (qcmp (base, last, arg) > 0)
	    { // l-b-m
	      a2 = mid;
	      a3 = last;
	    }
	  else
	    { // m-b-l
	      a2 = mid;
	      a3 = mid;
	    }
	}
      else if (qcmp (mid, last, arg) > 0)
	{
	  a1 = mid;
	  a3 = last;
	  if (qcmp (base, last, arg) > 0)  // m-l-b
	    a2 = base;
	  else  // b-l-m
	    a2 = a3;
	}
      else // b-m-l
	a3 = a2 = a1;
      if (a1 != a2)
	{
	  ITEM t = *a1;
	  *a1 = *a2;
	  if (a2 != a3)
	    *a2 = *a3;
	  *a3 = t;
	}

      // Partition
      ITEM *i = base + 1;
      ITEM *j = last - 1;
      for (;;)
	{
	  while (i < mid && qcmp (i, mid, arg) <= 0)
	    i++;
	  while (j > mid && qcmp (mid, j, arg) <= 0)
	    j--;
	  if (i == j)
	    break;
	  ITEM t = *i;
	  *i = *j;
	  *j = t;
	  if (i == mid)
	    {
	      mid = j;
	      i++;
	    }
	  else if (j == mid)
	    {
	      mid = i;
	      j--;
	    }
	  else
	    {
	      i++;
	      j--;
	    }
	}

      // Compare two partitions. Do the smaller one by recursion
      // and loop over the larger one.
      size_t nleft = mid - base;
      size_t nright = nelem - nleft - 1;
      if (nleft <= nright)
	{
	  qsort (base, nleft, qcmp, arg);
	  base = mid + 1;
	  nelem = nright;
	}
      else
	{
	  qsort (mid + 1, nright, qcmp, arg);
	  nelem = nleft;
	}
    }
}

template<> inline void
Vector<char*>::destroy ()
{
  for (long i = 0; i < count; i++)
    free (data[i]);
  count = 0;
}

template <typename ITEM> inline void
Vector<ITEM>::destroy ()
{
  for (long i = 0; i < count; i++)
    delete data[i];
  count = 0;
}

#endif /* _VEC_H */
