/*
 * #%L
 * gitools-core
 * %%
 * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group
 * %%
 * 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, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * 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.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */
package org.gitools.plugins.mutex.analysis;

import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.stat.StatUtils;
import org.gitools.analysis.stats.test.WeightedRandPerm;
import org.gitools.api.analysis.IProgressMonitor;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;


public class MutualExclusiveTest {

    private static final NormalDistribution NORMAL = new NormalDistribution();

    Random random;
    private String name;


    public MutualExclusiveTest() {
        name = "mutualExclusive";
        this.random = new Random(849);
    }

    public Class getResultClass() {
        return MutualExclusiveResult.class;
    }

    /**
     * @param draws      Array with count of events per item
     * @param weights
     * @param coverage
     * @param signal
     * @param iterations
     * @param monitor
     * @return
     */
    public MutualExclusiveResult processTest(int[] draws, double[] weights, int coverage, int signal, int iterations, IProgressMonitor monitor) {

        int samplesCount = weights.length;
        WeightedRandPerm permutator = new WeightedRandPerm(random, weights);

        double[] measurements = new double[iterations];
        int greaterEvents = 0;
        int smallerEvents = 0;
        int equalEevents = 0;
        for (int i = 0; i < iterations; i++) {
            measurements[i] = simulation(samplesCount, draws, permutator);
            if (measurements[i] > coverage) {
                greaterEvents++;
            } else if (measurements[i] < coverage) {
                smallerEvents++;
            } else {
                equalEevents++;
            }
            if ((i + 1) % 100 == 0) {
                monitor.worked(100);
                if (monitor.isCancelled()) {
                    break;
                }
            }
        }

        int mutexEvents = greaterEvents + equalEevents;
        int coocEvents = smallerEvents + equalEevents;

        double var = StatUtils.variance(measurements);
        double mean = StatUtils.mean(measurements);

        double zscore = (coverage - mean) / Math.sqrt(var);
        double coocP = NORMAL.cumulativeProbability(zscore);   // leftPValue
        double mutexP = 1.0 - coocP;                           // rightPValue

        return new MutualExclusiveResult(samplesCount, zscore, mutexP, coocP, coverage, signal, mean, var, mutexEvents, coocEvents);

    }


    private double simulation(int samplesCount, int[] drawsList, WeightedRandPerm random) {
        Set<Integer> set = new HashSet<>();

        for (int draws : drawsList) {
            random.reset(samplesCount);
            for (int i = 0; i < draws; i++) {
                set.add(random.next());
            }
        }

        return set.size();

    }

}
