fixedpoint/lib.rs
1//! # FixedPoint
2//!
3//! A simple library for computing the fixed point of a given function
4//!
5//! ## Example
6//! ```rust
7//! extern crate fixedpoint;
8//!
9//! use fixedpoint::fixedpoint;
10//!
11//! fn func_with_fixed_point(num: u32, param: &u32) -> u32 {
12//! 150 + (((num as f32 / param.clone() as f32).ceil() as u32)*100)
13//! }
14//!
15//! fn main() {
16//! let val = fixedpoint(&func_with_fixed_point, 0, &150, None, None).unwrap();
17//! println!("Fixed Point of function exists at: {}", val);
18//! }
19//! ```
20
21
22// Clippy Lints
23// #![warn(cast_possible_truncation)]
24// #![warn(cast_possible_wrap)]
25// #![warn(cast_precision_loss)]
26// #![warn(cast_sign_loss)]
27// #![warn(empty_enum)]
28// #![warn(enum_glob_use)]
29// #![warn(filter_map)]
30// #![warn(if_not_else)]
31// #![warn(indexing_slicing)]
32// #![warn(invalid_upcast_comparisons)]
33// #![warn(items_after_statements)]
34// #![warn(missing_docs_in_private_items)]
35// #![warn(mut_mut)]
36// #![warn(nonminimal_bool)]
37// #![warn(option_map_unwrap_or)]
38// #![warn(option_map_unwrap_or_else)]
39// #![warn(option_unwrap_used)]
40// #![warn(pub_enum_variant_names)]
41// #![warn(result_unwrap_used)]
42// #![warn(shadow_reuse)]
43// #![warn(shadow_same)]
44// #![warn(shadow_unrelated)]
45// #![warn(similar_names)]
46// #![warn(single_match_else)]
47// #![warn(stutter)]
48// #![warn(wrong_pub_self_convention)]
49
50#![warn(missing_docs,
51 missing_debug_implementations,
52 missing_copy_implementations,
53 trivial_casts, trivial_numeric_casts,
54 unsafe_code,
55 unstable_features,
56 unused_import_braces, unused_qualifications)]
57
58#[macro_use]
59extern crate error_chain;
60#[macro_use]
61extern crate log;
62
63#[allow(missing_docs)]
64pub mod errors {
65 // Create the Error, ErrorKind, ResultExt, and Result types
66 error_chain! {
67 errors {
68 ValueLimit
69 IterLimit(limit: usize) {
70 description("Max iteration limit exceeded")
71 display("Could not converge function after {} iterations", limit)
72 }
73 }
74 }
75}
76
77use errors::*;
78
79
80/// Compute the fixed point of a given function
81///
82/// Given a function `func`, compute the fixed point of the function such that `func(x, args) = x`
83/// `args` is a reference to any object that the function uses as auxillary data
84/// `maxiter` is used to limit the maximum number of iterations that the function will go through
85/// before giving up. The default is 100.
86/// If a `maxval` is provided, then the fixedpoint computation will stop when the value of the
87/// function exceeds `maxval`. This is mostly useful in providing upper bounds on monotonic
88/// functions.
89///
90/// The `args` parameter is generic. So you can pass arbitrary data types to it that only the
91/// provided function needs to know how to interpret. This allows you to pass a `Option`, `struct`,
92/// `Result` or any other type of complex object as well.
93pub fn fixedpoint<T, U>(func: &Fn(U, &T) -> U, x0: U, args: &T, maxiter: Option<usize>, maxval: Option<U>) -> Result<U>
94where U: PartialEq + PartialOrd + Copy + std::fmt::Debug {
95 let maxiter = maxiter.unwrap_or(100);
96 let mut itr = maxiter;
97 let mut x = x0;
98 let mut val = func(x0, args);
99 while val != x {
100 trace!("Iteration: {}", maxiter - itr);
101 x = val;
102 val = func(x, args);
103 trace!("\tx: {:?}; F(x): {:?}", x, val);
104 itr -= 1;
105 if itr == 0 {
106 debug!("Max Iterations reached. Last value: {:?}", val);
107 bail!(ErrorKind::IterLimit(maxiter));
108 } else if val > maxval.unwrap_or(val) {
109 debug!("Maximum value of function reached. Last value: {:?}", val);
110 bail!(ErrorKind::ValueLimit);
111 }
112 };
113 Ok(val)
114}
115
116#[cfg(test)]
117mod tests {
118 use fixedpoint;
119
120 fn func_with_fixed_point(num: u32, param: &u32) -> u32 {
121 150 + (((num as f32 / param.clone() as f32).ceil() as u32)*100)
122 }
123
124 #[test]
125 fn basic_tests() {
126 assert_eq!(fixedpoint(&func_with_fixed_point, 0, &150, None, None).unwrap(), 450);
127 assert_eq!(fixedpoint(&func_with_fixed_point, 0, &150, None, Some(400)).is_err(), true);
128 assert_eq!(fixedpoint(&func_with_fixed_point, 0, &10, Some(5), None).is_err(), true);
129 }
130
131 #[test]
132 #[should_panic(expected = "attempt to multiply with overflow")]
133 fn maxiter() {
134 fixedpoint(&func_with_fixed_point, 0, &10, Some(10), None).unwrap_or(0);
135 }
136}