-
Notifications
You must be signed in to change notification settings - Fork 1
/
p66.re
67 lines (56 loc) · 2.11 KB
/
p66.re
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
/* Misc. helpers */
let range = (min, max) => Array.to_list(Array.init(max - min + 1, (i) => min + i));
let floor_of_sqrt = (n) => int_of_float(sqrt(float_of_int(n)));
let even = (n: int) => (n mod 2) == 0;
let first = ((a, b)) => a;
let all_but_last = (l) => List.rev(List.tl(List.rev(l)));
let not_square = (n) => {
let root = floor_of_sqrt(n);
root * root != n;
}
/* Big rationals (note: There is no native "big integer" or rational support, but a (float, float) tuple works for this particular problem) */
type ratio = (float, float);
let invert = ((n, d): ratio) => (d, n);
let add_int = (a: float, (bn, bd): ratio) => (a *. bd +. bn, bd);
let rec compute_fraction = (terms: list(int)) => switch (terms) {
| [a] => (float_of_int(a), 1.);
| [a, ...rest] => add_int(float_of_int(a), invert(compute_fraction(rest)));
| [] => raise(Arg.Bad("No terms supplied"));
}
/* Continued fraction convergents */
type expansion = { a: int, b: int, c: int };
let find_convergents = (n: int) => {
let rec recurse = (a0: int, seen: list(expansion), count: int) => {
let previous = List.hd(seen);
let c = previous.a * previous.b - previous.c;
let b = (n - c * c) / previous.b;
let a = (a0 + c) / b;
let next = {a, b, c};
switch (List.find_opt((e) => e == next, seen)) {
| Some(_) => (count, seen |> List.rev |> List.map((e) => e.a))
| None => recurse(a0, [next, ...seen], count + 1)
};
}
let a0: int = floor_of_sqrt(n);
recurse(a0, [{ a: a0, b: 1, c: 0 }], 0);
}
/* Solve Pell's equation using continued fractions approach */
let find_fundamental_solution = (n) => {
let (period, terms) = find_convergents(n);
if (even(period)) {
compute_fraction(all_but_last(terms));
} else {
let a0 = List.hd(terms);
let rest = List.tl(terms);
compute_fraction(all_but_last(List.concat([[a0], rest, rest])));
}
}
/* Main entry point */
let solve = (n) => {
range(1, n)
|> List.filter(not_square)
|> List.map((n) => (n, first(find_fundamental_solution(n))))
|> List.fold_left(((an, ax), (bn, bx)) => (bx > ax) ? (bn, bx) : (an, ax), (0, 0.))
|> first;
}
Js.log(solve(1000));