r2rs-nmath 0.1.1

Statistics programming for Rust based on R's nmath package
Documentation
// Translation of nmath's qnf
//
// 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, Positive64, Probability64, Rational64, Real64,
};

use crate::{
    distribution::{
        beta::{log_qnbeta, qnbeta},
        chisq::{log_qnchisq, qnchisq},
    },
    traits::DPQ,
};

pub fn qnf<
    PR: Into<Probability64>,
    R1: Into<Rational64>,
    R2: Into<Rational64>,
    PO: Into<Positive64>,
>(
    p: PR,
    df1: R1,
    df2: R2,
    ncp: PO,
    lower_tail: bool,
) -> Real64 {
    let p = p.into().unwrap();
    qnf_inner(p, df1, df2, ncp, lower_tail, false)
}

pub fn log_qnf<
    LP: Into<LogProbability64>,
    R1: Into<Rational64>,
    R2: Into<Rational64>,
    P: Into<Positive64>,
>(
    p: LP,
    df1: R1,
    df2: R2,
    ncp: P,
    lower_tail: bool,
) -> Real64 {
    let p = p.into().unwrap();
    qnf_inner(p, df1, df2, ncp, lower_tail, true)
}

fn qnf_inner<R1: Into<Rational64>, R2: Into<Rational64>, P: Into<Positive64>>(
    p: f64,
    df1: R1,
    df2: R2,
    ncp: P,
    lower_tail: bool,
    log: bool,
) -> Real64 {
    let df1 = df1.into().unwrap();
    let df2 = df2.into().unwrap();
    let ncp = ncp.into().unwrap();

    let mut y = 0.0;

    if !ncp.is_finite() {
        return f64::nan().into();
    }
    if !df1.is_finite() && !df2.is_finite() {
        return f64::nan().into();
    }

    if let Some(ret) = p.q_p01_boundaries(0.0, f64::infinity(), lower_tail, log) {
        return ret.into();
    }

    if df2 > 1e8 {
        /* avoid problems with +Inf and loss of accuracy */
        return if log {
            log_qnchisq(p, df1, ncp, lower_tail).unwrap() / df1
        } else {
            qnchisq(p, df1, ncp, lower_tail).unwrap() / df1
        }
        .into();
    }

    if log {
        y = log_qnbeta(p, df1 / 2.0, df2 / 2.0, ncp, lower_tail).unwrap();
    } else {
        y = qnbeta(p, df1 / 2.0, df2 / 2.0, ncp, lower_tail).unwrap();
    }

    (y / (1.0 - y) * (df2 / df1)).into()
}