/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

  Copyright (C) 2006 Brockmann Consult
  See COPYING file for copying and redistribution conditions.

  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; version 2 of the License.

  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.
*/

#include <utility>
#include <stdio.h>
#include <assert.h>

#include "nth_element.h"

double
nth_element(double *array, int length, int n)
{
  int i, j, k = 0, l = length - 1, m;

  assert(array != nullptr);
  assert(l >= 0);
  assert(n >= 0);
  assert(n <= l);

  for (;;)
    {
      if (l <= k + 1)
        {
          if (l == k + 1 && array[l] < array[k]) std::swap(array[k], array[l]);
          return array[n];
        }
      else
        {
          m = (k + l) >> 1;
          std::swap(array[m], array[k + 1]);

          if (array[k] > array[l]) std::swap(array[k], array[l]);
          if (array[k + 1] > array[l]) std::swap(array[k + 1], array[l]);
          if (array[k] > array[k + 1]) std::swap(array[k], array[k + 1]);

          i = k + 1;
          j = l;
          auto a = array[k + 1];

          for (;;)
            {
              do
                i++;
              while (array[i] < a);
              do
                j--;
              while (array[j] > a);
              if (j < i) break;
              std::swap(array[i], array[j]);
            }

          array[k + 1] = array[j];
          array[j] = a;
          if (j >= n) l = j - 1;
          if (j <= n) k = i;
        }
    }
}
