rgsl/
error.rs

1//
2// A rust binding for the GSL library by Guillaume Gomez (guillaume1.gomez@gmail.com)
3//
4
5//! The error function is described in Abramowitz & Stegun, Chapter 7.
6
7use crate::{types, Value};
8use std::ffi::CStr;
9use std::mem::MaybeUninit;
10use std::os::raw::{c_char, c_int};
11
12/// This routine computes the error function erf(x), where erf(x) = (2/\sqrt(\pi)) \int_0^x dt \exp(-t^2).
13#[doc(alias = "gsl_sf_erf")]
14pub fn erf(x: f64) -> f64 {
15    unsafe { ::sys::gsl_sf_erf(x) }
16}
17
18/// This routine computes the error function erf(x), where erf(x) = (2/\sqrt(\pi)) \int_0^x dt \exp(-t^2).
19#[doc(alias = "gsl_sf_erf_e")]
20pub fn erf_e(x: f64) -> Result<types::Result, Value> {
21    let mut result = MaybeUninit::<sys::gsl_sf_result>::uninit();
22    let ret = unsafe { sys::gsl_sf_erf_e(x, result.as_mut_ptr()) };
23
24    result_handler!(ret, unsafe { result.assume_init() }.into())
25}
26
27/// This routine computes the complementary error function erfc(x) = 1 - erf(x) = (2/\sqrt(\pi)) \int_x^\infty \exp(-t^2).
28#[doc(alias = "gsl_sf_erfc")]
29pub fn erfc(x: f64) -> f64 {
30    unsafe { ::sys::gsl_sf_erfc(x) }
31}
32
33/// This routine computes the complementary error function erfc(x) = 1 - erf(x) = (2/\sqrt(\pi)) \int_x^\infty \exp(-t^2).
34#[doc(alias = "gsl_sf_erfc_e")]
35pub fn erfc_e(x: f64) -> Result<types::Result, Value> {
36    let mut result = MaybeUninit::<sys::gsl_sf_result>::uninit();
37    let ret = unsafe { sys::gsl_sf_erfc_e(x, result.as_mut_ptr()) };
38
39    result_handler!(ret, unsafe { result.assume_init() }.into())
40}
41
42/// This routine computes the logarithm of the complementary error function \log(\erfc(x)).
43#[doc(alias = "gsl_sf_log_erfc")]
44pub fn log_erfc(x: f64) -> f64 {
45    unsafe { ::sys::gsl_sf_log_erfc(x) }
46}
47
48/// This routine computes the logarithm of the complementary error function \log(\erfc(x)).
49#[doc(alias = "gsl_sf_log_erfc_e")]
50pub fn log_erfc_e(x: f64) -> Result<types::Result, Value> {
51    let mut result = MaybeUninit::<sys::gsl_sf_result>::uninit();
52    let ret = unsafe { sys::gsl_sf_log_erfc_e(x, result.as_mut_ptr()) };
53
54    result_handler!(ret, unsafe { result.assume_init() }.into())
55}
56
57/// This routine computes the Gaussian probability density function Z(x) = (1/\sqrt{2\pi}) \exp(-x^2/2).
58#[doc(alias = "gsl_sf_erf_Z")]
59pub fn erf_Z(x: f64) -> f64 {
60    unsafe { ::sys::gsl_sf_erf_Z(x) }
61}
62
63/// This routine computes the Gaussian probability density function Z(x) = (1/\sqrt{2\pi}) \exp(-x^2/2).
64#[doc(alias = "gsl_sf_erf_Z_e")]
65pub fn erf_Z_e(x: f64) -> Result<types::Result, Value> {
66    let mut result = MaybeUninit::<sys::gsl_sf_result>::uninit();
67    let ret = unsafe { sys::gsl_sf_erf_Z_e(x, result.as_mut_ptr()) };
68
69    result_handler!(ret, unsafe { result.assume_init() }.into())
70}
71
72/// This routine computes the upper tail of the Gaussian probability function Q(x) = (1/\sqrt{2\pi}) \int_x^\infty dt \exp(-t^2/2).
73///
74/// The hazard function for the normal distribution, also known as the inverse Mills’ ratio, is defined as,
75///
76/// h(x) = Z(x)/Q(x) = \sqrt{2/\pi} \exp(-x^2 / 2) / \erfc(x/\sqrt 2)
77///
78/// It decreases rapidly as x approaches -\infty and asymptotes to h(x) \sim x as x approaches +\infty.
79#[doc(alias = "gsl_sf_erf_Q")]
80pub fn erf_Q(x: f64) -> f64 {
81    unsafe { ::sys::gsl_sf_erf_Q(x) }
82}
83
84/// This routine computes the upper tail of the Gaussian probability function Q(x) = (1/\sqrt{2\pi}) \int_x^\infty dt \exp(-t^2/2).
85///
86/// The hazard function for the normal distribution, also known as the inverse Mills’ ratio, is defined as,
87///
88/// h(x) = Z(x)/Q(x) = \sqrt{2/\pi} \exp(-x^2 / 2) / \erfc(x/\sqrt 2)
89///
90/// It decreases rapidly as x approaches -\infty and asymptotes to h(x) \sim x as x approaches +\infty.
91#[doc(alias = "gsl_sf_erf_Q_e")]
92pub fn erf_Q_e(x: f64) -> Result<types::Result, Value> {
93    let mut result = MaybeUninit::<sys::gsl_sf_result>::uninit();
94    let ret = unsafe { sys::gsl_sf_erf_Q_e(x, result.as_mut_ptr()) };
95
96    result_handler!(ret, unsafe { result.assume_init() }.into())
97}
98
99/// This routine computes the hazard function for the normal distribution.
100#[doc(alias = "gsl_sf_hazard")]
101pub fn hazard(x: f64) -> f64 {
102    unsafe { ::sys::gsl_sf_hazard(x) }
103}
104
105/// This routine computes the hazard function for the normal distribution.
106#[doc(alias = "gsl_sf_hazard_e")]
107pub fn hazard_e(x: f64) -> Result<types::Result, Value> {
108    let mut result = MaybeUninit::<sys::gsl_sf_result>::uninit();
109    let ret = unsafe { sys::gsl_sf_hazard_e(x, result.as_mut_ptr()) };
110
111    result_handler!(ret, unsafe { result.assume_init() }.into())
112}
113
114pub fn str_error(error: crate::Value) -> &'static str {
115    match error {
116        crate::Value::Success => "Success",
117        crate::Value::Failure => "Failure",
118        crate::Value::Continue => "The iteration has not converged yet",
119        crate::Value::Domain => "Input domain error",
120        crate::Value::Range => "Output range error",
121        crate::Value::Fault => "Invalid pointer",
122        crate::Value::Invalid => "Invalid argument supplied by user",
123        crate::Value::Failed => "generic failure",
124        crate::Value::Factorization => "Factorization failed",
125        crate::Value::Sanity => "Sanity check failed - shouldn't happen",
126        crate::Value::NoMemory => "Malloc failed",
127        crate::Value::BadFunction => "Problem with user-supplied function",
128        crate::Value::RunAway => "Iterative process is out of control",
129        crate::Value::MaxIteration => "Exceeded max number of iterations",
130        crate::Value::ZeroDiv => "Tried to divide by zero",
131        crate::Value::BadTolerance => {
132            "Specified tolerance is invalid or theoretically unattainable"
133        }
134        crate::Value::Tolerance => "Failed to reach the specified tolerance",
135        crate::Value::UnderFlow => "Underflow",
136        crate::Value::OverFlow => "Overflow",
137        crate::Value::Loss => "Loss of accuracy",
138        crate::Value::Round => "Roundoff error",
139        crate::Value::BadLength => "Matrix/vector sizes are not conformant",
140        crate::Value::NotSquare => "Matrix not square",
141        crate::Value::Singularity => "Singularity or extremely bad function behavior detected",
142        crate::Value::Diverge => "Integral or series is divergent",
143        crate::Value::Unsupported => {
144            "The required feature is not supported by this hardware platform"
145        }
146        crate::Value::Unimplemented => "The requested feature is not (yet) implemented",
147        crate::Value::Cache => "Cache limit exceeded",
148        crate::Value::Table => "Table limit exceeded",
149        crate::Value::NoProgress => "Iteration is not making progress towards solution",
150        crate::Value::NoProgressJacobian => "Jacobian evaluations are not improving the solution",
151        crate::Value::ToleranceF => "Cannot reach the specified tolerance in F",
152        crate::Value::ToleranceX => "Cannot reach the specified tolerance in X",
153        crate::Value::ToleranceG => "Cannot reach the specified tolerance in gradient",
154        crate::Value::EOF => "End of file",
155        crate::Value::Unknown(_) => "Unknown error",
156    }
157}
158
159static mut CALLBACK: Option<fn(&str, &str, u32, crate::Value)> = None;
160
161/// `f` is the type of GSL error handler functions. An error handler will be passed four arguments
162/// which specify the reason for the error (a string), the name of the source file in which it
163/// occurred (also a string), the line number in that file (an integer) and the error number (an
164/// integer). The source file and line number are set at compile time using the __FILE__ and
165/// __LINE__ directives in the preprocessor. An error handler function returns type void. Error
166/// handler functions should be defined like this,
167///
168/// This function sets a new error handler, new_handler, for the GSL library routines. The previous
169/// handler is returned (so that you can restore it later). Note that the pointer to a user defined
170/// error handler function is stored in a static variable, so there can be only one error handler
171/// per program. This function should be not be used in multi-threaded programs except to set up a
172/// program-wide error handler from a master thread. The following example shows how to set and
173/// restore a new error handler,
174///
175/// ```
176/// use rgsl::error::set_error_handler;
177/// use rgsl::Value;
178///
179/// fn error_handling(error_str: &str, file: &str, line: u32, error_value: Value) {
180///     println!("[{:?}] '{}:{}': {}", error_value, file, line, error_str);
181/// }
182///
183/// /* save original handler, install new handler */
184/// let old_handler = set_error_handler(Some(error_handling));
185///
186/// /* code uses new handler */
187/// // ...
188///
189/// /* restore original handler */
190/// set_error_handler(old_handler);
191/// ```
192///
193/// To use the default behavior (abort on error) set the error handler to NULL,
194///
195/// ```
196/// # use rgsl::error::set_error_handler;
197/// let old_handler = set_error_handler(None);
198/// ```
199#[doc(alias = "gsl_set_error_handler")]
200pub fn set_error_handler(
201    f: Option<fn(&str, &str, u32, crate::Value)>,
202) -> Option<fn(&str, &str, u32, crate::Value)> {
203    unsafe {
204        let out = CALLBACK.take();
205        match f {
206            Some(f) => {
207                CALLBACK = Some(f);
208                sys::gsl_set_error_handler(Some(inner_error_handler));
209            }
210            None => {
211                sys::gsl_set_error_handler(None);
212            }
213        }
214        out
215    }
216}
217
218/// This function turns off the error handler by defining an error handler which does nothing. This
219/// will cause the program to continue after any error, so the return values from any library
220/// routines must be checked. This is the recommended behavior for production programs. The previous
221/// handler is returned (so that you can restore it later).
222#[doc(alias = "gsl_set_error_handler_off")]
223pub fn set_error_handler_off() -> Option<fn(&str, &str, u32, crate::Value)> {
224    unsafe {
225        sys::gsl_set_error_handler_off();
226        CALLBACK.take()
227    }
228}
229
230extern "C" fn inner_error_handler(
231    reason: *const c_char,
232    file: *const c_char,
233    line: c_int,
234    gsl_errno: c_int,
235) {
236    unsafe {
237        if let Some(ref call) = CALLBACK {
238            let s = CStr::from_ptr(reason);
239            let f = CStr::from_ptr(file);
240            call(
241                s.to_str().unwrap_or("Unknown"),
242                f.to_str().unwrap_or("Unknown"),
243                line as _,
244                crate::Value::from(gsl_errno),
245            );
246        }
247    }
248}
249
250#[test]
251fn test_error_handler() {
252    use {bessel, Value};
253
254    set_error_handler_off();
255    match bessel::K0_e(1e3) {
256        Err(Value::UnderFlow) => println!("K0(1e3) underflowed"),
257        _ => panic!("unexpected"),
258    }
259}