#ifndef RANKER_H
#define RANKER_H

#include <algorithm>
#include <string>
#include <vector>


using std::string;
using std::vector;

#ifndef uint
typedef unsigned int uint;
#endif

template <class T> class lt {
public:
  static int compare(T a, T b) { return (a < b); }
};
template <class T> class gt {
public:
  static int compare(T a, T b) { return (a > b); }
};

template <class T, class C> class ranker {
private:
  const T *p;
  uint sz;

public:
  ranker(const vector<T> &v) : p(&v[0]), sz(v.size()) {}
  ranker(const T *tp, uint s) : p(tp), sz(s) {}

  int operator()(uint i1, uint i2) const { return (C::compare(p[i1], p[i2])); }

  template <class S> void get_orders(vector<S> &w) const {
    w.resize(sz);
    w.front() = 0;
    for (typename vector<S>::iterator i = w.begin(); i != w.end() - 1; ++i)
      *(i + 1) = *i + 1;
    std::sort(w.begin(), w.end(), *this);
  }

  template <class S> void get_partial_orders(vector<S> &w, uint num) const {
    if (num > sz)
      num = sz;
    w.resize(sz);
    w.front() = 0;
    for (typename vector<S>::iterator i = w.begin(); i != w.end() - 1; ++i)
      *(i + 1) = *i + 1;
    std::partial_sort(w.begin(), w.begin() + num, w.end(), *this);
    w.resize(num);
  }

  template <class S> void get_ranks(vector<S> &w, const string &method) const {
    w.resize(sz);
    vector<uint> tmp(w.size());
    get_orders(tmp);
    if (method == "average") {
      for (uint c = 0, reps; c < w.size(); c += reps) {
        reps = 1;
        while (c + reps < w.size() && p[tmp[c]] == p[tmp[c + reps]])
          ++reps;
        for (uint k = 0; k < reps; ++k)
          w[tmp[c + k]] = S(2 * c + reps - 1) / 2 + 1;
      }
    } else if (method == "min") {
      for (uint c = 0, reps; c < w.size(); c += reps) {
        reps = 1;
        while (c + reps < w.size() && p[tmp[c]] == p[tmp[c + reps]])
          ++reps;
        for (uint k = 0; k < reps; ++k)
          w[tmp[c + k]] = c + 1;
      }
    } else if (method == "max") {
      for (uint c = 0, reps; c < w.size(); c += reps) {
        reps = 1;
        while (c + reps < w.size() && p[tmp[c]] == p[tmp[c + reps]])
          ++reps;
        for (uint k = 0; k < reps; ++k)
          w[tmp[c + k]] = c + reps;
      }
    } else
      // default
      for (uint c = 0; c < w.size(); ++c)
        w[tmp[c]] = c + 1;
  }

  template <class S>
  void get_partial_ranks(vector<S> &w, const string &method, size_t num) const {
    if (num > sz)
      num = sz;
    vector<uint> tmp(sz);
    get_partial_orders(tmp, num);
    w.resize(sz);
    fill(w.begin(), w.end(), 0);
    if (method == "average") {
      for (uint c = 0, reps; c < num; c += reps) {
        reps = 1;
        while (c + reps < num && p[tmp[c]] == p[tmp[c + reps]])
          ++reps;
        for (uint k = 0; k < reps; ++k)
          w[tmp[c + k]] = S(2 * c + reps - 1) / 2 + 1;
      }
    } else if (method == "min") {
      for (uint c = 0, reps; c < num; c += reps) {
        reps = 1;
        while (c + reps < num && p[tmp[c]] == p[tmp[c + reps]])
          ++reps;
        for (uint k = 0; k < reps; ++k)
          w[tmp[c + k]] = c + 1;
      }
    } else if (method == "max") {
      for (uint c = 0, reps; c < num; c += reps) {
        reps = 1;
        while (c + reps < num && p[tmp[c]] == p[tmp[c + reps]])
          ++reps;
        for (uint k = 0; k < reps; ++k)
          w[tmp[c + k]] = c + reps;
      }
    } else
      // default
      for (uint c = 0; c < num; ++c)
        w[tmp[c]] = c + 1;
  }
};

