/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.math.distribution;

import org.apache.commons.math.MathException;

/**
 * <code>PoissonDistributionTest</code>
 * 
 * @version $Revision$ $Date$
 */
public class PoissonDistributionTest extends IntegerDistributionAbstractTest {

	/**
	 * Poisson parameter value for the test distribution.
	 */
	private static final double DEFAULT_TEST_POISSON_PARAMETER = 4.0;

	/**
	 * Constructor.
	 * 
	 * @param name
	 */
	public PoissonDistributionTest(String name) {
		super(name);
		setTolerance(1e-12);
	}

	/**
	 * Creates the default discrete distribution instance to use in tests.
	 */
	@Override
	public IntegerDistribution makeDistribution() {
		return new PoissonDistributionImpl(DEFAULT_TEST_POISSON_PARAMETER);
	}

	/**
	 * Creates the default probability density test input values.
	 */
	@Override
	public int[] makeDensityTestPoints() {
		return new int[] { -1, 0, 1, 2, 3, 4, 5, 10, 20 };
	}

	/**
	 * Creates the default probability density test expected values. These and
	 * all other test values are generated by R, version 1.8.1
	 */
	@Override
	public double[] makeDensityTestValues() {
		return new double[] { 0d, 0.0183156388887d, 0.073262555555d, 0.14652511111d, 0.195366814813d, 0.195366814813,
				0.156293451851d, 0.00529247667642d, 8.27746364655e-09 };
	}

	/**
	 * Creates the default cumulative probability density test input values.
	 */
	@Override
	public int[] makeCumulativeTestPoints() {
		return new int[] { -1, 0, 1, 2, 3, 4, 5, 10, 20 };
	}

	/**
	 * Creates the default cumulative probability density test expected values.
	 */
	@Override
	public double[] makeCumulativeTestValues() {
		return new double[] { 0d, 0.0183156388887d, 0.0915781944437d, 0.238103305554d, 0.433470120367d, 0.62883693518,
				0.78513038703d, 0.99716023388d, 0.999999998077 };
	}

	/**
	 * Creates the default inverse cumulative probability test input values.
	 * Increased 3rd and 7th values slightly as computed cumulative
	 * probabilities for corresponding values exceeds the target value (still
	 * within tolerance).
	 */
	@Override
	public double[] makeInverseCumulativeTestPoints() {
		return new double[] { 0d, 0.018315638889d, 0.0915781944437d, 0.238103305554d, 0.433470120367d, 0.62883693518,
				0.78513038704d, 0.99716023388d, 0.999999998077 };
	}

	/**
	 * Creates the default inverse cumulative probability density test expected
	 * values.
	 */
	@Override
	public int[] makeInverseCumulativeTestValues() {
		return new int[] { -1, 0, 1, 2, 3, 4, 5, 10, 20 };
	}

	/**
	 * Test the normal approximation of the Poisson distribution by calculating
	 * P(90 &le; X &le; 110) for X = Po(100) and P(9900 &le; X &le; 10200) for X
	 * = Po(10000)
	 */
	public void testNormalApproximateProbability() throws Exception {
		PoissonDistribution dist = new PoissonDistributionImpl(100);
		double result = dist.normalApproximateProbability(110) - dist.normalApproximateProbability(89);
		assertEquals(0.706281887248, result, 1E-10);
		dist.setMean(10000);
		result = dist.normalApproximateProbability(10200) - dist.normalApproximateProbability(9899);
		assertEquals(0.820070051552, result, 1E-10);
	}

	/**
	 * Test the degenerate cases of a 0.0 and 1.0 inverse cumulative
	 * probability.
	 * 
	 * @throws Exception
	 */
	public void testDegenerateInverseCumulativeProbability() throws Exception {
		PoissonDistribution dist = new PoissonDistributionImpl(DEFAULT_TEST_POISSON_PARAMETER);
		assertEquals(Integer.MAX_VALUE, dist.inverseCumulativeProbability(1.0d));
		assertEquals(-1, dist.inverseCumulativeProbability(0d));
	}

	public void testMean() {
		PoissonDistribution dist = new PoissonDistributionImpl(DEFAULT_TEST_POISSON_PARAMETER);
		try {
			dist.setMean(-1);
			fail("negative mean.  IllegalArgumentException expected");
		} catch (IllegalArgumentException ex) {
		}

		dist.setMean(10.0);
		assertEquals(10.0, dist.getMean(), 0.0);
	}

	public void testLargeMeanCumulativeProbability() {
		PoissonDistribution dist = new PoissonDistributionImpl(1.0);
		double mean = 1.0;
		while (mean <= 10000000.0) {
			dist.setMean(mean);

			double x = mean * 2.0;
			double dx = x / 10.0;
			while (x >= 0) {
				try {
					dist.cumulativeProbability(x);
				} catch (MathException ex) {
					fail("mean of " + mean + " and x of " + x + " caused " + ex.getMessage());
				}
				x -= dx;
			}

			mean *= 10.0;
		}
	}

	public void testLargeMeanInverseCumulativeProbability() {
		PoissonDistribution dist = new PoissonDistributionImpl(1.0);
		double mean = 1.0;
		while (mean <= 10000000.0) {
			dist.setMean(mean);

			double p = 0.1;
			double dp = p;
			while (p < 1.0) {
				try {
					dist.inverseCumulativeProbability(p);
				} catch (MathException ex) {
					fail("mean of " + mean + " and p of " + p + " caused " + ex.getMessage());
				}
				p += dp;
			}

			mean *= 10.0;
		}
	}
}