/*
 *  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 2 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, a copy is available at
 *  http://www.r-project.org/Licenses/
 */
package jdistlib;

import jdistlib.generic.GenericDistribution;
import jdistlib.rng.RandomEngine;
import jdistlib.util.Debug;

import static java.lang.Math.*;
import static jdistlib.math.Constants.*;

/**
 * Bounded Arcsine distribution; bounded by [a, b]. For unbounded Arcsine distribution, use Beta(0.5, 0.5).
 * Note: Minimally tested.
 * @author Roby Joehanes
 *
 */
public class Arcsine extends GenericDistribution {
	public static final double density(double x, double a, double b, boolean give_log) {
		if (Double.isNaN(x) || Double.isNaN(a) || Double.isNaN(b)) return x + a + b;
		if (b < a) throw new IllegalArgumentException();
		b = - M_LOG_PI - 0.5 * (log(x - a)  + log(b - x));
		return give_log ? b : exp(b);
	}

	public static final double cumulative(double x, double a, double b, boolean lower_tail, boolean log_p) {
		if (Double.isNaN(x) || Double.isNaN(a) || Double.isNaN(b)) return x + a + b;
		if (b < a) throw new IllegalArgumentException();
		b = cumulative_raw(x, a, b);
		return log_p ? (lower_tail ? b : log1p(-exp(b))) : (lower_tail ? exp(b): 1-exp(b));
	}

	static final double cumulative_raw(double x, double a, double b) {
		return -log(M_PI_2) - log(asin(sqrt((x - a) / (b - a))));
	}

	/**
	 * Quantile method by bisection
	 * @param q
	 * @param a
	 * @param b
	 * @param lower_tail
	 * @param log_p
	 */
	public static final double quantile(double q, double a, double b, boolean lower_tail, boolean log_p) {
		if (Double.isNaN(q) || Double.isNaN(a) || Double.isNaN(b)) return q + a + b;
		if (b < a) throw new IllegalArgumentException();
		if (log_p) q = exp(q);
		if (q < 0 || q > 1) throw new IllegalArgumentException();
		if (lower_tail) q = 1-q;
		q = log(q); // Log form comparison
		double lo = a, hi = b, f_lo = cumulative_raw(lo, a, b), f_hi = cumulative_raw(hi, a, b), mid, f_mid;
		boolean pathological = false;
		do {
			mid = (lo + hi) / 2.;
			f_mid = cumulative_raw(mid, a, b);
			// When the case is pathological, prefer to shrink the
			// upper bound when lower_tail == true (shrink the lower bound otherwise)
			if (f_mid == q && (f_hi == q || f_lo == q) && (hi - lo > 2))
				pathological = true;
			if (lower_tail) {
				if (f_lo >= q) return lo;
				if (f_mid > q) {
					hi = mid;
					f_hi = f_mid;
				} else {
					lo = mid;
					f_lo = f_mid;
				}
			} else {
				if (f_hi <= q) return hi;
				if (f_mid < q) {
					lo = mid;
					f_lo = f_mid;
				} else {
					hi = mid;
					f_hi = f_mid;
				}
			}
		} while (hi - lo > 1);
		if (pathological) {
			System.err.println("Pathological case of Arcsine.quantile! Quantile estimate may not be accurate!");
			if (Debug.warningAsError) throw new RuntimeException("Pathological case of Arcsine.quantile! Quantile estimate may not be accurate!");
		}
		if (lower_tail)
			return f_hi <= q ? hi : f_mid <= q ? mid : lo;
		return f_lo >= q ? lo : f_mid >= q ? mid : hi;
	}

	public static final double random(double a, double b, RandomEngine random) {
		double u1 = random.nextDouble();
		u1 = (int) (134217728 * u1) + random.nextDouble();
		u1 = quantile(u1 / 134217728, a, b, true, false);
		return u1;
	}

	public static final double[] random(int n, double a, double b, RandomEngine random) {
		double[] rand = new double[n];
		for (int i = 0; i < n; i++)
			rand[i] = random(a, b, random);
		return rand;
	}

	protected double a, b;

	public Arcsine(double a, double b) {
		this.a = a; this.b = b;
	}

	@Override
	public double density(double x, boolean log) {
		return density(x, a, b, log);
	}

	@Override
	public double cumulative(double p, boolean lower_tail, boolean log_p) {
		return cumulative(p, a, b, lower_tail, log_p);
	}

	@Override
	public double quantile(double q, boolean lower_tail, boolean log_p) {
		return quantile(q, a, b, lower_tail, log_p);
	}

	@Override
	public double random() {
		return random(a, b, random);
	}
}

 

Ads help maintain this website.