template <class T, class S>
inline void rank(const vector<T> &v, vector<S> &w,
                 const string &method = "average") {
  ranker<T, lt<T>> r(v);
  r.get_ranks(w, method);
}

template <class T, class S>
inline void rank(const T *d, uint size, vector<S> &w,
                 const string &method = "average") {
  ranker<T, lt<T>> r(d, size);
  r.get_ranks(w, method);
}

template <class T, class S>
inline void partial_rank(const vector<T> &v, vector<S> &w, uint num,
                         const string &method = "average") {
  ranker<T, lt<T>> r(v);
  r.get_partial_ranks(w, method, num);
}

template <class T, class S>
inline void partial_rank(const T *d, uint size, vector<S> &w, uint num,
                         const string &method = "average") {
  ranker<T, lt<T>> r(d, size);
  r.get_partial_ranks(w, method, num);
}

template <class T, class S>
inline void order(const vector<T> &v, vector<S> &w) {
  ranker<T, lt<T>> r(v);
  r.get_orders(w);
}

template <class T, class S>
inline void order(const T *d, uint size, vector<S> &w) {
  ranker<T, lt<T>> r(d, size);
  r.get_orders(w);
}

template <class T, class S>
inline void partial_order(const vector<T> &v, vector<S> &w, uint num) {
  ranker<T, lt<T>> r(v);
  r.get_partial_orders(w, num);
}

template <class T, class S>
inline void partial_order(const T *d, uint size, vector<S> &w, uint num) {
  ranker<T, lt<T>> r(d, size);
  r.get_partial_orders(w, num);
}

template <class T, class S>
inline void rankhigh(const vector<T> &v, vector<S> &w,
                     const string &method = "average") {
  ranker<T, gt<T>> r(v);
  r.get_ranks(w, method);
}

template <class T, class S>
inline void rankhigh(const T *d, uint size, vector<S> &w,
                     const string &method = "average") {
  ranker<T, gt<T>> r(d, size);
  r.get_ranks(w, method);
}

template <class T, class S>
inline void partial_rankhigh(const vector<T> &v, vector<S> &w, uint num,
                             const string &method = "average") {
  ranker<T, gt<T>> r(v);
  r.get_partial_ranks(w, method, num);
}

template <class T, class S>
inline void partial_rankhigh(const T *d, uint size, vector<S> &w, uint num,
                             const string &method = "average") {
  ranker<T, gt<T>> r(d, size);
  r.get_partial_ranks(w, method, num);
}

template <class T, class S>
inline void orderhigh(const vector<T> &v, vector<S> &w) {
  ranker<T, gt<T>> r(v);
  r.get_orders(w);
}

template <class T, class S>
inline void orderhigh(const T *d, uint size, vector<S> &w) {
  ranker<T, gt<T>> r(d, size);
  r.get_orders(w);
}

template <class T, class S>
inline void partial_orderhigh(const vector<T> &v, vector<S> &w, uint num) {
  ranker<T, gt<T>> r(v);
  r.get_partial_orders(w, num);
}

template <class T, class S>
inline void partial_orderhigh(const T *d, uint size, vector<S> &w, uint num) {
  ranker<T, gt<T>> r(d, size);
  r.get_partial_orders(w, num);
}

template <class T>
inline T quantile(const T *d, const uint size, const double q) {
  if (size == 0)
    return T(0);
  if (size == 1)
    return d[0];
  if (q <= 0)
    return *std::min_element(d, d + size);
  if (q >= 1)
    return *std::max_element(d, d + size);

  double pos = (size - 1) * q;
  uint ind = uint(pos);
  double delta = pos - ind;
  vector<T> w(size);
  std::copy(d, d + size, w.begin());
  std::nth_element(w.begin(), w.begin() + ind, w.end());
  T i1 = *(w.begin() + ind);
  T i2 = *std::min_element(w.begin() + ind + 1, w.end());
  return i1 * (1.0 - delta) + i2 * delta;
}

template <class T> inline T quantile(const vector<T> &v, const double q) {
  return quantile(&v[0], v.size(), q);
}

#endif
// see
// https://sites.google.com/site/jivsoft/Home/compute-ranks-of-elements-in-a-c---array-or-vector
