Continued fraction/Arithmetic/G(matrix ng, continued fraction n1, continued fraction n2): Difference between revisions

Content added Content deleted
Line 1,948: Line 1,948:
val sqrt2 : continued_fraction
val sqrt2 : continued_fraction
val sqrt5 : continued_fraction
val sqrt5 : continued_fraction
</syntaxhighlight>

The second file is an implementation of the stuff declared in the first file. The second file is called <code>continued_fraction.dats</code>:

<syntaxhighlight lang="ats">
(* "Dynamic" file. (Implementations.) *)

(* To set up a predictable name-mangling scheme: *)
#define ATS_PACKNAME "rosetta-code.continued_fraction"

(* Load templates from the ATS2 prelude: *)
#include "share/atspre_staload.hats"

(* Load declarations and templates from ats2-xprelude: *)
#include "xprelude/HATS/xprelude.hats"
staload "xprelude/SATS/exrat.sats"
staload _ = "xprelude/DATS/exrat.dats"

(* Load the declarations for this package: *)
staload "continued_fraction.sats"

typedef cf_record =
(* A cf_record is an unboxed record, denoted by @{}. A boxed record
would be written '{} and would be placed in the heap. Either way,
it is an immutable record. For a mutable record, we would have to
use vtypedef to make it a LINEAR type. *)
@{
terminated = bool, (* Is the generator exhausted? *)
memo_count = size_t, (* How many terms are memoized? *)
(* An arrszref is an array with runtime bounds checking. An
arrszref is less efficient than an arrayref, but will not force
us to use dependent types for the indices. *)
memo = arrszref exrat, (* Memoized terms. *)

generate = term_generator (* The source of terms. *)
}

(* The actual type of a continued_fraction is a MUTABLE reference to
the (immutable) cf_record. Within this file, we may also call the
type cf_t. *)
typedef cf_t = ref cf_record
assume continued_fraction = cf_t

implement
continued_fraction_make generator =
let
val record : cf_record =
@{
terminated = false,
memo_count = i2sz 0,
memo = arrszref_make_elt<exrat> (i2sz 32, exrat_make (0, 1)),
generate = generator
}
in
ref record
end

(* "fn" means a non-recursive function. A function that might be
recursive is written "fun" (or sometimes "fnx"). Incidentally: it
is common to see the recursions put into nested functions, with the
function a programmer is supposed to call being non-recursive. This
is often a matter of style. (By the way, in a "*.sats" file there
is no distinction between "fn" and "fun" that I know of.) *)
fn
resize_if_necessary (cf : cf_t, i : size_t) : void =
let
val @{
terminated = terminated,
memo_count = memo_count,
memo = memo,
generate = generate
} = !cf
in
if size memo <= i then
let
val new_size = i2sz 2 * (succ i)
val new_memo =
arrszref_make_elt<exrat> (new_size, exrat_make (0, 1))
val new_record : cf_record =
@{
terminated = terminated,
memo_count = memo_count,
memo = new_memo,
generate = generate
}

var i : size_t (* A C-style automatic variable. *)
in
(* A C-style for-loop. *)
for (i := i2sz 0; i <> memo_count; i := succ i)
new_memo[i] := memo[i];

!cf := new_record
end
end

fn
update_terms (cf : cf_t, i : size_t) : void =
let
fun
loop () : void =
let
val @{
terminated = terminated,
memo_count = memo_count,
memo = memo,
generate = generate
} = !cf
in
if terminated then
()
else if i < memo_count then
()
else
case generate () of
| None () =>
let
val new_record =
@{
terminated = true,
memo_count = memo_count,
memo = memo,
generate = generate
}
in
!cf := new_record
end
| Some term =>
(* "begin-end" is a synonym for "()". *)
begin
memo[memo_count] := term;
let
val new_record =
@{
terminated = false,
memo_count = succ memo_count,
memo = memo,
generate = generate
}
in
!cf := new_record;
loop ()
end
end
end
in
loop ()
end

implement
term_exists (cf, i) =
let
val i = i2sz i

fun
loop () =
let
val @{
terminated = terminated,
memo_count = memo_count,
memo = memo,
generate = generate
} = !cf
in
if i < memo_count then
true
else if terminated then
false
else
begin
resize_if_necessary (cf, i);
update_terms (cf, i);
loop ()
end
end
in
loop ()
end

implement
get_term_exn (cf, i) =
if i2sz i < (!cf).memo_count then
let
val memo = (!cf).memo
in
memo[i]
end
else
$raise IllegalArgExn "get_term_exn:out_of_bounds"

implement
continued_fraction_to_term_generator cf =
let
val i : ref (intGte 0) = ref 0
in
lam () =<cloref1>
let
val j = !i
in
if term_exists (cf, j) then
begin
!i := succ j;
Some (cf[j])
end
else
None ()
end
end

