r2rs-nmath 0.1.1

Statistics programming for Rust based on R's nmath package
Documentation
// Translation of nmath's qcauchy
//
// Copyright (C) 2020 neek-sss
//
// 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 <https://www.gnu.org/licenses/>.

use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Natural64, Probability64, Real64};

use crate::traits::TrigPI;

/// The quantile function of the Cauchy distribution.
pub fn qcauchy<P: Into<Probability64>, R: Into<Real64>, N: Into<Natural64>>(
    p: P,
    location: R,
    scale: N,
    lower_tail: bool,
) -> Real64 {
    let p = p.into().unwrap();
    qcauchy_inner(p, location, scale, lower_tail, false)
}

/// The quantile function of the Cauchy distribution.
pub fn log_qcauchy<LP: Into<LogProbability64>, R: Into<Real64>, N: Into<Natural64>>(
    p: LP,
    location: R,
    scale: N,
    lower_tail: bool,
) -> Real64 {
    let p = p.into().unwrap();
    qcauchy_inner(p, location, scale, lower_tail, true)
}

fn qcauchy_inner<R: Into<Real64>, N: Into<Natural64>>(
    mut p: f64,
    location: R,
    scale: N,
    mut lower_tail: bool,
    log: bool,
) -> Real64 {
    let location = location.into().unwrap();
    let scale = scale.into().unwrap();

    if log {
        if p > -1.0 {
            /* when ep := p.exp(),
             * tan(pi*ep)= -tan(pi*(-ep))= -tan(pi*(-ep)+pi) = -tan(pi*(1-ep)) =
             *   = -tan(pi*(-p.exp_m1())
             * for p ~ 0, p.exp() ~ 1, tan(~0) may be better than tan(~pi).
             */
            if p == 0.0 {
                /* needed, since 1/tan(-0) = -Inf  for some arch. */
                return (location + (if lower_tail { scale } else { -scale }) * f64::infinity())
                    .into();
            } // avoid 1/Inf below
            lower_tail = !lower_tail; // p = 1.0 is handled above
            p = -p.exp_m1()
        } else {
            p = p.exp()
        }
    } else if p > 0.5 {
        if p == 1.0 {
            return (location + (if lower_tail { scale } else { -scale }) * f64::infinity()).into();
        }
        p = 1.0 - p;
        lower_tail = !lower_tail
    }

    if p == 0.5 {
        location
    } else if p == 0.0 {
        location + (if lower_tail { scale } else { -scale }) * f64::NEG_INFINITY
    } else {
        location + (if lower_tail { -scale } else { scale }) / p.tan_pi()
    }
    .into()
    /* -1/tan(pi * p) = -cot(pi * p) = tan(pi * (p - 1/2))  */
}