package math.kombi;

import java.math.BigInteger;
import java.util.concurrent.Callable;

/**
 * Nierekurencyjna - setki razy szybsza niz rekurencyjna
 *
 * @author Jacek
 */
public class Stir1 implements Callable<BigInteger> {
    private final long k;
    private final long n;

    public Stir1(long k, long n) {
        this.k = k;
        this.n = n;
    }

    @Override
    public BigInteger call() {
        return stir1(this.k, this.n);
    }

    //oblicza liczbe Stirlinga I rodzaju (dla cykli)
    public static long stir1(int k, int n) {
        long sum = 0L;
        if (n == 0 && k == 0) {
            return 1;
        } else if ((n > 0) && (k == 0) || (n < k)) {
            return 0;
        } else {
            for (int m = 0; m <= n - k; m++) {
                sum += ((int) Math.pow(-1.0, m))
                        * Npok.npok(n - 1 + m, n - k + m)
                        * Npok.npok(2 * n - k, n - k - m)
                        * Stir2.stir2(m, n - k + m);
            }
        }
        return sum;
    }

    public static BigInteger stir1(long k, long n) {
        BigInteger sum = BigInteger.ZERO;
        if (n == 0 && k == 0) {
            return BigInteger.ONE;
        } else if ((n > 0) && (k == 0) || (n < k)) {
            return BigInteger.ZERO;
        } else {
            for (long m = 0; m <= n - k; m++) {
                if ((m & 1) == 1) {
                    sum = sum.subtract(Npok.npok(n - 1 + m, n - k + m)
                            .multiply(Npok.npok(2 * n - k, n - k - m))
                            .multiply(Stir2.stir2(m, n - k + m)));
                } else {
                    sum = sum.add(Npok.npok(n - 1 + m, n - k + m)
                            .multiply(Npok.npok(2 * n - k, n - k - m))
                            .multiply(Stir2.stir2(m, n - k + m)));
                }
            }
        }
        return sum;
    }

}
