/* Returns the smallest |i| such that |array[i] == key|,
   or |-1| if |key| is not in |array[]|.
   |array[]| must be an array of |n int|s. */
int
seq_search (int array[], int n, int key)
{
  int i;

  for (i = 0; i < n; i++)
    if (array[i] == key)
      return i;
  return -1;
}

/* Returns the smallest |i| such that |array[i] == key|,
   or |-1| if |key| is not in |array[]|.
   |array[]| must be an modifiable array of |n int|s
   with room for a |(n + 1)|th element. */
int
seq_sentinel_search (int array[], int n, int key)
{
  int *p;

  array[n] = key;
  for (p = array; *p != key; p++)
    /* Nothing to do. */;
  return p - array < n ? p - array : -1;
}

/* Returns the smallest |i| such that |array[i] == key|,
   or |-1| if |key| is not in |array[]|.
   |array[]| must be an array of |n| |int|s sorted in ascending order. */
int
seq_sorted_search (int array[], int n, int key)
{
  int i;

  for (i = 0; i < n; i++)
    if (key <= array[i])
      return key == array[i] ? i : -1;

  return -1;
}

/* Returns the smallest |i| such that |array[i] == key|,
   or |-1| if |key| is not in |array[]|.
   |array[]| must be an modifiable array of |n int|s,
   sorted in ascending order,
   with room for a |(n + 1)|th element at the end. */
int
seq_sorted_sentinel_search (int array[], int n, int key)
{
  int *p;

  array[n] = key;
  for (p = array; *p < key; p++)
    /* Nothing to do. */;
  return p - array < n && *p == key ? p - array : -1;
}

/* Returns the smallest |i| such that |array[i] == key|,
   or |-1| if |key| is not in |array[]|.
   |array[]| must be an array of |n int|s,
   sorted in ascending order,
   with room for an |(n + 1)|th element to set to |INT_MAX|. */
int
seq_sorted_sentinel_search_2 (int array[], int n, int key)
{
  int *p;

  array[n] = INT_MAX;
  for (p = array; *p < key; p++)
    /* Nothing to do. */;
  return p - array < n && *p == key ? p - array : -1;
}

/* Returns the offset within |array[]| of an element equal to |key|,
   or |-1| if |key| is not in |array[]|.
   |array[]| must be an array of |n| |int|s sorted in ascending order. */
int
binary_search (int array[], int n, int key)
{
  int min = 0;
  int max = n - 1;

  while (max >= min)
    {
      int i = (min + max) / 2;
      if (key < array[i])
        max = i - 1;
      else if (key > array[i])
        min = i + 1;
      else
        return i;
    }

  return -1;
}

/* Returns the offset within |array[]| of an element equal to |key|,
   or |-1| if |key| is not in |array[]|.
   |array[]| must be an array of |n| |int|s sorted in ascending order,
   with |array[-1]| modifiable. */
int
uniform_binary_search (int array[], int n, int key)
{
  int i = (n + 1) / 2 - 1;
  int m = n / 2;

  array[-1] = INT_MIN;
  for (;;)
    {
      if (key < array[i])
        {
          if (m == 0)
            return -1;
          i -= (m + 1) / 2;
          m /= 2;
        }
      else if (key > array[i])
        {
          if (m == 0)
            return -1;
          i += (m + 1) / 2;
          m /= 2;
        }
      else
        return i >= 0 ? i : -1;
    }
}

/* Plug-compatible with standard C library |bsearch()|. */
static void *
blp_bsearch (const void *key, const void *array, size_t count,
             size_t size, int (*compare) (const void *, const void *))
{
  int min = 0;
  int max = count;

  while (max >= min)
    {
      int i = (min + max) / 2;
      void *item = ((char *) array) + size * i;
      int cmp = compare (key, item);

      if (cmp < 0)
        max = i - 1;
      else if (cmp > 0)
        min = i + 1;
      else
        return item;
    }

  return NULL;
}

/* Compares the |int|s pointed to by |pa| and |pb| and returns positive
   if |*pa > *pb|, negative if |*pa < *pb|, or zero if |*pa == *pb|. */
static int
compare_ints (const void *pa, const void *pb)
{
  const int *a = pa;
  const int *b = pb;

  if (*a > *b)
    return 1;
  else if (*a < *b)
    return -1;
  else
    return 0;
}

/* Returns the offset within |array[]| of an element equal to |key|,
   or |-1| if |key| is not in |array[]|.
   |array[]| must be an array of |n| |int|s sorted in ascending order. */
static int
binary_search_bsearch (int array[], int n, int key)
{
  int *p = blp_bsearch (&key, array, n, sizeof *array, compare_ints);
  return p != NULL ? p - array : -1;
}

/* Cheating search function that knows that |array[i] == i|.
   |n| must be the array size and |key| the item to search for.
   |array[]| is not used.
   Returns the index in |array[]| where |key| is found,
   or |-1| if |key| is not in |array[]|. */
int
cheat_search (int array[], int n, int key)
{
  return key >= 0 && key < n ? key : -1;
}

