// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.JunctionChecker.junctionchecking;

import org.openstreetmap.josm.tools.Logging;

public class Combination {

    private long n = 0;
    private long k = 0;
    long[] data = null;

    public Combination(long n, long k) {
        if (n < 0 || k < 0)
            Logging.error("Negative parameter in constructor");
        this.n = n;
        this.k = k;
        this.data = new long[(int) k];
        for (long i = 0; i < k; ++i) {
          this.data[(int) i] = i;
        }
      }

    public static long Choose(long n, long k) {
      if (n < 0 || k < 0)
          Logging.error("Invalid negative parameter in Choose()");
      if (n < k) return 0;
      if (n == k) return 1;

      long delta, iMax;

      if (k < n-k) { // ex: Choose(100,3)
        delta = n-k;
        iMax = k;
      } else {       // ex: Choose(100,97)
        delta = k;
        iMax = n-k;
      }

      long ans = delta + 1;

      for (long i = 2; i <= iMax; ++i) {
        ans = (ans * (delta + i)) / i;
      }

      return ans;
    }

    public long Choose() {

      if (n < k) return 0;
      if (n == k) return 1;

      long delta, iMax;

      if (k < n-k) { // ex: Choose(100,3)
        delta = n-k;
        iMax = k;
      } else {       // ex: Choose(100,97)
        delta = k;
        iMax = n-k;
      }

      long ans = delta + 1;

      for (long i = 2; i <= iMax; ++i) {
        ans = (ans * (delta + i)) / i;
      }

      return ans;
    }

    public Combination Successor() {
      if (this.data.length == 0 ||
          this.data[0] == this.n - this.k)
        return null;

      Combination ans = new Combination(this.n, this.k);

      long i;
      for (i = 0; i < this.k; ++i) {
        ans.data[(int) i] = this.data[(int) i];
      }
      for (i = this.k - 1; i > 0 && ans.data[(int) i] == this.n - this.k + i; --i) {
          // Count
      }

      ++ans.data[(int) i];

      for (long j = i; j < this.k - 1; ++j) {
        ans.data[(int) j+1] = ans.data[(int) j] + 1;
      }
      return ans;
    }

    public String ToString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        for (long i = 0; i < this.k; ++i) {
          sb.append(this.data[(int) i]);
          if (i < this.k-1) sb.append(", ");
        }
        sb.append("}");
        return sb.toString();
      }

}