implement default_max_terms = ref 20

implement
continued_fraction_to_string_given_max_terms (cf, max_terms) =
let
fun
loop (i : intGte 0, accum : string) : string =
if ~term_exists (cf, i) then
(* The return value of string_append is a LINEAR, MUTABLE
strptr, which we cast to a nonlinear, immutable string.
(One could introduce one's own shorthands, though.) *)
strptr2string (string_append (accum, "]"))
else if i = max_terms then
strptr2string (string_append (accum, ",...]"))
else
let
val separator =
if i = 0 then
""
else if i = 1 then
";"
else
","
and term_string = tostring_val<exrat> cf[i]
in
loop (succ i,
strptr2string (string_append (accum, separator,
term_string)))
end
in
loop (0, "[")
end

implement
continued_fraction_to_string_default_max_terms cf =
let
val max_terms = !default_max_terms
in
continued_fraction_to_string_given_max_terms (cf, max_terms)
end

implement
int_to_continued_fraction i =
let
val done : ref bool = ref false
val i = (g0i2f i) : exrat
in
continued_fraction_make
(lam () =<cloref1>
if !done then
None ()
else
begin
!done := true;
Some i
end)
end

implement
exrat_to_continued_fraction num =
let
val done : ref bool = ref false
val num : ref exrat = ref num
in
continued_fraction_make
(lam () =<cloref1>
if !done then
None ()
else
let
val q = floor !num
val r = !num - q
in
if iseqz r then
!done := true
else
!num := reciprocal r;
Some q
end)
end

implement
rational_to_continued_fraction (numer, denom) =
exrat_to_continued_fraction (exrat_make (numer, denom))

implement
continued_fraction_make_constant_term i =
let
val i = (g0i2f i) : exrat
in
continued_fraction_make (lam () =<cloref1> Some i)
end

implement
ng8_make_int tuple =
let
val @(a12, a1, a2, a, b12, b1, b2, b) = tuple
fn f (i : int) : exrat = exrat_make (i, 1)
in
@(f a12, f a1, f a2, f a, f b12, f b1, f b2, f b)
end

implement ng8_stopping_processing_threshold =
ref (exrat_make (2, 1) ** 512)
implement ng8_infinitization_threshold =
ref (exrat_make (2, 1) ** 64)

fn
too_big (term : exrat) : bool =
abs (term) >= abs (!ng8_stopping_processing_threshold)

fn
any_too_big (ng : ng8) : bool =
(* "orelse" may also be (and usually is) written "||", as in C.
The "orelse" notation resembles that of Standard ML.
Non-shortcircuiting OR also exists, and can be written "+". *)
case+ ng of (* <-- the + sign means all cases must have a match. *)
| @(a, b, c, d, e, f, g, h) =>
too_big (a) orelse too_big (b) orelse
too_big (c) orelse too_big (d) orelse
too_big (e) orelse too_big (f) orelse
too_big (g) orelse too_big (h)

fn
infinitize (term : exrat) : Option exrat =
if abs (term) >= abs (!ng8_infinitization_threshold) then
None ()
else
Some term

val no_terms_source : term_generator =
lam () =<cloref1> None ()

fn
divide (a : exrat, b : exrat) : @(exrat, exrat) =
if iseqz b then
@(exrat_make (0, 1), exrat_make (0, 1))
else
(* Do integer division if the numerators of a and b. The following
particular function does floor division if the divisor is
positive, ceiling division if the divisor is negative. Thus the
remainder is never negative. *)
exrat_numerator_euclid_division (a, b)

implement
ng8_apply ng =
lam (x, y) =>
let
val ng : ref ng8 = ref ng
and xsource : ref term_generator =
ref (continued_fraction_to_term_generator x)
and ysource : ref term_generator =
ref (continued_fraction_to_term_generator y)

fn
all_b_are_zero () : bool =
let
val @(_, _, _, _, b12, b1, b2, b) = !ng
in
(* Instead of the Standard ML-like notation "andalso", one
may (and usually does) use the C-like notation
"&&". There is also non-shortcircuiting AND, written
"*". *)
iseqz b andalso
iseqz b2 andalso
iseqz b1 andalso
iseqz b12
end

fn
all_four_equal (a : exrat, b : exrat,
c : exrat, d : exrat) : bool =
a = b && a = c && a = d

fn
absorb_x_term () =
let
val @(a12, a1, a2, a, b12, b1, b2, b) = !ng
in
case (!xsource) () of
| Some term =>
let
val new_ng = (a2 + (a12 * term),
a + (a1 * term), a12, a1,
b2 + (b12 * term),
b + (b1 * term), b12, b1)
in
if any_too_big new_ng then
(* Pretend all further x terms are infinite. *)
(!ng := @(a12, a1, a12, a1, b12, b1, b12, b1);
!xsource := no_terms_source)
else
!ng := new_ng
end
| None () =>
!ng := @(a12, a1, a12, a1, b12, b1, b12, b1)
end

fn
absorb_y_term () =
let
val @(a12, a1, a2, a, b12, b1, b2, b) = !ng
in
case (!ysource) () of
| Some term =>
let
val new_ng = (a1 + (a12 * term), a12,
a + (a2 * term), a2,
b1 + (b12 * term), b12,
b + (b2 * term), b2)
in
if any_too_big new_ng then
(* Pretend all further y terms are infinite. *)
(!ng := @(a12, a12, a2, a2, b12, b12, b2, b2);
!ysource := no_terms_source)
else
!ng := new_ng
end
| None () =>
!ng := @(a12, a12, a2, a2, b12, b12, b2, b2)
end

fun
loop () =
(* ATS2 can do mutual recursion with proper tail calls, but,
to stay closer to the Standard ML code, here I use only
single tail recursion. To do mutual recursion with proper
tail calls, one says "fnx" instead of "fun". *)
if all_b_are_zero () then
None () (* There are no more terms to output. *)
else
let
val @(_, _, _, _, b12, b1, b2, b) = !ng
in
if iseqz b andalso iseqz b2 then
(absorb_x_term (); loop ())
else if iseqz b orelse iseqz b2 then
(absorb_y_term (); loop ())
else if iseqz b1 then
(absorb_x_term (); loop ())
else
let
val @(a12, a1, a2, a, _, _, _, _) = !ng
val @(q12, r12) = divide (a12, b12)
and @(q1, r1) = divide (a1, b1)
and @(q2, r2) = divide (a2, b2)
and @(q, r) = divide (a, b)
in
if isneqz b12 andalso
all_four_equal (q12, q1, q2, q) then
(!ng := (b12, b1, b2, b, r12, r1, r2, r);
(* Return a term--or, if a magnitude threshold is
reached, return no more terms . *)
infinitize q)
else
let
(* Put a1, a2, and a over a common denominator and
compare some magnitudes. (SIDE NOTE: We are
representing big integers as EXACT rationals
with denominator one, so in fact could have put
a1, a2, and a over their respective
denominators and compared the
fractions. However, I have retained the
phrasing of the Standard ML program.) *)
val n1 = a1 * b2 * b
and n2 = a2 * b1 * b
and n = a * b1 * b2
in
if abs (n1 - n) > abs (n2 - n) then
(absorb_x_term (); loop ())
else
(absorb_y_term (); loop ())
end
end
end
in
continued_fraction_make (lam () =<cloref1> loop ())
end

(* A macro definition: *)
macdef make_op (tuple) = ng8_apply (ng8_make_int ,(tuple))

implement ng8_apply_add = make_op @(0, 1, 1, 0, 0, 0, 0, 1)
implement ng8_apply_sub = make_op @(0, 1, ~1, 0, 0, 0, 0, 1)
implement ng8_apply_mul = make_op @(1, 0, 0, 0, 0, 0, 0, 1)
implement ng8_apply_div = make_op @(0, 1, 0, 0, 0, 0, 1, 0)

(* Here the closure is "wrapped" in an ordinary function. *)
val _ng8_apply_neg = make_op @(0, 0, ~1, 0, 0, 0, 0, 1)
implement ng8_apply_neg cf = _ng8_apply_neg (cf, cf)


val _reciprocal = make_op @(0, 0, 0, 1, 0, 1, 0, 0)

implement
ng8_apply_pow (cf, i) =
let
macdef reciprocal cf = _reciprocal (,(cf), ,(cf))

fun
loop (x : continued_fraction,
n : int,
accum : continued_fraction) : continued_fraction =
if 1 < n then
let
val nhalf = n / 2
and xsquare = x * x
in
if nhalf + nhalf <> n then
loop (xsquare, nhalf, accum * x)
else
loop (xsquare, nhalf, accum)
end
else if n = 1 then
accum * x
else
accum
in
if 0 <= i then
loop (cf, i, one)
else
reciprocal (loop (cf, ~i, one))
end

implement zero = i2cf 0
implement one = i2cf 1
implement two = i2cf 2
implement three = i2cf 3
implement four = i2cf 4

implement one_fourth = r2cf (1, 4)
implement one_third = r2cf (1, 3)
implement one_half = r2cf (1, 2)
implement two_thirds = r2cf (2, 3)
implement three_fourths = r2cf (3, 4)

implement golden_ratio = constant_term_cf 1
implement silver_ratio = constant_term_cf 2
implement sqrt2 = silver_ratio - one
implement sqrt5 = (two * golden_ratio) - one
</syntaxhighlight>
</syntaxhighlight>