Continued fraction/Arithmetic/G(matrix ng, continued fraction n): Difference between revisions
Content added Content deleted
Line 2,967: | Line 2,967: | ||
1 4 1 4 1 4 1 4 1 4 3 2 1 9 5 |
1 4 1 4 1 4 1 4 1 4 3 2 1 9 5 |
||
</pre> |
</pre> |
||
=={{header|Fortran}}== |
|||
{{trans|ATS}} |
|||
{{trans|C}} |
|||
Unlike the ATS and C implementations upon which this Fortran code is based, there is no garbage collector. The memory management is tricky, and if you find bugs in it, please feel free to fix them. |
|||
(Fortran standards allow garbage collection, but the NAG compiler is the only Fortran compiler I know of that offers garbage collection as an option. I am using GNU Fortran.) |
|||
<syntaxhighlight lang="fortran"> |
|||
!--------------------------------------------------------------------- |
|||
module continued_fractions |
|||
! |
|||
! Continued fractions with memoization. |
|||
! |
|||
implicit none |
|||
private |
|||
public :: cf_generator_proc_t |
|||
public :: cf_generator_t |
|||
public :: cf_t |
|||
public :: cf_generator_make |
|||
public :: cf_make |
|||
public :: cf_generator_make_from_cf |
|||
public :: cf_get_at |
|||
public :: cf2string_max_terms |
|||
public :: cf2string_default_max_terms |
|||
public :: cf2string |
|||
public :: default_max_terms |
|||
integer :: default_max_terms = 20 |
|||
interface |
|||
subroutine cf_generator_proc_t (env, term_exists, term) |
|||
class(*), intent(inout) :: env |
|||
logical, intent(out) :: term_exists |
|||
integer, intent(out) :: term |
|||
end subroutine cf_generator_proc_t |
|||
end interface |
|||
type :: cf_generator_t |
|||
procedure(cf_generator_proc_t), pointer, nopass :: proc |
|||
class(*), pointer :: env |
|||
integer :: refcount = 0 |
|||
contains |
|||
final :: cf_generator_t_finalize |
|||
procedure :: cf_generator_t_refcount_incr |
|||
procedure :: cf_generator_t_refcount_decr |
|||
end type cf_generator_t |
|||
type :: cf_memo_t |
|||
integer, pointer :: storage(:) |
|||
integer :: refcount = 0 |
|||
contains |
|||
final :: cf_memo_t_finalize |
|||
procedure :: cf_memo_t_refcount_incr |
|||
procedure :: cf_memo_t_refcount_decr |
|||
end type cf_memo_t |
|||
type :: cf_t |
|||
logical :: terminated |
|||
integer :: m |
|||
integer :: n |
|||
class(cf_memo_t), pointer :: memo |
|||
class(cf_generator_t), pointer :: gen |
|||
contains |
|||
final :: cf_t_finalize |
|||
end type cf_t |
|||
interface cf2string |
|||
! |
|||
! Overload the name "cf2string". |
|||
! |
|||
module procedure cf2string_max_terms |
|||
module procedure cf2string_default_max_terms |
|||
end interface |
|||
type :: cf_generator_from_cf_env_t |
|||
class(cf_t), pointer :: cf |
|||
integer :: i |
|||
end type cf_generator_from_cf_env_t |
|||
contains |
|||
subroutine cf_generator_make (gen, proc, env) |
|||
type(cf_generator_t), intent(out), pointer :: gen |
|||
interface |
|||
subroutine proc (env, term_exists, term) |
|||
class(*), intent(inout) :: env |
|||
logical, intent(out) :: term_exists |
|||
integer, intent(out) :: term |
|||
end subroutine proc |
|||
end interface |
|||
class(*), pointer, intent(inout) :: env |
|||
allocate (gen) |
|||
gen%proc => proc |
|||
gen%env => env |
|||
end subroutine cf_generator_make |
|||
subroutine cf_generator_t_refcount_incr (gen) |
|||
class(cf_generator_t), intent(inout) :: gen |
|||
gen%refcount = gen%refcount + 1 |
|||
end subroutine cf_generator_t_refcount_incr |
|||
subroutine cf_generator_t_refcount_decr (gen) |
|||
class(cf_generator_t), intent(inout) :: gen |
|||
gen%refcount = gen%refcount - 1 |
|||
end subroutine cf_generator_t_refcount_decr |
|||
subroutine cf_generator_t_finalize (gen) |
|||
type(cf_generator_t), intent(inout) :: gen |
|||
deallocate (gen%env) |
|||
end subroutine cf_generator_t_finalize |
|||
subroutine cf_memo_t_refcount_incr (memo) |
|||
class(cf_memo_t), intent(inout) :: memo |
|||
memo%refcount = memo%refcount + 1 |
|||
end subroutine cf_memo_t_refcount_incr |
|||
subroutine cf_memo_t_refcount_decr (memo) |
|||
class(cf_memo_t), intent(inout) :: memo |
|||
memo%refcount = memo%refcount - 1 |
|||
end subroutine cf_memo_t_refcount_decr |
|||
subroutine cf_memo_t_finalize (memo) |
|||
type(cf_memo_t), intent(inout) :: memo |
|||
deallocate (memo%storage) |
|||
end subroutine cf_memo_t_finalize |
|||
subroutine cf_make (cf, gen) |
|||
type(cf_t), pointer, intent(out) :: cf |
|||
type(cf_generator_t), pointer, intent(inout) :: gen |
|||
integer, parameter :: start_size = 8 |
|||
allocate (cf) |
|||
allocate (cf%memo) |
|||
allocate (cf%memo%storage(0 : start_size - 1)) |
|||
cf%terminated = .false. |
|||
cf%m = 0 |
|||
cf%n = start_size |
|||
cf%gen => gen |
|||
call cf%memo%cf_memo_t_refcount_incr |
|||
call cf%gen%cf_generator_t_refcount_incr |
|||
end subroutine cf_make |
|||
subroutine cf_t_finalize (cf) |
|||
type(cf_t), intent(inout) :: cf |
|||
call cf%memo%cf_memo_t_refcount_decr |
|||
if (cf%memo%refcount == 0) deallocate (cf%memo) |
|||
call cf%gen%cf_generator_t_refcount_decr |
|||
if (cf%gen%refcount == 0) deallocate (cf%gen) |
|||
end subroutine cf_t_finalize |
|||
subroutine cf_generator_make_from_cf (gen, cf) |
|||
! |
|||
! TAKE NOTE: deallocating gen DOES NOT deallocate cf. (Most likely |
|||
! you would not want it to do so.) |
|||
! |
|||
type(cf_generator_t), intent(out), pointer :: gen |
|||
type(cf_t), pointer, intent(inout) :: cf |
|||
type(cf_generator_from_cf_env_t), pointer :: env |
|||
class(*), pointer :: p |
|||
allocate (env) |
|||
env%cf => cf |
|||
env%i = 0 |
|||
p => env |
|||
call cf_generator_make (gen, cf_generator_from_cf_proc, p) |
|||
end subroutine cf_generator_make_from_cf |
|||
subroutine cf_generator_from_cf_proc (env, term_exists, term) |
|||
class(*), intent(inout) :: env |
|||
logical, intent(out) :: term_exists |
|||
integer, intent(out) :: term |
|||
select type (env) |
|||
class is (cf_generator_from_cf_env_t) |
|||
call cf_get_at (env%cf, env%i, term_exists, term) |
|||
env%i = env%i + 1 |
|||
end select |
|||
end subroutine cf_generator_from_cf_proc |
|||
subroutine cf_get_more_terms (cf, needed) |
|||
class(cf_t), intent(inout) :: cf |
|||
integer, intent(in) :: needed |
|||
integer :: term_count |
|||
logical :: done |
|||
logical :: term_exists |
|||
integer :: term |
|||
term_count = cf%m |
|||
done = .false. |
|||
do while (.not. done) |
|||
if (term_count == needed) then |
|||
cf%m = needed |
|||
done = .true. |
|||
else |
|||
call cf%gen%proc (cf%gen%env, term_exists, term) |
|||
if (term_exists) then |
|||
cf%memo%storage(term_count) = term |
|||
term_count = term_count + 1 |
|||
else |
|||
cf%terminated = .true. |
|||
cf%m = term_count |
|||
done = .true. |
|||
end if |
|||
end if |
|||
end do |
|||
end subroutine cf_get_more_terms |
|||
subroutine cf_update (cf, needed) |
|||
class(cf_t), intent(inout) :: cf |
|||
integer, intent(in) :: needed |
|||
integer, pointer :: storage1(:) |
|||
integer :: i |
|||
if (cf%terminated .or. needed <= cf%m) then |
|||
continue |
|||
else if (needed <= cf%n) then |
|||
call cf_get_more_terms (cf, needed) |
|||
else |
|||
! Provide twice the needed storage. |
|||
cf%n = 2 * needed |
|||
allocate (storage1(0:cf%n - 1)) |
|||
storage1(0:cf%m) = cf%memo%storage(0:cf%m) |
|||
deallocate (cf%memo%storage) |
|||
cf%memo%storage => storage1 |
|||
call cf_get_more_terms (cf, needed) |
|||
end if |
|||
end subroutine cf_update |
|||
subroutine cf_get_at (cf, i, term_exists, term) |
|||
class(cf_t), intent(inout) :: cf |
|||
integer, intent(in) :: i |
|||
logical, intent(out) :: term_exists |
|||
integer, intent(out) :: term |
|||
call cf_update (cf, i + 1) |
|||
term_exists = (i < cf%m) |
|||
if (term_exists) term = cf%memo%storage(i) |
|||
end subroutine cf_get_at |
|||
function cf2string_max_terms (cf, max_terms) result (s) |
|||
class(cf_t), intent(inout) :: cf |
|||
integer, intent(in) :: max_terms |
|||
character(len = :), allocatable :: s |
|||
integer :: sep |
|||
integer :: i, j |
|||
logical :: done |
|||
logical :: term_exists |
|||
integer :: term |
|||
character(len = 100) :: buf |
|||
s = "[" |
|||
sep = 0 |
|||
i = 0 |
|||
done = .false. |
|||
do while (.not. done) |
|||
if (i == max_terms) then |
|||
s = s // ",...]" |
|||
done = .true. |
|||
else |
|||
call cf_get_at (cf, i, term_exists, term) |
|||
if (term_exists) then |
|||
select case (sep) |
|||
case(0) |
|||
sep = 1 |
|||
case(1) |
|||
s = s // ";" |
|||
sep = 2 |
|||
case(2) |
|||
s = s // "," |
|||
end select |
|||
write (buf, '(I100)') term |
|||
j = 1 |
|||
do while (buf(j:j) == ' ') |
|||
j = j + 1 |
|||
end do |
|||
s = s // buf(j:100) |
|||
i = i + 1 |
|||
else |
|||
s = s // "]" |
|||
done = .true. |
|||
end if |
|||
end if |
|||
end do |
|||
end function cf2string_max_terms |
|||
function cf2string_default_max_terms (cf) result (s) |
|||
class(cf_t), intent(inout) :: cf |
|||
character(len = :), allocatable :: s |
|||
s = cf2string_max_terms (cf, default_max_terms) |
|||
end function cf2string_default_max_terms |
|||
end module continued_fractions |
|||
!--------------------------------------------------------------------- |
|||
module continued_fractions_r2cf |
|||
! |
|||
! Rational numbers. |
|||
! |
|||
use, non_intrinsic :: continued_fractions |
|||
implicit none |
|||
public :: r2cf_generator_make |
|||
public :: r2cf_make |
|||
type :: r2cf_generator_env_t |
|||
integer :: n, d |
|||
end type r2cf_generator_env_t |
|||
contains |
|||
subroutine r2cf_generator_make (gen, n, d) |
|||
type(cf_generator_t), pointer, intent(out) :: gen |
|||
integer, intent(in) :: n, d |
|||
type(r2cf_generator_env_t), pointer :: env |
|||
class(*), pointer :: p |
|||
allocate (env) |
|||
env%n = n |
|||
env%d = d |
|||
p => env |
|||
call cf_generator_make (gen, r2cf_generator_proc, p) |
|||
end subroutine r2cf_generator_make |
|||
subroutine r2cf_generator_proc (env, term_exists, term) |
|||
class(*), intent(inout) :: env |
|||
logical, intent(out) :: term_exists |
|||
integer, intent(out) :: term |
|||
integer :: q, r |
|||
select type (env) |
|||
class is (r2cf_generator_env_t) |
|||
term_exists = (env%d /= 0) |
|||
if (term_exists) then |
|||
! The direction in which to round the quotient is |
|||
! arbitrary. We will round it towards negative infinity. |
|||
r = modulo (env%n, env%d) |
|||
q = (env%n - r) / env%d |
|||
env%n = env%d |
|||
env%d = r |
|||
term = q |
|||
end if |
|||
end select |
|||
end subroutine r2cf_generator_proc |
|||
subroutine r2cf_make (cf, n, d) |
|||
type(cf_t), pointer, intent(out) :: cf |
|||
integer, intent(in) :: n, d |
|||
type(cf_generator_t), pointer :: gen |
|||
allocate (gen) |
|||
call r2cf_generator_make (gen, n, d) |
|||
call cf_make (cf, gen) |
|||
end subroutine r2cf_make |
|||
end module continued_fractions_r2cf |
|||
!--------------------------------------------------------------------- |
|||
module continued_fractions_sqrt2 |
|||
! |
|||
! The square root of two. |
|||
! |
|||
use, non_intrinsic :: continued_fractions |
|||
implicit none |
|||
public :: sqrt2_generator_make |
|||
public :: sqrt2_make |
|||
type :: sqrt2_generator_env_t |
|||
integer :: term |
|||
end type sqrt2_generator_env_t |
|||
contains |
|||
subroutine sqrt2_generator_make (gen) |
|||
type(cf_generator_t), pointer, intent(out) :: gen |
|||
type(sqrt2_generator_env_t), pointer :: env |
|||
class(*), pointer :: p |
|||
allocate (env) |
|||
env%term = 1 |
|||
p => env |
|||
call cf_generator_make (gen, sqrt2_generator_proc, p) |
|||
end subroutine sqrt2_generator_make |
|||
subroutine sqrt2_generator_proc (env, term_exists, term) |
|||
class(*), intent(inout) :: env |
|||
logical, intent(out) :: term_exists |
|||
integer, intent(out) :: term |
|||
select type (env) |
|||
class is (sqrt2_generator_env_t) |
|||
term_exists = .true. |
|||
term = env%term |
|||
env%term = 2 |
|||
end select |
|||
end subroutine sqrt2_generator_proc |
|||
subroutine sqrt2_make (cf) |
|||
type(cf_t), pointer, intent(out) :: cf |
|||
type(cf_generator_t), pointer :: gen |
|||
allocate (gen) |
|||
call sqrt2_generator_make (gen) |
|||
call cf_make (cf, gen) |
|||
end subroutine sqrt2_make |
|||
end module continued_fractions_sqrt2 |
|||
!--------------------------------------------------------------------- |
|||
module continued_fractions_hfunc |
|||
! |
|||
! Homographic functions of cf_t objects. |
|||
! |
|||
use, non_intrinsic :: continued_fractions |
|||
implicit none |
|||
public :: hfunc_make |
|||
type :: hfunc_generator_env_t |
|||
integer :: a1, a, b1, b |
|||
class(cf_generator_t), allocatable :: source_gen |
|||
end type hfunc_generator_env_t |
|||
contains |
|||
subroutine hfunc_generator_make (gen, a1, a, b1, b, source_gen) |
|||
type(cf_generator_t), pointer, intent(out) :: gen |
|||
integer, intent(in) :: a1, a, b1, b |
|||
type(cf_generator_t), pointer, intent(inout) :: source_gen |
|||
type(hfunc_generator_env_t), pointer :: env |
|||
class(*), pointer :: p |
|||
allocate (env) |
|||
env%a1 = a1 |
|||
env%a = a |
|||
env%b1 = b1 |
|||
env%b = b |
|||
env%source_gen = source_gen |
|||
p => env |
|||
call cf_generator_make (gen, hfunc_generator_proc, p) |
|||
end subroutine hfunc_generator_make |
|||
subroutine hfunc_generator_proc (env, term_exists, term) |
|||
class(*), intent(inout) :: env |
|||
logical, intent(out) :: term_exists |
|||
integer, intent(out) :: term |
|||
integer :: q1, q |
|||
logical :: done |
|||
select type (env) |
|||
class is (hfunc_generator_env_t) |
|||
done = .false. |
|||
do while (.not. done) |
|||
if (env%b1 == 0 .and. env%b == 0) then |
|||
term_exists = .false. |
|||
done = .true. |
|||
else if (env%b1 /= 0 .and. env%b /= 0) then |
|||
! Because I feel like it, let us round quotients |
|||
! towards negative infinity. |
|||
q1 = (env%a1 - modulo (env%a1, env%b1)) / env%b1 |
|||
q = (env%a - modulo (env%a, env%b)) / env%b |
|||
if (q1 == q) then |
|||
block |
|||
integer :: a1, a, b1, b |
|||
a1 = env%a1 |
|||
a = env%a |
|||
b1 = env%b1 |
|||
b = env%b |
|||
env%a1 = b1 |
|||
env%a = b |
|||
env%b1 = a1 - (b1 * q) |
|||
env%b = a - (b * q) |
|||
end block |
|||
term_exists = .true. |
|||
term = q |
|||
done = .true. |
|||
end if |
|||
end if |
|||
if (.not. done) then |
|||
call env%source_gen%proc (env%source_gen%env, term_exists, term) |
|||
if (term_exists) then |
|||
block |
|||
integer :: a1, a, b1, b |
|||
a1 = env%a1 |
|||
a = env%a |
|||
b1 = env%b1 |
|||
b = env%b |
|||
env%a1 = a + (a1 * term) |
|||
env%a = a1 |
|||
env%b1 = b + (b1 * term) |
|||
env%b = b1 |
|||
end block |
|||
else |
|||
env%a = env%a1 |
|||
env%b = env%b1 |
|||
end if |
|||
end if |
|||
end do |
|||
end select |
|||
end subroutine hfunc_generator_proc |
|||
subroutine hfunc_make (cf, a1, a, b1, b, source_cf) |
|||
type(cf_t), pointer, intent(out) :: cf |
|||
integer, intent(in) :: a1, a, b1, b |
|||
type(cf_t), pointer, intent(inout) :: source_cf |
|||
type(cf_generator_t), pointer :: gen |
|||
class(cf_generator_t), pointer :: source_gen |
|||
call cf_generator_make_from_cf (source_gen, source_cf) |
|||
call hfunc_generator_make (gen, a1, a, b1, b, source_gen) |
|||
call cf_make (cf, gen) |
|||
end subroutine hfunc_make |
|||
end module continued_fractions_hfunc |
|||
!--------------------------------------------------------------------- |
|||
program univariate_continued_fraction_task |
|||
use, non_intrinsic :: continued_fractions |
|||
use, non_intrinsic :: continued_fractions_r2cf |
|||
use, non_intrinsic :: continued_fractions_sqrt2 |
|||
use, non_intrinsic :: continued_fractions_hfunc |
|||
implicit none |
|||
type(cf_t), pointer :: cf_13_11 |
|||
type(cf_t), pointer :: cf_22_7 |
|||
type(cf_t), pointer :: cf_sqrt2 |
|||
type(cf_t), pointer :: cf_13_11_add_1_2 |
|||
type(cf_t), pointer :: cf_22_7_add_1_2 |
|||
type(cf_t), pointer :: cf_22_7_div_4 |
|||
type(cf_t), pointer :: cf_sqrt2_div_2 |
|||
type(cf_t), pointer :: cf_1_div_sqrt2 |
|||
type(cf_t), pointer :: cf_one_way |
|||
type(cf_t), pointer :: cf_another_way |
|||
call r2cf_make (cf_13_11, 13, 11) |
|||
call r2cf_make (cf_22_7, 22, 7) |
|||
call sqrt2_make (cf_sqrt2) |
|||
call hfunc_make (cf_13_11_add_1_2, 2, 1, 0, 2, cf_13_11) |
|||
call hfunc_make (cf_22_7_add_1_2, 2, 1, 0, 2, cf_22_7) |
|||
call hfunc_make (cf_22_7_div_4, 1, 0, 0, 4, cf_22_7) |
|||
call hfunc_make (cf_sqrt2_div_2, 1, 0, 0, 2, cf_sqrt2) |
|||
call hfunc_make (cf_1_div_sqrt2, 0, 1, 1, 0, cf_sqrt2) |
|||
call hfunc_make (cf_one_way, 1, 2, 0, 4, cf_sqrt2) |
|||
call hfunc_make (cf_another_way, 1, 1, 0, 2, cf_1_div_sqrt2) |
|||
write (*, '("13/11 => ", A)') cf2string (cf_13_11) |
|||
write (*, '("22/7 => ", A)') cf2string (cf_22_7) |
|||
write (*, '("sqrt(2) => ", A)') cf2string (cf_sqrt2) |
|||
write (*, '("13/11 + 1/2 => ", A)') cf2string (cf_13_11_add_1_2) |
|||
write (*, '("22/7 + 1/2 => ", A)') cf2string (cf_22_7_add_1_2) |
|||
write (*, '("(22/7)/4 => ", A)') cf2string (cf_22_7_div_4) |
|||
write (*, '("sqrt(2)/2 => ", A)') cf2string (cf_sqrt2_div_2) |
|||
write (*, '("1/sqrt(2) => ", A)') cf2string (cf_1_div_sqrt2) |
|||
write (*, '("(2 + sqrt(2))/4 => ", A)') cf2string (cf_one_way) |
|||
write (*, '("(1 + 1/sqrt(2))/2 => ", A)') cf2string (cf_another_way) |
|||
deallocate (cf_13_11) |
|||
deallocate (cf_22_7) |
|||
deallocate (cf_sqrt2) |
|||
deallocate (cf_13_11_add_1_2) |
|||
deallocate (cf_22_7_add_1_2) |
|||
deallocate (cf_22_7_div_4) |
|||
deallocate (cf_sqrt2_div_2) |
|||
deallocate (cf_1_div_sqrt2) |
|||
deallocate (cf_one_way) |
|||
deallocate (cf_another_way) |
|||
end program univariate_continued_fraction_task |
|||
!--------------------------------------------------------------------- |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>$ gfortran -g -std=f2018 univariate_continued_fraction_task.f90 && ./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 + 1/sqrt(2))/2 => [0;1,5,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,...]</pre> |
|||
=={{header|Go}}== |
=={{header|Go}}== |