1use num::{BigInt, BigRational, FromPrimitive, Signed, ToPrimitive, Zero};
2use once_cell::sync::Lazy;
3use std::borrow::Cow;
4
5pub fn create_ratio(numer: i128, denom: i128) -> BigRational {
6 let numer = BigInt::from(numer);
7 let denom = BigInt::from(denom);
8 BigRational::new(numer, denom)
9}
10
11pub fn format_ratio(number: &BigRational) -> String {
12 static LENGTH: Lazy<usize> = Lazy::new(|| measure_sqrts());
13 format_inner(number, *LENGTH)
14}
15
16fn format_inner(number: &BigRational, length: usize) -> String {
17 let absolute = if number.is_negative() {
20 Cow::Owned(number.abs())
21 } else {
22 Cow::Borrowed(number)
23 };
24 let mut fraction = String::new();
25 let mut remainder = absolute.numer() % absolute.denom();
26 while !remainder.is_zero() && fraction.len() < length {
27 remainder *= BigInt::from(10);
28 fraction.push_str(&(remainder.clone() / absolute.denom()).to_string());
29 remainder %= absolute.denom();
30 }
31 let mut integer = if number.is_negative() {
32 String::from("-")
33 } else {
34 String::new()
35 };
36 integer.push_str(&(absolute.numer() / absolute.denom()).to_string());
37 if fraction.is_empty() {
38 integer
39 } else {
40 format!("{integer}.{fraction}")
41 }
42}
43
44fn measure_sqrts() -> usize {
45 let numbers = vec![2, 3, 5, 6, 7, 8];
46 numbers.iter()
47 .map(|n| measure_sqrt(*n))
48 .max()
49 .unwrap_or_default()
50}
51
52fn measure_sqrt(number: i128) -> usize {
53 create_ratio(number, 1).to_f64()
54 .map(f64::sqrt)
55 .and_then(BigRational::from_f64)
56 .map(|x| format_inner(&x, 200))
57 .map(measure_fraction)
58 .unwrap_or_default()
59}
60
61fn measure_fraction(number: String) -> usize {
62 if let Some(period) = number.find('.') {
63 number.len().checked_sub(period).unwrap_or_default()
64 } else {
65 0
66 }
67}