compute_tau/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use rug::{Float, ops::Pow};
4use std::{convert::TryFrom, f64::consts::LOG2_10};
5
6/// Calculates the value of tau 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 tau to calculate,
12///              not to exceed 1,292,913,982.
13///
14/// # Returns
15///
16/// A `Float` representing the calculated value of tau to the specified
17/// number of decimal places.
18///
19pub fn compute_tau(digits: usize) -> Float {
20    let precision = ((digits as f64) * (LOG2_10 + 1e-11)).ceil() as u32 + 10 + 1;
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, two.sqrt() / 2);
25    let mut t = Float::with_val(precision, 0.25);
26    let mut p = Float::with_val(precision, 1);
27    let mut tau_old = Float::with_val(precision, 0);
28
29    loop {
30        let sum = a.clone() + &b;
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 * 2.0);
40        let numerator = Float::with_val(precision, (&sum).pow(2));
41        let tau: Float = numerator / denominator;
42        let tau_diff = Float::with_val(tau.prec(), &tau - &tau_old).abs();
43        if tau_diff < threshold {
44            break tau;
45        }
46        tau_old = tau;
47    }
48}
49
50/// Calculates the value of tau to a specified number of decimal places using
51/// the Gauss-Legendre algorithm and returns String value.
52///
53/// # Arguments
54///
55/// * `digits` - The number of decimal places of tau to calculate,
56///              not to exceed 1,292,913,982.
57///
58/// # Returns
59///
60/// A `String` representing the calculated value of tau to the specified
61/// number of decimal places.
62///
63pub fn compute_tau_str(digits: usize) -> String {
64    let tau = compute_tau(digits);
65    let tau_str = tau.to_string_radix(10, Some(digits + 5));
66    tau_str[0..(digits + 2)].to_string()
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_compute_tau_10_digits() {
75        let tau_computed = compute_tau(10);
76        let tau_expected = Float::with_val(256, 6.2831853071);
77        assert!((tau_computed - &tau_expected).abs() < Float::with_val(256, 1e-10));
78    }
79
80    #[test]
81    fn test_compute_tau_str() {
82        // Test with 5 digits
83        let tau_str_5 = compute_tau_str(5);
84        assert_eq!(tau_str_5, "6.28318");
85
86        // Test with 10 digits
87        let tau_str_10 = compute_tau_str(10);
88        assert_eq!(tau_str_10, "6.2831853071");
89    }
90}