Continued fraction/Arithmetic/G(matrix ng, continued fraction n): Difference between revisions
Promote to full task |
|||
Line 1: | Line 1: | ||
{{ |
{{task}} |
||
This task investigates mathmatical operations that can be performed on a single continued fraction. This requires only a baby version of NG: |
This task investigates mathmatical operations that can be performed on a single continued fraction. This requires only a baby version of NG: |
||
: <math>\begin{bmatrix} |
: <math>\begin{bmatrix} |
||
Line 824: | Line 824: | ||
{{libheader|Boehm GC}} |
{{libheader|Boehm GC}} |
||
I use a garbage collector to make the free use of heap space both easier and more error-free |
I use a garbage collector to make the free use of heap space both easier and more error-free. |
||
<syntaxhighlight lang="c"> |
<syntaxhighlight lang="c"> |
Revision as of 13:40, 21 February 2023
You are encouraged to solve this task according to the task description, using any language you may know.
This task investigates mathmatical operations that can be performed on a single continued fraction. This requires only a baby version of NG:
I may perform perform the following operations:
- Input the next term of N1
- Output a term of the continued fraction resulting from the operation.
I output a term if the integer parts of and are equal. Otherwise I input a term from N. If I need a term from N but N has no more terms I inject .
When I input a term t my internal state: is transposed thus
When I output a term t my internal state: is transposed thus
When I need a term t but there are no more my internal state: is transposed thus
I am done when b1 and b are zero.
Demonstrate your solution by calculating:
- [1;5,2] + 1/2
- [3;7] + 1/2
- [3;7] divided by 4
Using a generator for (e.g., from Continued fraction) calculate . You are now at the starting line for using Continued Fractions to implement Arithmetic-geometric mean without ulps and epsilons.
The first step in implementing Arithmetic-geometric mean is to calculate do this now to cross the starting line and begin the race.
11l
T NG
Int a1, a, b1, b
F (a1, a, b1, b)
.a1 = a1
.a = a
.b1 = b1
.b = b
F ingress(n)
(.a, .a1) = (.a1, .a + .a1 * n)
(.b, .b1) = (.b1, .b + .b1 * n)
F needterm()
R (.b == 0 | .b1 == 0) | !(.a I/ .b == .a1 I/ .b1)
F egress()
V n = .a I/ .b
(.a, .b) = (.b, .a - .b * n)
(.a1, .b1) = (.b1, .a1 - .b1 * n)
R n
F egress_done()
I .needterm()
(.a, .b) = (.a1, .b1)
R .egress()
F done()
R .b == 0 & .b1 == 0
F r2cf(=n1, =n2)
[Int] r
L n2 != 0
(n1, V t1_n2) = (n2, divmod(n1, n2))
n2 = t1_n2[1]
r [+]= t1_n2[0]
R r
V data = [(‘[1;5,2] + 1/2’, 2,1,0,2, (13, 11)),
(‘[3;7] + 1/2’, 2,1,0,2, (22, 7)),
(‘[3;7] divided by 4’, 1,0,0,4, (22, 7))]
L(string, a1, a, b1, b, r) data
print(‘#<20->’.format(string), end' ‘’)
V op = NG(a1, a, b1, b)
L(n) r2cf(r[0], r[1])
I !op.needterm()
print(‘ ’op.egress(), end' ‘’)
op.ingress(n)
L
print(‘ ’op.egress_done(), end' ‘’)
I op.done()
L.break
print()
- Output:
[1;5,2] + 1/2 -> 1 1 2 7 [3;7] + 1/2 -> 3 1 1 1 4 [3;7] divided by 4 -> 0 1 3 1 2
ATS
For no reason despite my curiosity, the program outputs its results in MathML.
(*------------------------------------------------------------------*)
#include "share/atspre_staload.hats"
(* We need consistent definitions of division and remainder. Let us
set those here. For convenience (because the prelude provides it),
we will use truncation towards zero. *)
infixl ( / ) div
infixl ( mod ) rem
macdef div = g0int_div
macdef rem = g0int_mod
(* Some useful math Unicode, so we do not need entities in markup
using these characters. *)
val plus_sign = "+"
val dot_operator = "⋅"
val centered_ellipsis = "⋯"
val right_arrow = "→"
(*------------------------------------------------------------------*)
(* Continued fractions as processes for generating terms. The terms
are memoized and are accessed by their zero-based index. The terms
are represented as any one of the signed integer types for which
there is a typekind. *)
abstype cf (tk : tkind) = ptr
(* A ref of a cf has the advantage, over a cf itself, that it can be
more safely used in a closure. *)
typedef cfref (tk : tkind) = ref (cf tk)
typedef cf_generator (tk : tkind) =
() -<cloref1> Option (g0int tk)
extern fn {tk : tkind}
cf_make :
cf_generator tk -> cf tk
extern fn {tk : tkind}
cfref_make :
cf_generator tk -> cfref tk
extern fn {tk : tkind}
{tki : tkind}
cf_get_at_guint :
{i : int}
(&cf tk >> _, g1uint (tki, i)) -> Option (g0int tk)
extern fn {tk : tkind}
{tki : tkind}
cf_get_at_gint :
{i : nat}
(&cf tk >> _, g1int (tki, i)) -> Option (g0int tk)
overload cf_get_at with cf_get_at_guint
overload cf_get_at with cf_get_at_gint
overload [] with cf_get_at
macdef cfref_get_at (cfref, i) =
let
val @(pf, fpf | p) = ref_vtakeout ,(cfref)
val retval = cf_get_at (!p, ,(i))
prval () = fpf pf
in
retval
end
extern fn {tk : tkind}
cf2mathml_max_terms
(cf : &cf tk >> _,
max_terms : size_t)
: string
extern fn {tk : tkind}
cf2mathml_default_max_terms
(cf : &cf tk >> _)
: string
extern fn {tk : tkind}
cfref2mathml_max_terms
(cfref : cfref tk,
max_terms : size_t)
: string
extern fn {tk : tkind}
cfref2mathml_default_max_terms
(cfref : cfref tk)
: string
overload cf2mathml with cf2mathml_max_terms
overload cf2mathml with cf2mathml_default_max_terms
overload cfref2mathml with cfref2mathml_max_terms
overload cfref2mathml with cfref2mathml_default_max_terms
(* To use a cfref as a generator, you need cfref2generator. It would
do no good to use the cf object's internal generator directly,
because its state would be wrong. In any case, the internals of a
cf are hidden from the programmer. *)
extern fn {tk : tkind}
cfref2generator :
cfref tk -> cf_generator tk
(* - - - - - - - - - - - - - *)
local
typedef _cf (tk : tkind,
terminated : bool,
m : int,
n : int) =
[m <= n]
'{
terminated = bool terminated, (* No more terms? *)
m = size_t m, (* The number of terms computed so far. *)
n = size_t n, (* The size of memo storage.*)
memo = arrayref (g0int tk, n), (* Memoized terms. *)
gen = cf_generator tk (* A thunk to generate terms. *)
}
typedef _cf (tk : tkind, m : int) =
[terminated : bool]
[n : int | m <= n]
_cf (tk, terminated, m, n)
typedef _cf (tk : tkind) =
[m : int]
_cf (tk, m)
fn {tk : tkind}
_cf_get_more_terms
{terminated : bool}
{m : int}
{n : int}
{needed : int | m <= needed; needed <= n}
(cf : _cf (tk, terminated, m, n),
needed : size_t needed)
: [m1 : int | m <= m1; m1 <= needed]
[n1 : int | m1 <= n1]
_cf (tk, m1 < needed, m1, n1) =
let
prval () = lemma_g1uint_param (cf.m)
macdef memo = cf.memo
fun
loop {i : int | m <= i; i <= needed}
.<needed - i>.
(i : size_t i)
: [m1 : int | m <= m1; m1 <= needed]
[n1 : int | m1 <= n1]
_cf (tk, m1 < needed, m1, n1) =
if i = needed then
'{
terminated = false,
m = needed,
n = (cf.n),
memo = memo,
gen = (cf.gen)
}
else
begin
case+ (cf.gen) () of
| None () =>
'{
terminated = true,
m = i,
n = (cf.n),
memo = memo,
gen = (cf.gen)
}
| Some term =>
begin
memo[i] := term;
loop (succ i)
end
end
in
loop (cf.m)
end
fn {tk : tkind}
_cf_update {terminated : bool}
{m : int}
{n : int | m <= n}
{needed : int}
(cf : _cf (tk, terminated, m, n),
needed : size_t needed)
: [terminated1 : bool]
[m1 : int | m <= m1]
[n1 : int | m1 <= n1]
_cf (tk, terminated1, m1, n1) =
let
prval () = lemma_g1uint_param (cf.m)
macdef memo = cf.memo
macdef gen = cf.gen
in
if (cf.terminated) then
cf
else if needed <= (cf.m) then
cf
else if needed <= (cf.n) then
_cf_get_more_terms<tk> (cf, needed)
else
let (* Provides twice the room needed. *)
val n1 = needed + needed
val memo1 = arrayref_make_elt (n1, g0i2i 0)
val () =
let
var i : [i : nat] size_t i
in
for (i := i2sz 0; i < (cf.m); i := succ i)
memo1[i] := memo[i]
end
val cf1 =
'{
terminated = false,
m = (cf.m),
n = n1,
memo = memo1,
gen = (cf.gen)
}
in
_cf_get_more_terms<tk> (cf1, needed)
end
end
in (* local *)
assume cf tk = _cf tk
implement {tk}
cf_make gen =
let
#ifndef CF_START_SIZE #then
#define CF_START_SIZE 8
#endif
in
'{
terminated = false,
m = i2sz 0,
n = i2sz CF_START_SIZE,
memo = arrayref_make_elt (i2sz CF_START_SIZE, g0i2i 0),
gen = gen
}
end
implement {tk}
cfref_make gen =
ref (cf_make gen)
implement {tk} {tki}
cf_get_at_guint {i} (cf, i) =
let
prval () = lemma_g1uint_param i
val i : size_t i = g1u2u i
val cf1 = _cf_update<tk> (cf, succ i)
in
cf := cf1;
if i < (cf1.m) then
Some (arrayref_get_at<g0int tk> (cf1.memo, i))
else
None ()
end
implement {tk} {tki}
cf_get_at_gint (cf, i) =
cf_get_at_guint<tk><sizeknd> (cf, g1i2u i)
end (* local *)
implement {tk}
cf2mathml_max_terms (cf, max_terms) =
let
fun
loop (i : Size_t,
cf : &cf tk >> _,
sep : string,
accum : string)
: string =
if i = max_terms then
strptr2string
(string_append
(accum, "<mo>,</mo><mo>",
centered_ellipsis, "</mo><mo>]</mo>"))
else
begin
case+ cf[i] of
| None () =>
strptr2string (string_append (accum, "<mo>]</mo>"))
| Some term =>
let
val term_str = tostring_val<g0int tk> term
val accum =
strptr2string (string_append (accum, sep, "<mn>",
term_str, "</mn>"))
val sep =
if sep = "<mo>[</mo>" then
"<mo>;</mo>"
else
"<mo>,</mo>"
in
loop (succ i, cf, sep, accum)
end
end
in
loop (i2sz 0, cf, "<mo>[</mo>", "")
end
implement {tk}
cf2mathml_default_max_terms cf =
let
#ifndef DEFAULT_CF_MAX_TERMS #then
#define DEFAULT_CF_MAX_TERMS 20
#endif
in
cf2mathml_max_terms (cf, i2sz DEFAULT_CF_MAX_TERMS)
end
implement {tk}
cfref2mathml_max_terms (cfref, max_terms) =
let
val @(pf, fpf | p) = ref_vtakeout cfref
val retval = cf2mathml_max_terms (!p, max_terms)
prval () = fpf pf
in
retval
end
implement {tk}
cfref2mathml_default_max_terms cfref =
let
val @(pf, fpf | p) = ref_vtakeout cfref
val retval = cf2mathml_default_max_terms !p
prval () = fpf pf
in
retval
end
implement {tk}
cfref2generator cfref =
let
val index : ref Size_t = ref (i2sz 0)
in
lam () =>
let
val i = !index
val retval = cfref_get_at (cfref, i)
in
!index := succ i;
retval
end
end
(*------------------------------------------------------------------*)
(* A homographic function. *)
typedef hfunc (tk : tkind) =
@{
a1 = g0int tk,
a = g0int tk,
b1 = g0int tk,
b = g0int tk
}
extern fn {tk : tkind}
hfunc_make :
(g0int tk, g0int tk, g0int tk, g0int tk) -<> hfunc tk
extern fn {tk : tkind}
hfunc_apply_generator2generator :
(hfunc tk, cf_generator tk) -> cf_generator tk
extern fn {tk : tkind}
hfunc_apply_cfref2cfref :
(hfunc tk, cfref tk) -> cfref tk
overload hfunc_apply with hfunc_apply_generator2generator
overload hfunc_apply with hfunc_apply_cfref2cfref
(* - - - - - - - - - - - - - *)
implement {tk}
hfunc_make (a1, a, b1, b) =
@{
a1 = a1,
a = a,
b1 = b1,
b = b
}
fn {tk : tkind}
take_term_from_ngen
(state : ref (hfunc tk),
ngen : cf_generator tk)
: void =
let
val @{
a1 = a1,
a = a,
b1 = b1,
b = b
} = !state
in
case+ ngen () of
| Some term =>
!state :=
@{
a1 = a + (a1 * term),
a = a1,
b1 = b + (b1 * term),
b = b1
}
| None () =>
!state :=
@{
a1 = a1,
a = a1,
b1 = b1,
b = b1
}
end
fn {tk : tkind}
adjust_state_for_term_output
(state : ref (hfunc tk),
term : g0int tk)
: void =
let
val @{
a1 = a1,
a = a,
b1 = b1,
b = b
} = !state
in
!state :=
@{
a1 = b1,
a = b,
b1 = a1 - (b1 * term),
b = a - (b * term)
}
end
implement {tk}
hfunc_apply_generator2generator (f, ngen) =
let
val state : ref (hfunc tk) = ref f
val hgen =
lam () =<cloref1>
let
fun
loop () : Option (g0int tk) =
let
val b1_iseqz = iseqz (!state.b1)
and b_iseqz = iseqz (!state.b)
in
if b1_iseqz * b_iseqz then
None ()
else if (~b1_iseqz) * (~b_iseqz) then
let
val q1 = (!state.a1) div (!state.b1)
and q = (!state.a) div (!state.b)
in
if q1 = q then
begin
adjust_state_for_term_output<tk> (state, q);
Some q
end
else
begin
take_term_from_ngen (state, ngen);
loop ()
end
end
else
begin
take_term_from_ngen (state, ngen);
loop ()
end
end
in
loop ()
end
in
hgen : cf_generator tk
end
implement {tk}
hfunc_apply_cfref2cfref (f, cfref) =
let
val gen1 = cfref2generator<tk> cfref
val gen2 = hfunc_apply_generator2generator<tk> (f, gen1)
in
cfref_make<tk> gen2
end
(*------------------------------------------------------------------*)
(* Let us create some continued fractions. *)
extern fn {tk : tkind}
r2cf :
(g0int tk, g0int tk) -> cf tk
implement {tk}
r2cf (n, d) =
let
val n = ref<g0int tk> n
val d = ref<g0int tk> d
fn
gen () :<cloref1> Option (g0int tk) =
if iseqz !d then
None ()
else
let
val @(numer, denom) = @(!n, !d)
val q = numer div denom
and r = numer rem denom
in
!n := denom;
!d := r;
Some q
end
in
cf_make gen
end
val cfref_13_11 = ref (r2cf (13LL, 11LL)) (* 13/11 = [1;5,2] *)
val cfref_22_7 = ref (r2cf (22LL, 7LL)) (* 22/7 = [3;7] *)
val cfref_sqrt2 = (* sqrt(2) = [1;2,2,2,...] *)
let
val term : ref llint = ref 1LL
val gen =
lam () =<cloref1>
let
val retval = !term
in
if retval = 1LL then
!term := 2LL;
Some retval
end
in
cfref_make (gen : cf_generator llintknd)
end
(*------------------------------------------------------------------*)
(* Let us create some homographic functions that correspond to unary
arithmetic operations. *)
val add_one_half = hfunc_make (2LL, 1LL, 0LL, 2LL)
val add_one = hfunc_make (1LL, 1LL, 0LL, 1LL)
val divide_by_two = hfunc_make (1LL, 0LL, 0LL, 2LL)
val divide_by_four = hfunc_make (1LL, 0LL, 0LL, 4LL)
val take_reciprocal = hfunc_make (0LL, 1LL, 1LL, 0LL)
val add_one_then_div_two = hfunc_make (1LL, 1LL, 0LL, 2LL)
val add_two_then_div_four = hfunc_make (1LL, 2LL, 0LL, 4LL)
(*------------------------------------------------------------------*)
(* Now let us derive some continued fractions. *)
local
macdef apply = hfunc_apply<llintknd>
macdef cfref2ml = cfref2mathml<llintknd>
in
val cfref_13_11_plus_1_2 = apply (add_one_half, cfref_13_11)
val cfref_22_7_plus_1_2 = apply (add_one_half, cfref_22_7)
val cfref_22_7_div_4 = apply (divide_by_four, cfref_22_7)
(* The following two give the same result: *)
val cfref_sqrt2_div_2 = apply (divide_by_two, cfref_sqrt2)
val cfref_1_div_sqrt2 = apply (take_reciprocal, cfref_sqrt2)
val () = assertloc (cfref2ml cfref_sqrt2_div_2
= cfref2ml cfref_1_div_sqrt2)
(* The following three give the same result: *)
val cfref_2_plus_sqrt2_grouped_div_4 =
apply (add_two_then_div_four, cfref_sqrt2)
val cfref_half_of_1_plus_half_sqrt2 =
apply (add_one_then_div_two, cfref_sqrt2_div_2)
val cfref_half_of_1_plus_1_div_sqrt2 =
apply (divide_by_two, (apply (add_one, cfref_sqrt2_div_2)))
val () = assertloc (cfref2ml cfref_2_plus_sqrt2_grouped_div_4
= cfref2ml cfref_half_of_1_plus_half_sqrt2)
val () = assertloc (cfref2ml cfref_half_of_1_plus_half_sqrt2
= cfref2ml cfref_half_of_1_plus_1_div_sqrt2)
end
(*------------------------------------------------------------------*)
implement
main () =
let
macdef cfref2ml = cfref2mathml<llintknd>
macdef apply = hfunc_apply<llintknd>
macdef text (s) =
strptr2string (string_append ("<p>", ,(s), "</p>"))
macdef becomes =
strptr2string (string_append ("<mo>", right_arrow, "</mo>"))
macdef start_math =
"<math xmlns='http://www.w3.org/1998/Math/MathML'>"
macdef stop_math = "</math>"
macdef start_table = "<mtable>"
macdef stop_table = "</mtable>"
macdef left_side (s) =
strptr2string
(string_append
("<mtd columnalign='right'>", ,(s), "</mtd>"))
macdef middle (s) =
strptr2string
(string_append
("<mtd columnalign='center'>", ,(s), "</mtd>"))
macdef right_side (s) =
strptr2string
(string_append
("<mtd columnalign='left'>", ,(s), "</mtd>"))
macdef entry (left, right) =
strptr2string
(string_append
("<mtr>",
left_side (,(left)),
middle becomes,
right_side (,(right)),
"</mtr>"))
macdef num s =
strptr2string (string_append ("<mn>", ,(s), "</mn>"))
macdef id s =
strptr2string (string_append ("<mi>", ,(s), "</mi>"))
macdef oper s =
strptr2string (string_append ("<mo>", ,(s), "</mo>"))
macdef frac (n, d) =
strptr2string (string_append ("<mfrac>", ,(n), ,(d),
"</mfrac>"))
macdef numfrac (n, d) = frac (num ,(n), num ,(d))
macdef sqrt_of (s) =
strptr2string (string_append ("<msqrt>", ,(s), "</msqrt>"))
in
println! (start_math);
println! (start_table);
println! (entry (numfrac ("13", "11"), cfref2ml cfref_13_11));
println! (entry (numfrac ("22", "7"), cfref2ml cfref_22_7));
println! (entry (sqrt_of (num "2"), cfref2ml cfref_sqrt2));
println! (entry (strptr2string
(string_append (numfrac ("13", "11"),
oper plus_sign,
numfrac ("1", "2"))),
cfref2ml cfref_13_11_plus_1_2));
println! (entry (strptr2string
(string_append (numfrac ("22", "7"),
oper plus_sign,
numfrac ("1", "2"))),
cfref2ml cfref_22_7_plus_1_2));
println! (entry (frac (numfrac ("22", "7"), num ("4")),
cfref2ml cfref_22_7_div_4));
println! (entry (frac (sqrt_of (num "2"), num ("2")),
cfref2ml cfref_sqrt2_div_2));
println! (entry (frac (num ("1"), sqrt_of (num "2")),
cfref2ml cfref_1_div_sqrt2));
println! (entry (strptr2string
(string_append
(numfrac ("1", "4"),
oper dot_operator,
strptr2string
(string_append
(oper "(", num "2", oper plus_sign,
sqrt_of (num "2"), oper ")")))),
cfref2ml cfref_2_plus_sqrt2_grouped_div_4));
println! (entry (strptr2string
(string_append
(numfrac ("1", "2"),
oper dot_operator,
strptr2string
(string_append
(oper "(", num "1", oper plus_sign,
frac (sqrt_of (num "2"), num "2"),
oper ")")))),
cfref2ml cfref_half_of_1_plus_half_sqrt2));
println! (entry (strptr2string
(string_append
(numfrac ("1", "2"),
oper dot_operator,
strptr2string
(string_append
(oper "(", num "1", oper plus_sign,
frac (num "1", sqrt_of (num "2")),
oper ")")))),
cfref2ml cfref_half_of_1_plus_1_div_sqrt2));
println! (stop_table);
println! (stop_math);
0
end
(*------------------------------------------------------------------*)
- Output:
C
I use a garbage collector to make the free use of heap space both easier and more error-free.
/*------------------------------------------------------------------*/
/* For C with Boehm GC as garbage collector. */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <gc.h> /* Boehm GC. */
/*------------------------------------------------------------------*/
/* Let us choose an integer type. */
typedef long long int integer;
/* We need consistent definitions of division and remainder. Let us
set those here. For convenience (because C provides it), we will
use truncation towards zero. */
#define DIV(a, b) ((a) / (b))
#define REM(a, b) ((a) % (b))
/* Choose a memory allocator: Boehm GC. (Ideally one should check for
NULL return values, but for this pedagogical example let us skip
that.) */
#define MALLOC_INIT() GC_INIT ()
#define MALLOC GC_MALLOC
#define REALLOC GC_REALLOC
#define FREE GC_FREE /* Or one could make this a no-op. */
/*------------------------------------------------------------------*/
/* Some operations on char-strings. (In practice, I would write an m4
macro to create such repetitive C functions for me. Of course, it
is also possible to use <stdarg.h> or some such [generally unsafe]
mechanism.) */
static char *
string_append1 (const char *s1)
{
size_t n1 = strlen (s1);
char *s = MALLOC (n1 + 1);
s[n1] = '\0';
memcpy (s, s1, n1);
return s;
}
static char *
string_append2 (const char *s1, const char *s2)
{
size_t n1 = strlen (s1);
size_t n2 = strlen (s2);
char *s = MALLOC (n1 + n2 + 1);
s[n1 + n2] = '\0';
memcpy (s, s1, n1);
memcpy (s + n1, s2, n2);
return s;
}
static char *
string_append3 (const char *s1, const char *s2, const char *s3)
{
size_t n1 = strlen (s1);
size_t n2 = strlen (s2);
size_t n3 = strlen (s3);
char *s = MALLOC (n1 + n2 + n3 + 1);
s[n1 + n2 + n3] = '\0';
memcpy (s, s1, n1);
memcpy (s + n1, s2, n2);
memcpy (s + n1 + n2, s3, n3);
return s;
}
/*------------------------------------------------------------------*/
/* Continued fractions as processes for generating terms. The terms
are memoized and are accessed by their zero-based index. */
typedef void cf_generator_func_t (void *env, bool *there_is_a_term,
integer *term);
struct _cf_generator /* For practical purposes, this is a closure. */
{
cf_generator_func_t *func;
void *env;
};
typedef struct _cf_generator *cf_generator_t;
struct _cf
{
bool terminated; /* No more terms? */
size_t m; /* The number of terms computed so far. */
size_t n; /* The size of memo storage. */
integer *memo; /* Memoized terms. */
cf_generator_t gen; /* A closure to generate terms. */
};
typedef struct _cf *cf_t;
cf_generator_t
cf_generator_make (cf_generator_func_t func, void *env)
{
cf_generator_t gen = MALLOC (sizeof (struct _cf_generator));
gen->func = func;
gen->env = env;
return gen;
}
cf_t
cf_make (cf_generator_t gen)
{
const size_t start_size = 8;
integer *memo = MALLOC (start_size * sizeof (integer));
cf_t cf = MALLOC (sizeof (struct _cf));
cf->terminated = false;
cf->m = 0;
cf->n = start_size;
cf->memo = memo;
cf->gen = gen;
return cf;
}
static void
_cf_get_more_terms (cf_t cf, size_t needed)
{
size_t term_count = cf->m;
bool done = false;
while (!done)
{
if (term_count == needed)
{
cf->m = needed;
done = true;
}
else
{
bool there_is_a_term;
integer term;
cf->gen->func (cf->gen->env, &there_is_a_term, &term);
if (there_is_a_term)
{
cf->memo[term_count] = term;
term_count += 1;
}
else
{
cf->terminated = true;
cf->m = term_count;
done = true;
}
}
}
}
static void
_cf_update (cf_t cf, size_t needed)
{
if (cf->terminated || needed <= (cf->m))
/* Do nothing. */ ;
else if (needed <= (cf->n))
_cf_get_more_terms (cf, needed);
else
{
/* Provide twice the needed storage. */
cf->n = 2 * needed;
cf->memo = REALLOC (cf->memo, cf->n * sizeof (integer));
_cf_get_more_terms (cf, needed);
}
}
void
cf_get_at (cf_t cf, size_t i, bool *there_is_a_term,
integer *term)
{
_cf_update (cf, i + 1);
*there_is_a_term = (i < (cf->m));
if (*there_is_a_term)
*term = cf->memo[i];
}
char *
cf2string_max_terms (cf_t cf, size_t max_terms)
{
char *s = string_append1 ("[");
const char *sep = "";
size_t i = 0;
bool done = false;
while (!done)
{
if (i == max_terms)
{
s = string_append2 (s, ",...]");
done = true;
}
else
{
bool there_is_a_term;
integer term;
cf_get_at (cf, i, &there_is_a_term, &term);
if (there_is_a_term)
{
char buf1[1];
const int numeral_len =
snprintf (buf1, 1, "%jd", (intmax_t) term);
char buf[numeral_len + 1];
snprintf (buf, numeral_len + 1, "%jd", (intmax_t) term);
s = string_append3 (s, sep, buf);
sep = (sep[0] == '\0') ? ";" : ",";
i += 1;
}
else
{
s = string_append2 (s, "]");
done = true;
}
}
}
return s;
}
char *
cf2string (cf_t cf)
{
const size_t default_max_terms = 20;
return cf2string_max_terms (cf, default_max_terms);
}
/*------------------------------------------------------------------*/
/* Using a cf_t as a cf_generator_t. */
struct _cf_gen_env
{
cf_t cf;
size_t i;
};
static void
cf_gen_run (void *env, bool *there_is_a_term, integer *term)
{
struct _cf_gen_env *state = env;
cf_get_at (state->cf, state->i, there_is_a_term, term);
state->i += 1;
}
cf_generator_t
cf_gen_make (cf_t cf)
{
struct _cf_gen_env *state = MALLOC (sizeof (struct _cf_gen_env));
state->cf = cf;
state->i = 0;
return cf_generator_make (cf_gen_run, state);
}
/*------------------------------------------------------------------*/
/* A homographic function. */
struct _hfunc
{
integer a1;
integer a;
integer b1;
integer b;
};
typedef struct _hfunc *hfunc_t;
struct _hfunc_gen_env
{
struct _hfunc state;
cf_generator_t gen;
};
hfunc_t
hfunc_make (integer a1, integer a, integer b1, integer b)
{
hfunc_t f = MALLOC (sizeof (struct _hfunc));
f->a1 = a1;
f->a = a;
f->b1 = b1;
f->b = b;
return f;
}
static void
_take_term_from_ngen (struct _hfunc *state,
cf_generator_t ngen)
{
const integer a1 = state->a1;
const integer b1 = state->b1;
bool there_is_a_term;
integer term;
ngen->func (ngen->env, &there_is_a_term, &term);
if (there_is_a_term)
{
const integer a = state->a;
const integer b = state->b;
state->a1 = a + (a1 * term);
state->a = a1;
state->b1 = b + (b1 * term);
state->b = b1;
}
else
{
state->a = a1;
state->b = b1;
}
}
static void
_adjust_state_for_term_output (struct _hfunc *state,
integer term)
{
const integer a1 = state->a1;
const integer a = state->a;
const integer b1 = state->b1;
const integer b = state->b;
state->a1 = b1;
state->a = b;
state->b1 = a1 - (b1 * term);
state->b = a - (b * term);
}
static void
hfunc_gen_run (void *env, bool *there_is_a_term, integer *term)
{
struct _hfunc *state = &(((struct _hfunc_gen_env *) env)->state);
cf_generator_t ngen = ((struct _hfunc_gen_env *) env)->gen;
bool done = false;
while (!done)
{
const bool b1_iseqz = (state->b1 == 0);
const bool b_iseqz = (state->b == 0);
if (b1_iseqz && b_iseqz)
{
*there_is_a_term = false;
done = true;
}
else if (!b1_iseqz && !b_iseqz)
{
const integer q1 = DIV (state->a1, state->b1);
const integer q = DIV (state->a, state->b);
if (q1 == q)
{
_adjust_state_for_term_output (state, q);
*there_is_a_term = true;
*term = q;
done = true;
}
else
_take_term_from_ngen (state, ngen);
}
else
_take_term_from_ngen (state, ngen);
}
}
/* Make a new generator that applies an hfunc_t to another
generator. */
cf_generator_t
hfunc_gen_make (hfunc_t f, cf_generator_t gen)
{
struct _hfunc_gen_env *env =
MALLOC (sizeof (struct _hfunc_gen_env));
env->state = *f;
env->gen = gen;
return cf_generator_make (hfunc_gen_run, env);
}
/* Make a new cf_t that applies an hfunc_t to another cf_t. */
cf_t
hfunc_apply (hfunc_t f, cf_t cf)
{
cf_generator_t gen1 = cf_gen_make (cf);
cf_generator_t gen2 = hfunc_gen_make (f, gen1);
return cf_make (gen2);
}
/*------------------------------------------------------------------*/
/* Creation of a cf_t for a rational number. */
struct _r2cf_gen_env
{
integer n;
integer d;
};
static void
r2cf_gen_run (void *env, bool *there_is_a_term, integer *term)
{
struct _r2cf_gen_env *state = env;
*there_is_a_term = (state->d != 0);
if (*there_is_a_term)
{
const integer q = DIV (state->n, state->d);
const integer r = REM (state->n, state->d);
state->n = state->d;
state->d = r;
*term = q;
}
}
cf_generator_t
r2cf_gen_make (integer n, integer d)
{
struct _r2cf_gen_env *state =
MALLOC (sizeof (struct _r2cf_gen_env));
state->n = n;
state->d = d;
return cf_generator_make (r2cf_gen_run, state);
}
cf_t
r2cf (integer n, integer d)
{
return cf_make (r2cf_gen_make (n, d));
}
/*------------------------------------------------------------------*/
/* Creation of a cf_t for sqrt(2). */
struct _sqrt2_gen_env
{
integer term;
};
static void
sqrt2_gen_run (void *env, bool *there_is_a_term, integer *term)
{
struct _sqrt2_gen_env *state = env;
*there_is_a_term = true;
*term = state->term;
state->term = 2;
}
cf_generator_t
sqrt2_gen_make (void)
{
struct _sqrt2_gen_env *state =
MALLOC (sizeof (struct _sqrt2_gen_env));
state->term = 1;
return cf_generator_make (sqrt2_gen_run, state);
}
cf_t
sqrt2_make (void)
{
return cf_make (sqrt2_gen_make ());
}
/*------------------------------------------------------------------*/
int
main (void)
{
MALLOC_INIT ();
hfunc_t add_one_half = hfunc_make (2, 1, 0, 2);
hfunc_t add_one = hfunc_make (1, 1, 0, 1);
hfunc_t divide_by_two = hfunc_make (1, 0, 0, 2);
hfunc_t divide_by_four = hfunc_make (1, 0, 0, 4);
hfunc_t take_reciprocal = hfunc_make (0, 1, 1, 0);
hfunc_t add_one_then_div_two = hfunc_make (1, 1, 0, 2);
hfunc_t add_two_then_div_four = hfunc_make (1, 2, 0, 4);
cf_t cf_13_11 = r2cf (13, 11);
cf_t cf_22_7 = r2cf (22, 7);
cf_t cf_sqrt2 = sqrt2_make ();
cf_t cf_13_11_plus_1_2 = hfunc_apply (add_one_half, cf_13_11);
cf_t cf_22_7_plus_1_2 = hfunc_apply (add_one_half, cf_22_7);
cf_t cf_22_7_div_4 = hfunc_apply (divide_by_four, cf_22_7);
/* The following two give the same result: */
cf_t cf_sqrt2_div_2 = hfunc_apply (divide_by_two, cf_sqrt2);
cf_t cf_1_div_sqrt2 = hfunc_apply (take_reciprocal, cf_sqrt2);
assert (strcmp (cf2string (cf_sqrt2_div_2),
cf2string (cf_1_div_sqrt2)) == 0);
/* The following three give the same result: */
cf_t cf_2_plus_sqrt2_grouped_div_4 =
hfunc_apply (add_two_then_div_four, cf_sqrt2);
cf_t cf_half_of_1_plus_half_sqrt2 =
hfunc_apply (add_one_then_div_two, cf_sqrt2_div_2);
cf_t cf_half_of_1_plus_1_div_sqrt2 =
hfunc_apply (divide_by_two,
hfunc_apply (add_one, cf_sqrt2_div_2));
assert (strcmp (cf2string (cf_2_plus_sqrt2_grouped_div_4),
cf2string (cf_half_of_1_plus_half_sqrt2)) == 0);
assert (strcmp (cf2string (cf_half_of_1_plus_half_sqrt2),
cf2string (cf_half_of_1_plus_1_div_sqrt2)) == 0);
printf ("13/11 => %s\n", cf2string (cf_13_11));
printf ("22/7 => %s\n", cf2string (cf_22_7));
printf ("sqrt(2) => %s\n", cf2string (cf_sqrt2));
printf ("13/11 + 1/2 => %s\n", cf2string (cf_13_11_plus_1_2));
printf ("22/7 + 1/2 => %s\n", cf2string (cf_22_7_plus_1_2));
printf ("(22/7)/4 => %s\n", cf2string (cf_22_7_div_4));
printf ("sqrt(2)/2 => %s\n", cf2string (cf_sqrt2_div_2));
printf ("1/sqrt(2) => %s\n", cf2string (cf_1_div_sqrt2));
printf ("(2+sqrt(2))/4 => %s\n",
cf2string (cf_2_plus_sqrt2_grouped_div_4));
printf ("(1+sqrt(2)/2)/2 => %s\n",
cf2string (cf_half_of_1_plus_half_sqrt2));
printf ("(1+1/sqrt(2))/2 => %s\n",
cf2string (cf_half_of_1_plus_1_div_sqrt2));
return 0;
}
/*------------------------------------------------------------------*/
- Output:
$ cc univariate-continued-fraction-task.c -lgc && ./a.out 13/11 => [1;5,2] 22/7 => [3;7] sqrt(2) => [1;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...] 13/11 + 1/2 => [1;1,2,7] 22/7 + 1/2 => [3;1,1,1,4] (22/7)/4 => [0;1,3,1,2] sqrt(2)/2 => [0;1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...] 1/sqrt(2) => [0;1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...] (2+sqrt(2))/4 => [0;1,5,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,...] (1+sqrt(2)/2)/2 => [0;1,5,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,...] (1+1/sqrt(2))/2 => [0;1,5,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,...]
C++
Uses ContinuedFraction and r2cf from Continued_fraction/Arithmetic/Construct_from_rational_number#C++
/* Interface for all matrixNG classes
Nigel Galloway, February 10th., 2013.
*/
class matrixNG {
private:
virtual void consumeTerm(){}
virtual void consumeTerm(int n){}
virtual const bool needTerm(){}
protected: int cfn = 0, thisTerm;
bool haveTerm = false;
friend class NG;
};
/* Implement the babyNG matrix
Nigel Galloway, February 10th., 2013.
*/
class NG_4 : public matrixNG {
private: int a1, a, b1, b, t;
const bool needTerm() {
if (b1==0 and b==0) return false;
if (b1==0 or b==0) return true; else thisTerm = a/b;
if (thisTerm==(int)(a1/b1)){
t=a; a=b; b=t-b*thisTerm; t=a1; a1=b1; b1=t-b1*thisTerm;
haveTerm=true; return false;
}
return true;
}
void consumeTerm(){a=a1; b=b1;}
void consumeTerm(int n){t=a; a=a1; a1=t+a1*n; t=b; b=b1; b1=t+b1*n;}
public:
NG_4(int a1, int a, int b1, int b): a1(a1), a(a), b1(b1), b(b){}
};
/* Implement a Continued Fraction which returns the result of an arithmetic operation on
1 or more Continued Fractions (Currently 1 or 2).
Nigel Galloway, February 10th., 2013.
*/
class NG : public ContinuedFraction {
private:
matrixNG* ng;
ContinuedFraction* n[2];
public:
NG(NG_4* ng, ContinuedFraction* n1): ng(ng){n[0] = n1;}
NG(NG_8* ng, ContinuedFraction* n1, ContinuedFraction* n2): ng(ng){n[0] = n1; n[1] = n2;}
const int nextTerm() {ng->haveTerm = false; return ng->thisTerm;}
const bool moreTerms(){
while(ng->needTerm()) if(n[ng->cfn]->moreTerms()) ng->consumeTerm(n[ng->cfn]->nextTerm()); else ng->consumeTerm();
return ng->haveTerm;
}
};
Testing
[1;5,2] + 1/2
int main() {
NG_4 a1(2,1,0,2);
r2cf n1(13,11);
for(NG n(&a1, &n1); n.moreTerms(); std::cout << n.nextTerm() << " ");
std::cout << std::endl;
return 0;
}
- Output:
1 1 2 7
[3;7] * 7/22
int main() {
NG_4 a2(7,0,0,22);
r2cf n2(22,7);
for(NG n(&a2, &n2); n.moreTerms(); std::cout << n.nextTerm() << " ");
std::cout << std::endl;
return 0;
}
- Output:
1
[3;7] + 1/22
int main() {
NG_4 a3(2,1,0,2);
r2cf n3(22,7);
for(NG n(&a3, &n3); n.moreTerms(); std::cout << n.nextTerm() << " ");
std::cout << std::endl;
return 0;
}
- Output:
3 1 1 1 4
[3;7] divided by 4
int main() {
NG_4 a4(1,0,0,4);
r2cf n4(22,7);
for(NG n(&a4, &n4); n.moreTerms(); std::cout << n.nextTerm() << " ");
std::cout << std::endl;
return 0;
}
- Output:
0 1 3 1 2
First I generate as a continued fraction, then I obtain an approximate value using r2cf for comparison.
int main() {
NG_4 a5(0,1,1,0);
SQRT2 n5;
int i = 0;
for(NG n(&a5, &n5); n.moreTerms() and i++ < 20; std::cout << n.nextTerm() << " ");
std::cout << "..." << std::endl;
for(r2cf cf(10000000, 14142136); cf.moreTerms(); std::cout << cf.nextTerm() << " ");
std::cout << std::endl;
return 0;
}
- Output:
0 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 ... 0 1 2 2 2 2 2 2 2 2 2 6 1 2 4 1 1 2
First I generate as a continued fraction, then I obtain an approximate value using r2cf for comparison.
int main() {
int i = 0;
NG_4 a6(1,1,0,2);
SQRT2 n6;
for(NG n(&a6, &n6); n.moreTerms() and i++ < 20; std::cout << n.nextTerm() << " ");
std::cout << "..." << std::endl;
for(r2cf cf(24142136, 20000000); cf.moreTerms(); std::cout << cf.nextTerm() << " ");
std::cout << std::endl;
return 0;
}
- Output:
1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 ... 1 4 1 4 1 4 1 4 1 4 3 2 1 9 5
Go
Adding to the existing package from the
Continued_fraction/Arithmetic/Construct_from_rational_number#Go
task, re-uses cf.go
and rat.go
as given in that task.
File ng4.go
:
package cf
// A 2×2 matix:
// [ a₁ a ]
// [ b₁ b ]
//
// which when "applied" to a continued fraction representing x
// gives a new continued fraction z such that:
//
// a₁ * x + a
// z = ----------
// b₁ * x + b
//
// Examples:
// NG4{0, 1, 0, 4}.ApplyTo(x) gives 0*x + 1/4 -> 1/4 = [0; 4]
// NG4{0, 1, 1, 0}.ApplyTo(x) gives 1/x
// NG4{1, 1, 0, 2}.ApplyTo(x) gives (x+1)/2
//
// Note that several operations (e.g. addition and division)
// can be efficiently done with a single matrix application.
// However, each matrix application may require
// several calculations for each outputed term.
type NG4 struct {
A1, A int64
B1, B int64
}
func (ng NG4) needsIngest() bool {
if ng.isDone() {
panic("b₁==b==0")
}
return ng.B1 == 0 || ng.B == 0 || ng.A1/ng.B1 != ng.A/ng.B
}
func (ng NG4) isDone() bool {
return ng.B1 == 0 && ng.B == 0
}
func (ng *NG4) ingest(t int64) {
// [ a₁ a ] becomes [ a + a₁×t a₁ ]
// [ b₁ b ] [ b + b₁×t b₁ ]
ng.A1, ng.A, ng.B1, ng.B =
ng.A+ng.A1*t, ng.A1,
ng.B+ng.B1*t, ng.B1
}
func (ng *NG4) ingestInfinite() {
// [ a₁ a ] becomes [ a₁ a₁ ]
// [ b₁ b ] [ b₁ b₁ ]
ng.A, ng.B = ng.A1, ng.B1
}
func (ng *NG4) egest(t int64) {
// [ a₁ a ] becomes [ b₁ b ]
// [ b₁ b ] [ a₁ - b₁×t a - b×t ]
ng.A1, ng.A, ng.B1, ng.B =
ng.B1, ng.B,
ng.A1-ng.B1*t, ng.A-ng.B*t
}
// ApplyTo "applies" the matrix `ng` to the continued fraction `cf`,
// returning the resulting continued fraction.
func (ng NG4) ApplyTo(cf ContinuedFraction) ContinuedFraction {
return func() NextFn {
next := cf()
done := false
return func() (int64, bool) {
if done {
return 0, false
}
for ng.needsIngest() {
if t, ok := next(); ok {
ng.ingest(t)
} else {
ng.ingestInfinite()
}
}
t := ng.A1 / ng.B1
ng.egest(t)
done = ng.isDone()
return t, true
}
}
}
File ng4_test.go
:
package cf
import (
"fmt"
"reflect"
"testing"
)
func Example_NG4() {
cases := [...]struct {
r Rat
ng NG4
}{
{Rat{13, 11}, NG4{2, 1, 0, 2}},
{Rat{22, 7}, NG4{2, 1, 0, 2}},
{Rat{22, 7}, NG4{1, 0, 0, 4}},
}
for _, tc := range cases {
cf := tc.r.AsContinuedFraction()
fmt.Printf("%5s = %-9s with %v gives %v\n", tc.r, cf.String(), tc.ng,
tc.ng.ApplyTo(cf),
)
}
invSqrt2 := NG4{0, 1, 1, 0}.ApplyTo(Sqrt2)
fmt.Println(" 1/√2 =", invSqrt2)
result := NG4{1, 1, 0, 2}.ApplyTo(Sqrt2)
fmt.Println("(1+√2)/2 =", result)
// Output:
// 13/11 = [1; 5, 2] with {2 1 0 2} gives [1; 1, 2, 7]
// 22/7 = [3; 7] with {2 1 0 2} gives [3; 1, 1, 1, 4]
// 22/7 = [3; 7] with {1 0 0 4} gives [0; 1, 3, 1, 2]
// 1/√2 = [0; 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...]
// (1+√2)/2 = [1; 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, ...]
}
- Output:
13/11 = [1; 5, 2] with {2 1 0 2} gives [1; 1, 2, 7] 22/7 = [3; 7] with {2 1 0 2} gives [3; 1, 1, 1, 4] 22/7 = [3; 7] with {1 0 0 4} gives [0; 1, 3, 1, 2] 1/√2 = [0; 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...] (1+√2)/2 = [1; 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, ...]
J
Note that the continued fraction representation used here differs from those implemented in the Continued_fraction fraction task. In that task, we alternated a and b values. Here, we only work with a values -- b is implicitly always 1
.
Implementation:
ng4cf=: 4 : 0
cf=. 1000{.!._ y
ng=. x
r=.i. ndx=.0
while. +./0~:{:ng do.
if.=/<.%/ng do.
r=.r, t=.{.<.%/ng
ng=. t (|.@] - ]*0,[) ng
else.
if. _=t=.ndx{cf do.
ng=. ng+/ .*2 2$1 1 0 0
else.
ng=. ng+/ .*2 2$t,1 1 0
end.
if. (#cf)=ndx=. ndx+1 do. r return. end.
end.
end.
r
)
Notes:
- we arbitrarily stop processing continued fractions after 1000 elements. That's more than enough precision for most purposes.
- we can convert a continued fraction to a rational number using
(+%)/
though if we want the full represented precision we should instead use(+%)/@x:
(which is slower).
- we can convert a rational number to a continued fraction using
1 1 {."1@}. ({: , (0 , {:) #: {.)^:(*@{:)^:a:
but also this expects a numerator,denominator pair so if you have only a single number use,&1
to give it a denominator. This works equally well with floating point and arbitrary precision numbers.
Some arbitrary continued fractions and their floating point representations
arbs=:(,1);(,3);?~&.>3+i.10
":@>arbs
1
3
1 2 0
0 2 3 1
1 0 3 2 4
0 2 3 5 1 4
2 5 0 1 6 3 4
7 5 6 3 0 4 1 2
7 0 1 2 6 3 8 4 5
8 0 5 6 3 7 4 9 1 2
0 9 8 1 3 10 2 5 6 7 4
1 7 3 4 5 8 9 10 6 11 0 2
(+%)/@>arbs
1 3 1 0.444444 4.44444 0.431925 2.16238 7.19368 8.46335 13.1583 0.109719 1.13682
Some NG based cf functions, verifying their behavior against our test set:
plus1r2=: (2 1,:0 2)&ng4cf
(plus1r2 each -&((+%)/@>) ]) arbs
0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5
For every one of our arbitrary continued fractions, the 2 1,:0 2 NG matrix gives us a new continued fraction whose rational value is the original rational value + 1r2.
times7r22=: (7 0,:0 22)&ng4cf
(times7r22 each %&((+%)/@>) ]) arbs
0.318182 0.318182 0.318182 0.318182 0.318182 0.318182 0.318182 0.318182 0.318182 0.318182 0.318182 0.318182
(times7r22 each %&((+%)/@x:@>) ]) arbs
7r22 7r22 7r22 7r22 7r22 7r22 7r22 7r22 7r22 7r22 7r22 7r22
For every one of our arbitrary continued fractions, the 7 0,:0 22 NG matrix gives us a new continued fraction whose rational value is 7r22 times the original rational value.
times1r4=:(1 0,:0 4)&ng4cf
(times1r4 each %&((+%)/@>) ]) arbs
0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.25
It seems like a diagonal matrix has the effect of multiplying the numerator by the upper left element and the denominator by the lower right element. And our first experiment suggests that an upper right element in NG with a 0 for the bottom left will add the top right divided by bottom right to our continued fraction.
reciprocal=:(0 1,:1 0)&ng4cf
(reciprocal each *&((+%)/@>) ]) arbs
1 1 1 1 1 1 1 1 1 1 1 1
Looks like we can also divide by our continued fraction...
plus1r2times1r2=: (1 1,:0 2)&ng4cf
(plus1r2times1r2 each (= 0.5+0.5*])&((+%)/@>) ]) arbs
1 1 1 1 1 1 1 1 1 1 1 1
We can add and multiply using a single "ng4" operation.
Task examples:
1r2 + 13r11
(+%)/1 5 2
1.18182
plus1r2 1 5 2
1 1 2 7
(+%)/plus1r2 1 5 2
1.68182
7r22 * 22r7
(+%)/3 7x
22r7
times7r22 3 7x
1
1r2 + 22r7
plus1r2 3 7x
3 1 1 1 4
(+%)/plus1r2 3 7x
3.64286
(+%)/x:plus1r2 3 7x
51r14
1r4 * 22r7
times1r4 3 7x
0 1 3 1 2
(+%)/x:times1r4 3 7x
11r14
reciprocal 1,999$2
0 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 ...
(+%)/1,999$2
1.41421
(+%)/reciprocal 1,999$2
0.707107
plus1r2times1r2 1,999$2
1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 ...
(+%)/plus1r2times1r2 1,999$2
1.20711
plus1r2times1r2 0 1,999$2
0 1 5 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 ...
(+%)/plus1r2times1r2 0 1,999$2
0.853553
Julia
function r2cf(n1::Integer, n2::Integer)
ret = Int[]
while n2 != 0
n1, (t1, n2) = n2, divrem(n1, n2)
push!(ret, t1)
end
ret
end
r2cf(r::Rational) = r2cf(numerator(r), denominator(r))
function r2cf(n1, n2, maxiter=20)
ret = Int[]
while n2 != 0 && maxiter > 0
n1, (t1, n2) = n2, divrem(n1, n2)
push!(ret, t1)
maxiter -= 1
end
ret
end
mutable struct NG
a1::Int
a::Int
b1::Int
b::Int
end
function ingress(ng, n)
ng.a, ng.a1= ng.a1, ng.a + ng.a1 * n
ng.b, ng.b1 = ng.b1, ng.b + ng.b1 * n
end
needterm(ng) = ng.b == 0 || ng.b1 == 0 || !(ng.a // ng.b == ng.a1 // ng.b1)
function egress(ng)
n = ng.a // ng.b
ng.a, ng.b = ng.b, ng.a - ng.b * n
ng.a1, ng.b1 = ng.b1, ng.a1 - ng.b1 * n
r2cf(n)
end
egress_done(ng) = (if needterm(ng) ng.a, ng.b = ng.a1, ng.b1 end; egress(ng))
done(ng) = ng.b == 0 && ng.b1 == 0
function testng()
data = [["[1;5,2] + 1/2", [2,1,0,2], [13,11]],
["[3;7] + 1/2", [2,1,0,2], [22, 7]],
["[3;7] divided by 4", [1,0,0,4], [22, 7]],
["[1;1] divided by sqrt(2)", [0,1,1,0], [1,sqrt(2)]]]
for d in data
str, ng, r = d[1], NG(d[2]...), d[3]
print(rpad(str, 25), "->")
for n in r2cf(r...)
if !needterm(ng)
print(" $(egress(ng))")
end
ingress(ng, n)
end
while true
print(" $(egress_done(ng))")
if done(ng)
println()
break
end
end
end
end
testng()
- Output:
[1;5,2] + 1/2 -> [1, 1, 2, 7] [3;7] + 1/2 -> [3, 1, 1, 1, 4] [3;7] divided by 4 -> [0, 1, 3, 1, 2] [1;1] divided by sqrt(2) -> [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
Kotlin
This is based on the Python entry but has been expanded to deal with the '√2' calculations:
// version 1.1.3
// compile with -Xcoroutines=enable flag from command line
import kotlin.coroutines.experimental.*
typealias CFGenerator = (Pair<Int, Int>) -> Sequence<Int>
data class CFData(
val str: String,
val ng: IntArray,
val r: Pair<Int,Int>,
val gen: CFGenerator
)
fun r2cf(frac: Pair<Int, Int>) =
buildSequence {
var num = frac.first
var den = frac.second
while (Math.abs(den) != 0) {
val div = num / den
val rem = num % den
num = den
den = rem
yield(div)
}
}
fun d2cf(d: Double) =
buildSequence {
var dd = d
while (true) {
val div = Math.floor(dd)
val rem = dd - div
yield(div.toInt())
if (rem == 0.0) break
dd = 1.0 / rem
}
}
@Suppress("UNUSED_PARAMETER")
fun root2(dummy: Pair<Int, Int>) =
buildSequence {
yield(1)
while (true) yield(2)
}
@Suppress("UNUSED_PARAMETER")
fun recipRoot2(dummy: Pair<Int, Int>) =
buildSequence {
yield(0)
yield(1)
while (true) yield(2)
}
class NG(var a1: Int, var a: Int, var b1: Int, var b: Int) {
fun ingress(n: Int) {
var t = a
a = a1
a1 = t + a1 * n
t = b
b = b1
b1 = t + b1 * n
}
fun egress(): Int {
val n = a / b
var t = a
a = b
b = t - b * n
t = a1
a1 = b1
b1 = t - b1 * n
return n
}
val needTerm get() = (b == 0 || b1 == 0) || ((a / b) != (a1 / b1))
val egressDone: Int
get() {
if (needTerm) {
a = a1
b = b1
}
return egress()
}
val done get() = b == 0 && b1 == 0
}
fun main(args: Array<String>) {
val data = listOf(
CFData("[1;5,2] + 1/2 ", intArrayOf(2, 1, 0, 2), 13 to 11, ::r2cf),
CFData("[3;7] + 1/2 ", intArrayOf(2, 1, 0, 2), 22 to 7, ::r2cf),
CFData("[3;7] divided by 4 ", intArrayOf(1, 0, 0, 4), 22 to 7, ::r2cf),
CFData("sqrt(2) ", intArrayOf(0, 1, 1, 0), 0 to 0, ::recipRoot2),
CFData("1 / sqrt(2) ", intArrayOf(0, 1, 1, 0), 0 to 0, ::root2),
CFData("(1 + sqrt(2)) / 2 ", intArrayOf(1, 1, 0, 2), 0 to 0, ::root2),
CFData("(1 + 1 / sqrt(2)) / 2", intArrayOf(1, 1, 0, 2), 0 to 0, ::recipRoot2)
)
println("Produced by NG class:")
for ((str, ng, r, gen) in data) {
print("$str -> ")
val (a1, a, b1, b) = ng
val op = NG(a1, a, b1, b)
for (n in gen(r).take(20)) {
if (!op.needTerm) print(" ${op.egress()} ")
op.ingress(n)
}
while (true) {
print(" ${op.egressDone} ")
if (op.done) break
}
println()
}
println("\nProduced by direct calculation:")
val data2 = listOf(
Pair("(1 + sqrt(2)) / 2 ", (1 + Math.sqrt(2.0)) / 2),
Pair("(1 + 1 / sqrt(2)) / 2", (1 + 1 / Math.sqrt(2.0)) / 2)
)
for ((str, d) in data2) {
println("$str -> ${d2cf(d).take(20).joinToString(" ")}")
}
}
- Output:
Produced by NG class: [1;5,2] + 1/2 -> 1 1 2 7 [3;7] + 1/2 -> 3 1 1 1 4 [3;7] divided by 4 -> 0 1 3 1 2 sqrt(2) -> 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 / sqrt(2) -> 0 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 (1 + sqrt(2)) / 2 -> 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 (1 + 1 / sqrt(2)) / 2 -> 0 1 5 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 5 Produced by direct calculation: (1 + sqrt(2)) / 2 -> 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 (1 + 1 / sqrt(2)) / 2 -> 0 1 5 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1
Nim
import math, rationals, strformat
type
Rat = Rational[int]
Ng = tuple[a1, a, b1, b: int]
const NS = 1 // 1 # Non significant value.
iterator r2cf(rat: Rat): int {.closure.} =
var
num = rat.num
den = rat.den
for count in 1..20:
let d = num div den
num = num mod den
swap num, den
yield d
if den == 0: break
iterator d2cf(f: float): int {.closure.} =
var f = f
for count in 1..20:
let d = floor(f)
let r = f - d
yield int(d)
if r == 0: break
f = 1 / r
iterator root2(dummy: Rat): int {.closure.} =
yield 1
for count in 1..20: yield 2
iterator recipRoot2(rat: Rat): int {.closure.} =
yield 0
yield 1
for count in 1..20: yield 2
func ingress(ng: var Ng; n: int) =
ng.a += ng.a1 * n
swap ng.a, ng.a1
ng.b += ng.b1 * n
swap ng.b, ng.b1
func egress(ng: var Ng): int =
let n = ng.a div ng.b
ng.a -= ng.b * n
swap ng.a, ng.b
ng.a1 -= ng.b1 * n
swap ng.a1, ng.b1
result = n
func needTerm(ng: Ng): bool = ng.b == 0 or ng.b1 == 0 or (ng.a // ng.b != ng.a1 // ng.b1)
func egressDone(ng: var Ng): int =
if ng.needTerm:
ng.a = ng.a1
ng.b = ng.b1
result = ng.egress()
func done(ng: Ng): bool = ng.b == 0 or ng.b1 == 0
when isMainModule:
let data = [("[1;5,2] + 1/2 ", (2, 1, 0, 2), 13 // 11, r2cf),
("[3;7] + 1/2 ", (2, 1, 0, 2), 22 // 7, r2cf),
("[3;7] divided by 4 ", (1, 0, 0, 4), 22 // 7, r2cf),
("sqrt(2) ", (0, 1, 1, 0), NS, recipRoot2),
("1 / sqrt(2) ", (0, 1, 1, 0), NS, root2),
("(1 + sqrt(2)) / 2 ", (1, 1, 0, 2), NS, root2),
("(1 + 1 / sqrt(2)) / 2", (1, 1, 0, 2), NS, recipRoot2)]
echo "Produced by NG object:"
for (str, ng, r, gen) in data:
stdout.write &"{str} → "
var op = ng
for n in gen(r):
if not op.needTerm: stdout.write &" {op.egress()} "
op.ingress(n)
while true:
stdout.write &" {op.egressDone} "
if op.done: break
echo()
echo "\nProduced by direct calculation:"
let data2 = [("(1 + sqrt(2)) / 2 ", (1 + sqrt(2.0)) / 2),
("(1 + 1 / sqrt(2)) / 2", (1 + 1 / sqrt(2.0)) / 2)]
for (str, d) in data2:
stdout.write &"{str} →"
for n in d2cf(d): stdout.write " ", n
echo()
- Output:
Produced by NG object: [1;5,2] + 1/2 → 1 1 2 7 [3;7] + 1/2 → 3 1 1 1 4 [3;7] divided by 4 → 0 1 3 1 2 sqrt(2) → 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 / sqrt(2) → 0 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 (1 + sqrt(2)) / 2 → 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 (1 + 1 / sqrt(2)) / 2 → 0 1 5 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 5 Produced by direct calculation: (1 + sqrt(2)) / 2 → 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 (1 + 1 / sqrt(2)) / 2 → 0 1 5 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1
Phix
Self-contained. The supporting cast of r2cf(), cf2s(), cf2r() and d2cf() ended up being more code than the task itself.
requires("0.8.2")
class baby_matrix
integer a1, a, b1, b
--
-- used by apply_baby_matrix to yield (a1*cf+a)/(b1*cf+b)
--
-- examples: (a1 a b1 b) => above, simplified:
-- ======== = = = =
-- {2, 1, 0, 2} => (2*cf+1)/2, aka cf+1/2
-- {1, 0, 0, 4} => cf/4
-- {1, 0, 0, 1} => cf/1, aka cf
-- {0, 1, 1, 0} => 1/cf
-- {1, 1, 0, 2} => (cf+1)/2
--
function need_term()
return b==0 or b1==0 or ((a/b)!=(a1/b1))
end function
function next_term()
integer n = floor(a/b)
{a1,a,b1,b} = {b1,b,a1-b1*n,a-b*n}
return n
end function
procedure in_term(object n={})
if integer(n) then
{a1,a,b,b1} = {a+a1*n,a1,b1,b+b1*n}
else
{a,b} = {a1,b1}
end if
end procedure
function done()
return b=0 and b1=0
end function
end class
function apply_baby_matrix(sequence m, cf)
--
-- for m of integer {a1,a,b1,b}, return (a1*cf+a)/(b1*cf+b):
--
baby_matrix bm = new(m)
sequence res = {}
for i=1 to length(cf) do
if not bm.need_term() then
res &= bm.next_term()
end if
bm.in_term(cf[i])
end for
while true do
if bm.need_term() then
bm.in_term()
end if
res &= bm.next_term()
if bm.done() then exit end if
end while
return res
end function
function r2cf(sequence rat, integer count=20)
sequence s = {}
atom {num,den} = rat
while den!=0 and length(s)<count do
s &= trunc(num/den)
{num,den} = {den,num-s[$]*den}
end while
return s
end function
function root2(integer count=20)
return {1}&repeat(2,count-1)
end function
function recip_root2(integer count=20)
return {0,1}&repeat(2,count-2)
end function
function cf2s(sequence cf)
sequence s = join(apply(cf,sprint),",") -- eg "1,5,2"
return "["&substitute(s,",",";",1)&"]" -- => "[1;5,2]"
end function
include mpfr.e
function cf2r(sequence cf)
mpq res = mpq_init(), -- 0/1
cfn = mpq_init()
for n=length(cf) to 1 by -1 do
mpq_set_si(cfn,cf[n])
mpq_add(res,res,cfn)
if n=1 then exit end if
mpq_inv(res,res)
end for
mpz num = mpz_init(),
den = mpz_init()
mpq_get_num(num,res)
mpq_get_den(den,res)
mpfr x = mpfr_init()
mpfr_set_q(x,res)
string xs = mpfr_sprintf("%.15Rf",x),
ns = mpz_get_str(num),
ds = mpz_get_str(den),
s = sprintf("%s (%s/%s)",{xs,ns,ds})
return s
end function
function d2cf(atom d, integer count=20)
string res = "["
integer sep = ';'
while count do
atom div = floor(d),
rem = d - div
res &= sprintf("%d%c",{div,sep})
if rem==0 then exit end if
d = 1/rem
count -= 1
sep = ','
end while
res[$] = ']'
return res
end function
constant tests = {
{"[1;5,2] + 1/2 ", {2, 1, 0, 2}, r2cf({13,11}), 37/22},
{"[3;7] + 1/2 ", {2, 1, 0, 2}, r2cf({22, 7}), 3+1/7+1/2},
{"[3;7] / 4 ", {1, 0, 0, 4}, r2cf({22, 7}), (3+1/7)/4},
{"sqrt(2) ", {1, 0, 0, 1}, root2(), sqrt(2)},
{"sqrt(2) (inv) ", {0, 1, 1, 0}, recip_root2(), 1/(1/sqrt(2))},
{"1/sqrt(2) ", {1, 0, 0, 1}, recip_root2(), 1/sqrt(2)},
{"1/sqrt(2) (inv)", {0, 1, 1, 0}, root2(), 1/sqrt(2)},
{"(1+sqrt(2))/2 ", {1, 1, 0, 2}, root2(), (1+sqrt(2))/2},
{"(1+1/sqrt(2))/2", {1, 1, 0, 2}, recip_root2(), (1+1/sqrt(2))/2}}
for i=1 to length(tests) do
{string str, sequence bm, sequence cf, atom eres} = tests[i]
sequence res = apply_baby_matrix(bm, cf)
printf(1,"%s -> %s --> %s\n",{str,cf2s(res),cf2r(res)})
printf(1," direct: %s ==> %.15f\n",{d2cf(eres,length(res)),eres})
end for
- Output:
[1;5,2] + 1/2 -> [1;1,2,7] --> 1.681818181818182 (37/22) direct: [1;1,2,6] ==> 1.681818181818182 [3;7] + 1/2 -> [3;1,1,1,4] --> 3.642857142857143 (51/14) direct: [3;1,1,1,3] ==> 3.642857142857143 [3;7] / 4 -> [0;1,3,1,2] --> 0.785714285714286 (11/14) direct: [0;1,3,1,2] ==> 0.785714285714286 sqrt(2) -> [1;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] --> 1.414213562373096 (22619537/15994428) direct: [1;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] ==> 1.414213562373095 sqrt(2) (inv) -> [1;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] --> 1.414213562373087 (9369319/6625109) direct: [1;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] ==> 1.414213562373095 1/sqrt(2) -> [0;1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] --> 0.707106781186552 (6625109/9369319) direct: [0;1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] ==> 0.707106781186547 1/sqrt(2) (inv) -> [0;1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] --> 0.707106781186547 (15994428/22619537) direct: [0;1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] ==> 0.707106781186547 (1+sqrt(2))/2 -> [1;4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4] --> 1.207106781186548 (38613965/31988856) direct: [1;4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4] ==> 1.207106781186547 (1+1/sqrt(2))/2 -> [0;1,5,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5] --> 0.853553390593276 (7997214/9369319) direct: [0;1,5,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4] ==> 0.853553390593274
The last digits of direct in the first two tests match on 64-bit, ie ,7] and ,4], plus 6/7/8 end in 548.
Python
Python: NG
class NG:
def __init__(self, a1, a, b1, b):
self.a1, self.a, self.b1, self.b = a1, a, b1, b
def ingress(self, n):
self.a, self.a1 = self.a1, self.a + self.a1 * n
self.b, self.b1 = self.b1, self.b + self.b1 * n
@property
def needterm(self):
return (self.b == 0 or self.b1 == 0) or not self.a//self.b == self.a1//self.b1
@property
def egress(self):
n = self.a // self.b
self.a, self.b = self.b, self.a - self.b * n
self.a1, self.b1 = self.b1, self.a1 - self.b1 * n
return n
@property
def egress_done(self):
if self.needterm: self.a, self.b = self.a1, self.b1
return self.egress
@property
def done(self):
return self.b == 0 and self.b1 == 0
Python: Testing
Uses r2cf method from here.
data = [["[1;5,2] + 1/2", [2,1,0,2], [13,11]],
["[3;7] + 1/2", [2,1,0,2], [22, 7]],
["[3;7] divided by 4", [1,0,0,4], [22, 7]]]
for string, ng, r in data:
print( "%-20s->" % string, end='' )
op = NG(*ng)
for n in r2cf(*r):
if not op.needterm: print( " %r" % op.egress, end='' )
op.ingress(n)
while True:
print( " %r" % op.egress_done, end='' )
if op.done: break
print()
- Output:
[1;5,2] + 1/2 -> 1 1 2 7 [3;7] + 1/2 -> 3 1 1 1 4 [3;7] divided by 4 -> 0 1 3 1 2
Racket
Main part of the NG-baby matrices. They are implemented as mutable structs.
#lang racket/base
(struct ng (a1 a b1 b) #:transparent #:mutable)
(define (ng-ingress! v t)
(define a (ng-a v))
(define a1 (ng-a1 v))
(define b (ng-b v))
(define b1 (ng-b1 v))
(set-ng-a! v a1)
(set-ng-a1! v (+ a (* a1 t)))
(set-ng-b! v b1)
(set-ng-b1! v (+ b (* b1 t))))
(define (ng-needterm? v)
(or (zero? (ng-b v))
(zero? (ng-b1 v))
(not (= (quotient (ng-a v) (ng-b v)) (quotient (ng-a1 v) (ng-b1 v))))))
(define (ng-egress! v)
(define t (quotient (ng-a v) (ng-b v)))
(define a (ng-a v))
(define a1 (ng-a1 v))
(define b (ng-b v))
(define b1 (ng-b1 v))
(set-ng-a! v b)
(set-ng-a1! v b1)
(set-ng-b! v (- a (* b t)))
(set-ng-b1! v (- a1 (* b1 t)))
t)
(define (ng-infty! v)
(when (ng-needterm? v)
(set-ng-a! v (ng-a1 v))
(set-ng-b! v (ng-b1 v))))
(define (ng-done? v)
(and (zero? (ng-b v)) (zero? (ng-b1 v))))
Auxiliary functions to create producers of well known continued fractions. The function rational->cf is copied from r2cf task.
(define ((rational->cf n d))
(and (not (zero? d))
(let-values ([(q r) (quotient/remainder n d)])
(set! n d)
(set! d r)
q)))
(define (sqrt2->cf)
(define first? #t)
(lambda ()
(if first?
(begin
(set! first? #f)
1)
2)))
The function combine-ng-cf->cf combines a ng-matrix and a cf- producer and creates a cf-producer. The cf-producers can represent infinitely long continued fractions. The function cf-showln shows the first coefficients of a continued fraction represented in a cf-producer.
(define (combine-ng-cf->cf ng cf)
(define empty-producer? #f)
(lambda ()
(let loop ()
(cond
[(not empty-producer?) (define t (cf))
(cond
[t (ng-ingress! ng t)
(if (ng-needterm? ng)
(loop)
(ng-egress! ng))]
[else (set! empty-producer? #t)
(loop)])]
[(ng-done? ng) #f]
[(ng-needterm? ng) (ng-infty! ng)
(loop)]
[else (ng-egress! ng)]))))
(define (cf-showln cf n)
(for ([i (in-range n)])
(define val (cf))
(when val
(printf " ~a" val)))
(when (cf)
(printf " ..."))
(printf "~n"))
Some test
(display "[1;5,2] + 1/2 ->")
(cf-showln (combine-ng-cf->cf (ng 2 1 0 2) (rational->cf 13 11)) 20)
(display "[3;7] + 1/2 ->")
(cf-showln (combine-ng-cf->cf (ng 2 1 0 2) (rational->cf 22 7)) 20)
(display "[3;7] / 4 ->")
(cf-showln (combine-ng-cf->cf (ng 1 0 0 4) (rational->cf 22 7)) 20)
(display "sqrt(2)/2 ->")
(cf-showln (combine-ng-cf->cf (ng 1 0 0 2) (sqrt2->cf)) 20)
(display "1/sqrt(2) ->")
(cf-showln (combine-ng-cf->cf (ng 0 1 1 0) (sqrt2->cf)) 20)
(display "(1+sqrt(2))/2 ->")
(cf-showln (combine-ng-cf->cf (ng 1 1 0 2) (sqrt2->cf)) 20)
Sample output:
[1;5,2] + 1/2 -> 1 1 2 7 [3;7] + 1/2 -> 3 1 1 1 4 [3;7] / 4 -> 0 1 3 1 2 sqrt(2)/2 -> 0 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 ... 1/sqrt(2) -> 0 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 ... (1+sqrt(2))/2 -> 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 ...
Raku
(formerly Perl 6)
All the important stuff takes place in the NG object. Everything else is helper subs for testing and display. The NG object is capable of working with infinitely long continued fractions, but displaying them can be problematic. You can pass in a limit to the apply method to get a fixed maximum number of terms though. See the last example: 100 terms from the infinite cf (1+√2)/2 and its Rational representation.
class NG {
has ( $!a1, $!a, $!b1, $!b );
submethod BUILD ( :$!a1, :$!a, :$!b1, :$!b ) { }
# Public methods
method new( $a1, $a, $b1, $b ) { self.bless( :$a1, :$a, :$b1, :$b ) }
method apply(@cf, :$limit = Inf) {
(gather {
map { take self!extract unless self!needterm; self!inject($_) }, @cf;
take self!drain until self!done;
})[ ^ $limit ]
}
# Private methods
method !inject ($n) {
sub xform($n, $x, $y) { $x, $n * $x + $y }
( $!a, $!a1 ) = xform( $n, $!a1, $!a );
( $!b, $!b1 ) = xform( $n, $!b1, $!b );
}
method !extract {
sub xform($n, $x, $y) { $y, $x - $y * $n }
my $n = $!a div $!b;
($!a, $!b ) = xform( $n, $!a, $!b );
($!a1, $!b1) = xform( $n, $!a1, $!b1 );
$n
}
method !drain { $!a = $!a1, $!b = $!b1 if self!needterm; self!extract }
method !needterm { so [||] !$!b, !$!b1, $!a/$!b != $!a1/$!b1 }
method !done { not [||] $!b, $!b1 }
}
sub r2cf(Rat $x is copy) { # Rational to continued fraction
gather loop {
$x -= take $x.floor;
last if !$x;
$x = 1 / $x;
}
}
sub cf2r(@a) { # continued fraction to Rational
my $x = @a[* - 1]; # Use FatRats for arbitrary precision
$x = ( @a[$_- 1] + 1 / $x ).FatRat for reverse 1 ..^ @a;
$x
}
sub ppcf(@cf) { # format continued fraction for pretty printing
"[{ @cf.join(',').subst(',',';') }]"
}
sub pprat($a) { # format Rational for pretty printing
# Use FatRats for arbitrary precision
$a.FatRat.denominator == 1 ?? $a !! $a.FatRat.nude.join('/')
}
sub test_NG ($rat, @ng, $op) {
my @cf = $rat.Rat(1e-18).&r2cf;
my @op = NG.new( |@ng ).apply( @cf );
say $rat.raku, ' as a cf: ', @cf.&ppcf, " $op = ",
@op.&ppcf, "\tor ", @op.&cf2r.&pprat, "\n";
}
# Testing
test_NG(|$_) for (
[ 13/11, [<2 1 0 2>], '+ 1/2 ' ],
[ 22/7, [<2 1 0 2>], '+ 1/2 ' ],
[ 22/7, [<1 0 0 4>], '/ 4 ' ],
[ 22/7, [<7 0 0 22>], '* 7/22 ' ],
[ 2**.5, [<1 1 0 2>], "\n(1+√2)/2 (approximately)" ]
);
say '100 terms of (1+√2)/2 as a continued fraction and as a rational value:';
my @continued-fraction = NG.new( 1,1,0,2 ).apply( (lazy flat 1, 2 xx * ), limit => 100 );
say @continued-fraction.&ppcf.comb(/ . ** 1..80/).join("\n");
say @continued-fraction.&cf2r.&pprat;
- Output:
<13/11> as a cf: [1;5,2] + 1/2 = [1;1,2,7] or 37/22 <22/7> as a cf: [3;7] + 1/2 = [3;1,1,1,4] or 51/14 <22/7> as a cf: [3;7] / 4 = [0;1,3,1,2] or 11/14 <22/7> as a cf: [3;7] * 7/22 = [1] or 1 1.4142135623731e0 as a cf: [1;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] (1+√2)/2 (approximately) = [1;4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4] or 225058681/186444716 100 terms of (1+√2)/2 and its rational value [1;4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4 ,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4 ,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4] 161733217200188571081311986634082331709/133984184101103275326877813426364627544
Ruby
NG
# I define a class to implement baby NG
class NG
def initialize(a1, a, b1, b)
@a1, @a, @b1, @b = a1, a, b1, b
end
def ingress(n)
@a, @a1 = @a1, @a + @a1 * n
@b, @b1 = @b1, @b + @b1 * n
end
def needterm?
return true if @b == 0 or @b1 == 0
return true unless @a/@b == @a1/@b1
false
end
def egress
n = @a / @b
@a, @b = @b, @a - @b * n
@a1, @b1 = @b1, @a1 - @b1 * n
n
end
def egress_done
@a, @b = @a1, @b1 if needterm?
egress
end
def done?
@b == 0 and @b1 == 0
end
end
Testing
Uses r2cf method from here.
data = [["[1;5,2] + 1/2", [2,1,0,2], [13,11]],
["[3;7] + 1/2", [2,1,0,2], [22, 7]],
["[3;7] divided by 4", [1,0,0,4], [22, 7]]]
data.each do |str, ng, r|
printf "%-20s->", str
op = NG.new(*ng)
r2cf(*r) do |n|
print " #{op.egress}" unless op.needterm?
op.ingress(n)
end
print " #{op.egress_done}" until op.done?
puts
end
- Output:
[1;5,2] + 1/2 -> 1 1 2 7 [3;7] + 1/2 -> 3 1 1 1 4 [3;7] divided by 4 -> 0 1 3 1 2
Scheme
For CHICKEN Scheme you need the r7rs egg.
I generate continued fractions differently from how the Racket does. I use an incomprehensible jumble of "calls with the current continuation" that lets you return values but keep going. Thus you can write your generators as loops or recursions. The method would work in Racket as well.
(Use of some procedure less general than call/cc might improve performance in Racket. In CHICKEN, call/cc itself should be efficient.)
;;;-------------------------------------------------------------------
;;;
;;; For R7RS Scheme, translated from the Racket.
;;;
(cond-expand
(r7rs)
(chicken (import r7rs))) ; CHICKEN is not natively R7RS.
;;;-------------------------------------------------------------------
;;;
;;; A partial implementation of Icon-style co-expressions.
;;;
;;; This limited form does not implement co-expressions that receive
;;; inputs.
;;;
(define-library (suspendable-procedures)
(export suspend)
(export make-generator-procedure)
(import (scheme base))
(begin
(define *suspend* (make-parameter (lambda (x) x)))
(define (suspend v) ((*suspend*) v))
(define (make-generator-procedure thunk)
;; This is for making a suspendable procedure that takes no
;; arguments when resumed. The result is a simple generator of
;; values.
(define (next-run return)
(define (my-suspend v)
(set! return (call/cc (lambda (resumption-point)
(set! next-run resumption-point)
(return v)))))
(parameterize ((*suspend* my-suspend))
(suspend (thunk))))
(lambda () (call/cc next-run)))
)) ;; end library
;;;-------------------------------------------------------------------
;;;
;;; Let us decide how we wish to do integer division.
;;;
(define-library (division-procedures)
(export div rem divrem)
(import (scheme base))
(begin
(define div floor-quotient)
(define rem floor-remainder)
(define divrem floor/)))
;;;-------------------------------------------------------------------
;;;
;;; The Main part of the baby-NG matrices. They are implemented as
;;; R7RS (SRFI-9) records, made to look like the Racket structs.
;;;
(define-library (baby-ng-matrices)
(export ng ng?
ng-a1 set-ng-a1!
ng-a set-ng-a!
ng-b1 set-ng-b1!
ng-b set-ng-b!)
(export ng-ingress!
ng-needterm?
ng-egress!
ng-infty!
ng-done?)
(import (scheme base))
(import (division-procedures))
(begin
(define-record-type <ng>
(ng a1 a b1 b)
ng?
(a1 ng-a1 set-ng-a1!)
(a ng-a set-ng-a!)
(b1 ng-b1 set-ng-b1!)
(b ng-b set-ng-b!))
(define (ng-ingress! v t)
(define a (ng-a v))
(define a1 (ng-a1 v))
(define b (ng-b v))
(define b1 (ng-b1 v))
(set-ng-a! v a1)
(set-ng-a1! v (+ a (* a1 t)))
(set-ng-b! v b1)
(set-ng-b1! v (+ b (* b1 t))))
(define (ng-needterm? v)
(or (zero? (ng-b v))
(zero? (ng-b1 v))
(not (= (div (ng-a v) (ng-b v))
(div (ng-a1 v) (ng-b1 v))))))
(define (ng-egress! v)
(define t (div (ng-a v) (ng-b v)))
(define a (ng-a v))
(define a1 (ng-a1 v))
(define b (ng-b v))
(define b1 (ng-b1 v))
(set-ng-a! v b)
(set-ng-a1! v b1)
(set-ng-b! v (- a (* b t)))
(set-ng-b1! v (- a1 (* b1 t)))
t)
(define (ng-infty! v)
(when (ng-needterm? v)
(set-ng-a! v (ng-a1 v))
(set-ng-b! v (ng-b1 v))))
(define (ng-done? v)
(and (zero? (ng-b v))
(zero? (ng-b1 v))))
)) ;; end library
;;;-------------------------------------------------------------------
;;;
;;; Procedures to create generators of continued fractions. (The
;;; Racket implementations could have been adapted, but I like to use
;;; my suspendable-procedures library.)
;;;
(define-library (cf-generators)
(export make-generator:rational->cf
make-generator:sqrt2->cf
make-generator:apply-baby-ng)
(import (scheme base))
(import (baby-ng-matrices))
(import (suspendable-procedures))
(import (division-procedures))
(begin
;; Generate n/d.
(define (make-generator:rational->cf n d)
(make-generator-procedure
(lambda ()
(let loop ((n n)
(d d))
(if (zero? d)
(begin
;; One might reasonably (suspend +inf.0) instead of
;; (suspend #f)
(suspend #f)
(loop n d))
(let-values (((q r) (divrem n d)))
(suspend q)
(loop d r)))))))
;; Generate sqrt(2).
(define (make-generator:sqrt2->cf)
(make-generator-procedure
(lambda ()
(suspend 1)
(let loop ()
(suspend 2)
(loop)))))
;; Apply a baby NG to a generator, resulting in a new generator.
(define (make-generator:apply-baby-ng ng gen)
(make-generator-procedure
(lambda ()
(let loop ()
(let ((t (gen)))
(when t
(ng-ingress! ng t)
(unless (ng-needterm? ng)
(suspend (ng-egress! ng)))
(loop))))
(let loop ()
(cond ((ng-done? ng)
(suspend #f)
(loop))
((ng-needterm? ng)
(ng-infty! ng)
(loop))
(else
(suspend (ng-egress! ng))
(loop)))))))
)) ;; end library
;;;-------------------------------------------------------------------
;;;
;;; Demo.
;;;
(define-library (demonstration)
(export demonstration)
(import (scheme base))
(import (scheme cxr))
(import (scheme write))
(import (baby-ng-matrices))
(import (cf-generators))
(begin
(define (display-cf max-digits)
(lambda (gen)
(let loop ((i 0)
(sep "["))
(if (= i max-digits)
(display ",...")
(let ((digit (gen)))
(when digit
(display sep)
(display digit)
(loop (+ i 1) (if (string=? sep "[") ";" ","))))))
(display "]")))
(define demonstration-instances
(let ((rat make-generator:rational->cf)
(sr2 make-generator:sqrt2->cf))
`(("[1;5,2] + 1/2" ,(ng 2 1 0 2) ,(rat 13 11))
("[3;7] + 1/2" ,(ng 2 1 0 2) ,(rat 22 7))
("[3;7] / 4" ,(ng 1 0 0 4) ,(rat 22 7))
("sqrt(2)/2", (ng 1 0 0 2) ,(sr2))
("1/sqrt(2)" ,(ng 0 1 1 0) ,(sr2))
("(1+sqrt(2))/2" ,(ng 1 1 0 2) ,(sr2))
("(2+sqrt(2))/4 = (1+1/sqrt(2))/2" ,(ng 1 2 0 4) ,(sr2)))))
(define (demonstration max-digits)
(define dsply (display-cf max-digits))
(do ((p demonstration-instances (cdr p)))
((null? p))
(let ((expr-string (caar p))
(baby-ng (cadar p))
(gen (caddar p)))
(display expr-string)
(display " => ")
(dsply (make-generator:apply-baby-ng baby-ng gen))
(newline))))
)) ;; end library
(import (demonstration))
(demonstration 20)
;;;-------------------------------------------------------------------
- Output:
$ gosh single-continued-fraction-task.scm [1;5,2] + 1/2 => [1;1,2,7] [3;7] + 1/2 => [3;1,1,1,4] [3;7] / 4 => [0;1,3,1,2] sqrt(2)/2 => [0;1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...] 1/sqrt(2) => [0;1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...] (1+sqrt(2))/2 => [1;4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,...] (2+sqrt(2))/4 = (1+1/sqrt(2))/2 => [0;1,5,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,...]
Tcl
This uses the Generator
class, R2CF
class and printcf
procedure from the r2cf task.
# The single-operand version of the NG operator, using our little generator framework
oo::class create NG1 {
superclass Generator
variable a1 a b1 b cf
constructor args {
next
lassign $args a1 a b1 b
}
method Ingress n {
lassign [list [expr {$a + $a1*$n}] $a1 [expr {$b + $b1*$n}] $b1] \
a1 a b1 b
}
method NeedTerm? {} {
expr {$b1 == 0 || $b == 0 || $a/$b != $a1/$b1}
}
method Egress {} {
set n [expr {$a/$b}]
lassign [list $b1 $b [expr {$a1 - $b1*$n}] [expr {$a - $b*$n}]] \
a1 a b1 b
return $n
}
method EgressDone {} {
if {[my NeedTerm?]} {
set a $a1
set b $b1
}
tailcall my Egress
}
method Done? {} {
expr {$b1 == 0 && $b == 0}
}
method operand {N} {
set cf $N
return [self]
}
method Produce {} {
while 1 {
set n [$cf]
if {![my NeedTerm?]} {
yield [my Egress]
}
my Ingress $n
}
while {![my Done?]} {
yield [my EgressDone]
}
}
}
Demonstrating:
# The square root of 2 as a continued fraction in the framework
oo::class create Root2 {
superclass Generator
method apply {} {
yield 1
while {[self] ne ""} {
yield 2
}
}
}
set op [[NG1 new 2 1 0 2] operand [R2CF new 13/11]]
printcf "\[1;5,2\] + 1/2" $op
set op [[NG1 new 7 0 0 22] operand [R2CF new 22/7]]
printcf "\[3;7\] * 7/22" $op
set op [[NG1 new 2 1 0 2] operand [R2CF new 22/7]]
printcf "\[3;7\] + 1/2" $op
set op [[NG1 new 1 0 0 4] operand [R2CF new 22/7]]
printcf "\[3;7\] / 4" $op
set op [[NG1 new 0 1 1 0] operand [Root2 new]]
printcf "1/\u221a2" $op 20
set op [[NG1 new 1 1 0 2] operand [Root2 new]]
printcf "(1+\u221a2)/2" $op 20
printcf "approx val" [R2CF new 24142136 20000000]
- Output:
[1;5,2] + 1/2 -> 1,1,2,7 [3;7] * 7/22 -> 1 [3;7] + 1/2 -> 3,1,1,1,4 [3;7] / 4 -> 0,1,3,1,2 1/√2 -> 0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,… (1+√2)/2 -> 1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,… approx val -> 1,4,1,4,1,4,1,4,1,4,3,2,1,9,5
Wren
import "/dynamic" for Tuple
var CFData = Tuple.create("Tuple", ["str", "ng", "r", "gen"])
var r2cf = Fn.new { |frac|
var num = frac[0]
var den = frac[1]
while (den.abs != 0) {
var div = (num/den).truncate
var rem = num % den
num = den
den = rem
Fiber.yield(div)
}
}
var d2cf = Fn.new { |d|
while (true) {
var div = d.floor
var rem = d - div
Fiber.yield(div)
if (rem == 0) break
d = 1 / rem
}
}
var root2 = Fn.new {
Fiber.yield(1)
while (true) Fiber.yield(2)
}
var recipRoot2 = Fn.new {
Fiber.yield(0)
Fiber.yield(1)
while (true) Fiber.yield(2)
}
class NG {
construct new(a1, a, b1, b) {
_a1 = a1
_a = a
_b1 = b1
_b = b
}
ingress(n) {
var t = _a
_a = _a1
_a1 = t + _a1 * n
t = _b
_b = _b1
_b1 = t + _b1 * n
}
egress() {
var n = (_a/_b).truncate
var t = _a
_a = _b
_b = t - _b * n
t = _a1
_a1 = _b1
_b1 = t - _b1 * n
return n
}
needTerm { (_b == 0 || _b1 == 0) || ((_a / _b) != (_a1 / _b1)) }
egressDone {
if (needTerm) {
_a = _a1
_b = _b1
}
return egress()
}
done { _b == 0 && _b1 == 0 }
}
var data = [
CFData.new("[1;5,2] + 1/2 ", [2, 1, 0, 2], [13, 11], r2cf),
CFData.new("[3;7] + 1/2 ", [2, 1, 0, 2], [22, 7], r2cf),
CFData.new("[3;7] divided by 4 ", [1, 0, 0, 4], [22, 7], r2cf),
CFData.new("sqrt(2) ", [0, 1, 1, 0], [ 0, 0], recipRoot2),
CFData.new("1 / sqrt(2) ", [0, 1, 1, 0], [ 0, 0], root2),
CFData.new("(1 + sqrt(2)) / 2 ", [1, 1, 0, 2], [ 0, 0], root2),
CFData.new("(1 + 1 / sqrt(2)) / 2", [1, 1, 0, 2], [ 0, 0], recipRoot2)
]
System.print("Produced by NG class:")
for (cfd in data) {
System.write("%(cfd.str) -> ")
var a1 = cfd.ng[0]
var a = cfd.ng[1]
var b1 = cfd.ng[2]
var b = cfd.ng[3]
var op = NG.new(a1, a, b1, b)
var seq = []
var i = 0
var fib = Fiber.new(cfd.gen)
while (i < 20) {
var j = fib.call(cfd.r)
if (j) seq.add(j) else break
i = i + 1
}
for (n in seq) {
if (!op.needTerm) System.write(" %(op.egress()) ")
op.ingress(n)
}
while (true) {
System.write(" %(op.egressDone) ")
if (op.done) break
}
System.print()
}
System.print("\nProduced by direct calculation:")
var data2 = [
["(1 + sqrt(2)) / 2 ", (1 + 2.sqrt) / 2],
["(1 + 1 / sqrt(2)) / 2", (1 + 1 / 2.sqrt) / 2]
]
for (p in data2) {
var seq = []
var fib = Fiber.new(d2cf)
var i = 0
while (i < 20) {
var j = fib.call(p[1])
if (j) seq.add(j) else break
i = i + 1
}
System.print("%(p[0]) -> %(seq.join(" "))")
}
- Output:
Produced by NG class: [1;5,2] + 1/2 -> 1 1 2 7 [3;7] + 1/2 -> 3 1 1 1 4 [3;7] divided by 4 -> 0 1 3 1 2 sqrt(2) -> 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 / sqrt(2) -> 0 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 (1 + sqrt(2)) / 2 -> 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 (1 + 1 / sqrt(2)) / 2 -> 0 1 5 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 5 Produced by direct calculation: (1 + sqrt(2)) / 2 -> 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 (1 + 1 / sqrt(2)) / 2 -> 0 1 5 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1 4 1
zkl
class NG{
fcn init(_a1,_a, _b1,_b){ var a1=_a1,a=_a, b1=_b1,b=_b; }
var [proxy] done =fcn{ b==0 and b1==0 };
var [proxy] needterm=fcn{ (b==0 or b1==0) or (a/b!=a1/b1) };
fcn ingress(n){
t:=self.copy(True); // tmp copy of vars for eager vs late evaluation
a,a1=t.a1, t.a + n*t.a1;
b,b1=t.b1, t.b + n*t.b1;
}
fcn egress{
n,t:=a/b,self.copy(True);
a,b =t.b, t.a - n*t.b;
a1,b1=t.b1,t.a1 - n*t.b1;
n
}
fcn egress_done{
if(needterm) a,b=a1,b1;
egress()
}
}
// from task: Continued fraction/Arithmetic/Construct from rational number
fcn r2cf(nom,dnom){ // -->Walker (iterator)
Walker.tweak(fcn(_,state){
nom,dnom:=state;
if(dnom==0) return(Void.Stop);
n,d:=nom.divr(dnom);
state.clear(dnom,d);
n
}.fp1(List(nom,dnom)))
}
data:=T(T("[1;5,2] + 1/2", T(2,1,0,2), T(13,11)),
T("[3;7] + 1/2", T(2,1,0,2), T(22, 7)),
T("[3;7] divided by 4", T(1,0,0,4), T(22, 7)));
foreach string,ng,r in (data){
print("%-20s-->".fmt(string));
op:=NG(ng.xplode());
foreach n in (r2cf(r.xplode())){
if(not op.needterm) print(" %s".fmt(op.egress()));
op.ingress(n);
}
do{ print(" ",op.egress_done()) }while(not op.done);
println();
}
- Output:
[1;5,2] + 1/2 --> 1 1 2 7 [3;7] + 1/2 --> 3 1 1 1 4 [3;7] divided by 4 --> 0 1 3 1 2