1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//! 平方根の連分数展開。

use super::sqrt;

use sqrt::Sqrt;

/// 平方根の連分数展開。
///
/// $\\sqrt{n}$ の連分数展開を $[a\_0; a\_1, a\_2, \\dots]$ とする。
/// このとき、$a\_\\bullet$ を生成するイテレータを返す。
///
/// # Examples
/// ```
/// use nekolib::math::sqrt_fraction;
///
/// let a: Vec<_> = sqrt_fraction(3).take(30).map(|x| x as f64).collect();
/// let r = a.into_iter().rev().reduce(|a1, a0| a0 + 1.0 / a1).unwrap();
///
/// assert!((r - 3.0_f64.sqrt()).abs() < 1.0e-16);
/// ```
pub fn sqrt_fraction(n: i128) -> impl Iterator<Item = i128> {
    let (r, next) = sqrt_fraction_fn(n);
    std::iter::successors(Some((r, r, 1)), move |&(_, b, c)| Some(next(b, c)))
        .map(|(a, ..)| a)
}

/// 平方根の連分数展開。
///
/// $\\sqrt{n}$ の連分数展開の $i$ step 目が次のように表されるとする。
/// $$ \\sqrt{n} = a\_0 +
/// \\frac{1}{\\dots\\,+\\frac{1}{a\_{i-1}+\\frac{\\sqrt{n}-b\_{i-1}}{c\_{i-1}}}}.
/// $$
///
/// $a\_0$ と、関数 $f: (b\_i, c\_i)\\mapsto (a\_{i+1}, b\_{i+1}, c\_{i+1})$
/// を返す。
/// ただし、$(a\_0, b\_0, c\_0)
/// = (\\lfloor\\sqrt{n}\\rfloor, \\lfloor\\sqrt{n}\\rfloor, 1)$ である。
///
/// 実際の連分数展開が欲しいときは [`sqrt_fraction`] を用いればよい。
/// 周期検出などをしたいときは $(b\_\\bullet, c\_\\bullet)$ が必要になる。
///
/// # Derivation
/// `todo!()`
///
/// # Examples
/// ```
/// use nekolib::math::sqrt_fraction_fn;
///
/// let (a0, next) = sqrt_fraction_fn(3);
/// assert_eq!(a0, 1);
/// let (a1, b1, c1) = next(a0, 1);
/// let (a2, b2, c2) = next(b1, c1);
/// let (a3, b3, c3) = next(b2, c2);
///
/// assert_eq!((a1, b1, c1), (a3, b3, c3));  // sqrt(3) has period 2
/// assert_eq!([a0, a1, a2], [1, 1, 2]);  // sqrt(3) = [1; (1, 2)]
/// ```
pub fn sqrt_fraction_fn(
    n: i128,
) -> (i128, impl Fn(i128, i128) -> (i128, i128, i128)) {
    let r = n.sqrt();
    let next = move |b, c| {
        assert_eq!((n - b * b) % c, 0);
        let c_ = (n - b * b) / c;
        let a_ = (r + b) / c_;
        let b_ = a_ * c_ - b;
        (a_, b_, c_)
    };
    (r, next)
}