compute_pi/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use rug::{Float, ops::Pow};
4use std::convert::TryFrom;
5
6/// Calculates the value of pi to a specified number of decimal places using
7/// the Gauss-Legendre algorithm and returns Float value.
8///
9/// # Arguments
10///
11/// * `digits` - The number of decimal places of pi to calculate,
12///              not to exceed 1,292,913,983.
13///
14/// # Returns
15///
16/// A `Float` representing the calculated value of pi to the specified
17/// number of decimal places.
18///
19pub fn compute_pi(digits: usize) -> Float {
20    let precision = ((digits as f64) * 3.3219280948874).ceil() as u32 + 10;
21    let threshold = Float::with_val(precision, 10).pow(-i32::try_from(digits).unwrap());
22    let mut a = Float::with_val(precision, 1);
23    let two = Float::with_val(precision, 2);
24    let mut b = Float::with_val(precision, 1.0 / two.sqrt());
25    let mut t = Float::with_val(precision, 0.25);
26    let mut p = Float::with_val(precision, 1);
27    let mut pi_old = Float::with_val(precision, 0);
28
29    let pi_result = loop {
30        let sum = a.clone() + b.clone();
31        let a_next = Float::with_val(precision, &sum / 2.0);
32        let product = Float::with_val(precision, &a * &b);
33        b = product.sqrt();
34        let difference = Float::with_val(precision, &a - &a_next);
35        let difference_squared = difference.pow(2);
36        t -= &p * difference_squared;
37        a = a_next;
38        p *= 2;
39        let denominator = Float::with_val(precision, &t * 4.0);
40        let numerator = Float::with_val(precision, (&sum).pow(2));
41        let pi = numerator / denominator;
42        let pi_diff = Float::with_val(pi.prec(), &pi - &pi_old).abs();
43        if pi_diff < threshold {
44            break pi;
45        }
46        pi_old = pi;
47    };
48    pi_result
49}
50
51/// Calculates the value of pi to a specified number of decimal places using
52/// the Gauss-Legendre algorithm and returns String value.
53///
54/// # Arguments
55///
56/// * `digits` - The number of decimal places of pi to calculate,
57///              not to exceed 1,292,913,983.
58///
59/// # Returns
60///
61/// A `String` representing the calculated value of pi to the specified
62/// number of decimal places.
63///
64pub fn compute_pi_str(digits: usize) -> String {
65    let pi = compute_pi(digits);
66    let pi_str = pi.to_string_radix(10, Some(digits + 5));
67    let pi_str_trimmed = pi_str[0..(digits + 2)].to_string();
68    pi_str_trimmed
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_compute_pi_10_digits() {
77        let pi_computed = compute_pi(10);
78        let pi_expected = Float::with_val(256, 3.1415926535);
79        assert!((pi_computed - &pi_expected).abs() < Float::with_val(256, 1e-10));
80    }
81
82    #[test]
83    fn test_compute_pi_str() {
84        // Test with 5 digits
85        let pi_str_5 = compute_pi_str(5);
86        assert_eq!(pi_str_5, "3.14159");
87
88        // Test with 10 digits
89        let pi_str_10 = compute_pi_str(10);
90        assert_eq!(pi_str_10, "3.1415926535");
91    }
92}