Continued fraction/Arithmetic/G(matrix ng, continued fraction n1, continued fraction n2): Difference between revisions
Content added Content deleted
Line 2,626: | Line 2,626: | ||
sqrt(2) * sqrt(2) => [2] |
sqrt(2) * sqrt(2) => [2] |
||
sqrt(2) / sqrt(2) => [1] |
sqrt(2) / sqrt(2) => [1] |
||
</pre> |
|||
=={{header|Fortran}}== |
|||
{{trans|Python}} |
|||
{{trans|ObjectIcon}} |
|||
This program includes a primitive module for multiple-precision integer arithmetic. |
|||
<syntaxhighlight lang="fortran"> |
|||
!--------------------------------------------------------------------- |
|||
module big_integers ! Big (but not very big) integers. |
|||
! NOTE: I assume that iachar and achar do not alter the most |
|||
! significant bit. |
|||
use, intrinsic :: iso_fortran_env, only: int16 |
|||
implicit none |
|||
private |
|||
public :: big_integer |
|||
public :: integer2big |
|||
public :: string2big |
|||
public :: big2string |
|||
public :: big_sgn |
|||
public :: big_cmp, big_cmpabs |
|||
public :: big_neg, big_abs |
|||
public :: big_addabs, big_add |
|||
public :: big_subabs, big_sub |
|||
public :: big_mul ! One might also include a big_muladd. |
|||
public :: big_divrem ! One could also include big_div and big_rem. |
|||
public :: operator(+) |
|||
public :: operator(-) |
|||
public :: operator(*) |
|||
type :: big_integer |
|||
! The representation is sign-magnitude. The radix is 256, which |
|||
! is not speed-efficient, but which seemed relatively easy to |
|||
! work with if one were writing in standard Fortran (and assuming |
|||
! iachar and achar were "8-bit clean"). |
|||
logical :: sign = .false. ! .false. => +sign, .true. => -sign. |
|||
character, allocatable :: bytes(:) |
|||
end type big_integer |
|||
character, parameter :: zero = achar (0) |
|||
character, parameter :: one = achar (1) |
|||
! An integer type capable of holding an unsigned 8-bit value. |
|||
integer, parameter :: bytekind = int16 |
|||
interface operator(+) |
|||
module procedure big_add |
|||
end interface |
|||
interface operator(-) |
|||
module procedure big_neg |
|||
module procedure big_sub |
|||
end interface |
|||
interface operator(*) |
|||
module procedure big_mul |
|||
end interface |
|||
contains |
|||
elemental function logical2byte (bool) result (byte) |
|||
logical, intent(in) :: bool |
|||
character :: byte |
|||
if (bool) then |
|||
byte = one |
|||
else |
|||
byte = zero |
|||
end if |
|||
end function logical2byte |
|||
elemental function logical2i (bool) result (i) |
|||
logical, intent(in) :: bool |
|||
integer :: i |
|||
if (bool) then |
|||
i = 1 |
|||
else |
|||
i = 0 |
|||
end if |
|||
end function logical2i |
|||
elemental function byte2i (c) result (i) |
|||
character, intent(in) :: c |
|||
integer :: i |
|||
i = iachar (c) |
|||
end function byte2i |
|||
elemental function i2byte (i) result (c) |
|||
integer, intent(in) :: i |
|||
character :: c |
|||
c = achar (i) |
|||
end function i2byte |
|||
elemental function byte2bk (c) result (i) |
|||
character, intent(in) :: c |
|||
integer(bytekind) :: i |
|||
i = iachar (c, kind = bytekind) |
|||
end function byte2bk |
|||
elemental function bk2byte (i) result (c) |
|||
integer(bytekind), intent(in) :: i |
|||
character :: c |
|||
c = achar (i) |
|||
end function bk2byte |
|||
elemental function bk2i (i) result (j) |
|||
integer(bytekind), intent(in) :: i |
|||
integer :: j |
|||
j = int (i) |
|||
end function bk2i |
|||
elemental function i2bk (i) result (j) |
|||
integer, intent(in) :: i |
|||
integer(bytekind) :: j |
|||
j = int (iand (i, 255), kind = bytekind) |
|||
end function i2bk |
|||
! Left shift of the least significant 8 bits of a bytekind integer. |
|||
elemental function lshftbk (a, i) result (c) |
|||
integer(bytekind), intent(in) :: a |
|||
integer, intent(in) :: i |
|||
integer(bytekind) :: c |
|||
c = ishft (ibits (a, 0, 8 - i), i) |
|||
end function lshftbk |
|||
! Right shift of the least significant 8 bits of a bytekind integer. |
|||
elemental function rshftbk (a, i) result (c) |
|||
integer(bytekind), intent(in) :: a |
|||
integer, intent(in) :: i |
|||
integer(bytekind) :: c |
|||
c = ibits (a, i, 8 - i) |
|||
end function rshftbk |
|||
! Left shift an integer. |
|||
elemental function lshfti (a, i) result (c) |
|||
integer, intent(in) :: a |
|||
integer, intent(in) :: i |
|||
integer :: c |
|||
c = ishft (a, i) |
|||
end function lshfti |
|||
! Right shift an integer. |
|||
elemental function rshfti (a, i) result (c) |
|||
integer, intent(in) :: a |
|||
integer, intent(in) :: i |
|||
integer :: c |
|||
c = ishft (a, -i) |
|||
end function rshfti |
|||
function integer2big (i) result (a) |
|||
integer, intent(in) :: i |
|||
type(big_integer), allocatable :: a |
|||
! |
|||
! To write a more efficient implementation of this procedure is |
|||
! left as an exercise for the reader. |
|||
! |
|||
character(len = 100) :: buffer |
|||
write (buffer, '(I0)') i |
|||
a = string2big (trim (buffer)) |
|||
end function integer2big |
|||
function string2big (s) result (a) |
|||
character(len = *), intent(in) :: s |
|||
type(big_integer), allocatable :: a |
|||
integer :: n, i, istart, iend |
|||
integer :: digit |
|||
if ((s(1:1) == '-') .or. s(1:1) == '+') then |
|||
istart = 2 |
|||
else |
|||
istart = 1 |
|||
end if |
|||
iend = len (s) |
|||
n = (iend - istart + 2) / 2 |
|||
allocate (a) |
|||
allocate (a%bytes(n)) |
|||
a%bytes = zero |
|||
do i = istart, iend |
|||
digit = ichar (s(i:i)) - ichar ('0') |
|||
if (digit < 0 .or. 9 < digit) error stop |
|||
a = short_multiplication (a, 10) |
|||
a = short_addition (a, digit) |
|||
end do |
|||
a%sign = (s(1:1) == '-') |
|||
call normalize (a) |
|||
end function string2big |
|||
function big2string (a) result (s) |
|||
type(big_integer), intent(in) :: a |
|||
character(len = :), allocatable :: s |
|||
type(big_integer), allocatable :: q |
|||
integer :: r |
|||
integer :: sgn |
|||
sgn = big_sgn (a) |
|||
if (sgn == 0) then |
|||
s = '0' |
|||
else |
|||
q = a |
|||
s = '' |
|||
do while (big_sgn (q) /= 0) |
|||
call short_division (q, 10, q, r) |
|||
s = achar (r + ichar ('0')) // s |
|||
end do |
|||
if (sgn < 0) s = '-' // s |
|||
end if |
|||
end function big2string |
|||
function big_sgn (a) result (sgn) |
|||
type(big_integer), intent(in) :: a |
|||
integer :: sgn |
|||
integer :: n, i |
|||
n = size (a%bytes) |
|||
i = 1 |
|||
sgn = 1234 |
|||
do while (sgn == 1234) |
|||
if (i == n + 1) then |
|||
sgn = 0 |
|||
else if (a%bytes(i) /= zero) then |
|||
if (a%sign) then |
|||
sgn = -1 |
|||
else |
|||
sgn = 1 |
|||
end if |
|||
else |
|||
i = i + 1 |
|||
end if |
|||
end do |
|||
end function big_sgn |
|||
function big_cmp (a, b) result (cmp) |
|||
type(big_integer(*)), intent(in) :: a, b |
|||
integer :: cmp |
|||
if (a%sign) then |
|||
if (b%sign) then |
|||
cmp = -big_cmpabs (a, b) |
|||
else |
|||
cmp = -1 |
|||
end if |
|||
else |
|||
if (b%sign) then |
|||
cmp = 1 |
|||
else |
|||
cmp = big_cmpabs (a, b) |
|||
end if |
|||
end if |
|||
end function big_cmp |
|||
function big_cmpabs (a, b) result (cmp) |
|||
type(big_integer(*)), intent(in) :: a, b |
|||
integer :: cmp |
|||
integer :: n, i |
|||
integer :: ia, ib |
|||
cmp = 1234 |
|||
n = max (size (a%bytes), size (b%bytes)) |
|||
i = n |
|||
do while (cmp == 1234) |
|||
if (i == 0) then |
|||
cmp = 0 |
|||
else |
|||
ia = byteval (a, i) |
|||
ib = byteval (b, i) |
|||
if (ia < ib) then |
|||
cmp = -1 |
|||
else if (ia > ib) then |
|||
cmp = 1 |
|||
else |
|||
i = i - 1 |
|||
end if |
|||
end if |
|||
end do |
|||
end function big_cmpabs |
|||
function big_neg (a) result (c) |
|||
type(big_integer), intent(in) :: a |
|||
type(big_integer), allocatable :: c |
|||
c = a |
|||
c%sign = .not. c%sign |
|||
end function big_neg |
|||
function big_abs (a) result (c) |
|||
type(big_integer), intent(in) :: a |
|||
type(big_integer), allocatable :: c |
|||
c = a |
|||
c%sign = .false. |
|||
end function big_abs |
|||
function big_add (a, b) result (c) |
|||
type(big_integer), intent(in) :: a |
|||
type(big_integer), intent(in) :: b |
|||
type(big_integer), allocatable :: c |
|||
logical :: sign |
|||
if (a%sign) then |
|||
if (b%sign) then ! a <= 0, b <= 0 |
|||
c = big_addabs (a, b) |
|||
sign = .true. |
|||
else ! a <= 0, b >= 0 |
|||
c = big_subabs (a, b) |
|||
sign = .not. c%sign |
|||
end if |
|||
else |
|||
if (b%sign) then ! a >= 0, b <= 0 |
|||
c = big_subabs (a, b) |
|||
sign = c%sign |
|||
else ! a >= 0, b >= 0 |
|||
c = big_addabs (a, b) |
|||
sign = .false. |
|||
end if |
|||
end if |
|||
c%sign = sign |
|||
end function big_add |
|||
function big_sub (a, b) result (c) |
|||
type(big_integer), intent(in) :: a |
|||
type(big_integer), intent(in) :: b |
|||
type(big_integer), allocatable :: c |
|||
logical :: sign |
|||
if (a%sign) then |
|||
if (b%sign) then ! a <= 0, b <= 0 |
|||
c = big_subabs (a, b) |
|||
sign = .not. c%sign |
|||
else ! a <= 0, b >= 0 |
|||
c = big_addabs (a, b) |
|||
sign = .true. |
|||
end if |
|||
else |
|||
if (b%sign) then ! a >= 0, b <= 0 |
|||
c = big_addabs (a, b) |
|||
sign = .false. |
|||
else ! a >= 0, b >= 0 |
|||
c = big_subabs (a, b) |
|||
sign = c%sign |
|||
end if |
|||
end if |
|||
c%sign = sign |
|||
end function big_sub |
|||
function big_addabs (a, b) result (c) |
|||
type(big_integer), intent(in) :: a, b |
|||
type(big_integer), allocatable :: c |
|||
! Compute abs(a) + abs(b). |
|||
integer :: n, nc, i |
|||
logical :: carry |
|||
type(big_integer), allocatable :: tmp |
|||
n = max (size (a%bytes), size (b%bytes)) |
|||
nc = n + 1 |
|||
allocate(tmp) |
|||
allocate(tmp%bytes(nc)) |
|||
call add_bytes (get_byte (a, 1), get_byte (b, 1), .false., tmp%bytes(1), carry) |
|||
do i = 2, n |
|||
call add_bytes (get_byte (a, i), get_byte (b, i), carry, tmp%bytes(i), carry) |
|||
end do |
|||
tmp%bytes(nc) = logical2byte (carry) |
|||
call normalize (tmp) |
|||
c = tmp |
|||
end function big_addabs |
|||
function big_subabs (a, b) result (c) |
|||
type(big_integer), intent(in) :: a, b |
|||
type(big_integer), allocatable :: c |
|||
! Compute abs(a) - abs(b). The result is signed. |
|||
integer :: n, i |
|||
logical :: carry |
|||
type(big_integer), allocatable :: tmp |
|||
n = max (size (a%bytes), size (b%bytes)) |
|||
allocate(tmp) |
|||
allocate(tmp%bytes(n)) |
|||
if (big_cmpabs (a, b) >= 0) then |
|||
tmp%sign = .false. |
|||
call sub_bytes (get_byte (a, 1), get_byte (b, 1), .false., tmp%bytes(1), carry) |
|||
do i = 2, n |
|||
call sub_bytes (get_byte (a, i), get_byte (b, i), carry, tmp%bytes(i), carry) |
|||
end do |
|||
else |
|||
tmp%sign = .true. |
|||
call sub_bytes (get_byte (b, 1), get_byte (a, 1), .false., tmp%bytes(1), carry) |
|||
do i = 2, n |
|||
call sub_bytes (get_byte (b, i), get_byte (a, i), carry, tmp%bytes(i), carry) |
|||
end do |
|||
end if |
|||
call normalize (tmp) |
|||
c = tmp |
|||
end function big_subabs |
|||
function big_mul (a, b) result (c) |
|||
type(big_integer), intent(in) :: a, b |
|||
type(big_integer), allocatable :: c |
|||
! |
|||
! This is Knuth, Volume 2, Algorithm 4.3.1M. |
|||
! |
|||
integer :: na, nb, nc |
|||
integer :: i, j |
|||
integer :: ia, ib, ic |
|||
integer :: carry |
|||
type(big_integer), allocatable :: tmp |
|||
na = size (a%bytes) |
|||
nb = size (b%bytes) |
|||
nc = na + nb + 1 |
|||
allocate (tmp) |
|||
allocate (tmp%bytes(nc)) |
|||
tmp%bytes = zero |
|||
j = 1 |
|||
do j = 1, nb |
|||
ib = byte2i (b%bytes(j)) |
|||
if (ib /= 0) then |
|||
carry = 0 |
|||
do i = 1, na |
|||
ia = byte2i (a%bytes(i)) |
|||
ic = byte2i (tmp%bytes(i + j - 1)) |
|||
ic = (ia * ib) + ic + carry |
|||
tmp%bytes(i + j - 1) = i2byte (iand (ic, 255)) |
|||
carry = ishft (ic, -8) |
|||
end do |
|||
tmp%bytes(na + j) = i2byte (carry) |
|||
end if |
|||
end do |
|||
tmp%sign = (a%sign .neqv. b%sign) |
|||
call normalize (tmp) |
|||
c = tmp |
|||
end function big_mul |
|||
subroutine big_divrem (a, b, q, r) |
|||
type(big_integer), intent(in) :: a, b |
|||
type(big_integer), allocatable, intent(inout) :: q, r |
|||
! |
|||
! Division with a remainder that is never negative. Equivalently, |
|||
! this is floor division if the divisor is positive, and ceiling |
|||
! division if the divisor is negative. |
|||
! |
|||
! See Raymond T. Boute, "The Euclidean definition of the functions |
|||
! div and mod", ACM Transactions on Programming Languages and |
|||
! Systems, Volume 14, Issue 2, pp. 127-144. |
|||
! https://doi.org/10.1145/128861.128862 |
|||
! |
|||
call nonnegative_division (a, b, .true., .true., q, r) |
|||
if (a%sign) then |
|||
if (big_sgn (r) /= 0) then |
|||
q = short_addition (q, 1) |
|||
r = big_sub (big_abs (b), r) |
|||
end if |
|||
q%sign = .not. b%sign |
|||
else |
|||
q%sign = b%sign |
|||
end if |
|||
end subroutine big_divrem |
|||
function short_addition (a, b) result (c) |
|||
type(big_integer), intent(in) :: a |
|||
integer, intent(in) :: b |
|||
type(big_integer), allocatable :: c |
|||
! Compute abs(a) + b. |
|||
integer :: na, nc, i |
|||
logical :: carry |
|||
type(big_integer), allocatable :: tmp |
|||
na = size (a%bytes) |
|||
nc = na + 1 |
|||
allocate(tmp) |
|||
allocate(tmp%bytes(nc)) |
|||
call add_bytes (a%bytes(1), i2byte (b), .false., tmp%bytes(1), carry) |
|||
do i = 2, na |
|||
call add_bytes (a%bytes(i), zero, carry, tmp%bytes(i), carry) |
|||
end do |
|||
tmp%bytes(nc) = logical2byte (carry) |
|||
call normalize (tmp) |
|||
c = tmp |
|||
end function short_addition |
|||
function short_multiplication (a, b) result (c) |
|||
type(big_integer), intent(in) :: a |
|||
integer, intent(in) :: b |
|||
type(big_integer), allocatable :: c |
|||
integer :: i, na, nc |
|||
integer :: ia, ic |
|||
integer :: carry |
|||
type(big_integer), allocatable :: tmp |
|||
na = size (a%bytes) |
|||
nc = na + 1 |
|||
allocate (tmp) |
|||
allocate (tmp%bytes(nc)) |
|||
tmp%sign = a%sign |
|||
carry = 0 |
|||
do i = 1, na |
|||
ia = byte2i (a%bytes(i)) |
|||
ic = (ia * b) + carry |
|||
tmp%bytes(i) = i2byte (iand (ic, 255)) |
|||
carry = ishft (ic, -8) |
|||
end do |
|||
tmp%bytes(nc) = i2byte (carry) |
|||
call normalize (tmp) |
|||
c = tmp |
|||
end function short_multiplication |
|||
! Division without regard to signs. |
|||
subroutine nonnegative_division (a, b, want_q, want_r, q, r) |
|||
type(big_integer), intent(in) :: a, b |
|||
logical, intent(in) :: want_q, want_r |
|||
type(big_integer), intent(inout), allocatable :: q, r |
|||
integer :: na, nb |
|||
integer :: remainder |
|||
na = size (a%bytes) |
|||
nb = size (b%bytes) |
|||
! It is an error if b has "significant" zero-bytes or is equal to |
|||
! zero. |
|||
if (b%bytes(nb) == zero) error stop |
|||
if (nb == 1) then |
|||
if (want_q) then |
|||
call short_division (a, byte2i (b%bytes(1)), q, remainder) |
|||
else |
|||
block |
|||
type(big_integer), allocatable :: bit_bucket |
|||
call short_division (a, byte2i (b%bytes(1)), bit_bucket, remainder) |
|||
end block |
|||
end if |
|||
if (want_r) then |
|||
if (allocated (r)) deallocate (r) |
|||
allocate (r) |
|||
allocate (r%bytes(1)) |
|||
r%bytes(1) = i2byte (remainder) |
|||
end if |
|||
else |
|||
if (na >= nb) then |
|||
call long_division (a, b, want_q, want_r, q, r) |
|||
else |
|||
if (want_q) q = string2big ("0") |
|||
if (want_r) r = a |
|||
end if |
|||
end if |
|||
end subroutine nonnegative_division |
|||
subroutine short_division (a, b, q, r) |
|||
type(big_integer), intent(in) :: a |
|||
integer, intent(in) :: b |
|||
type(big_integer), intent(inout), allocatable :: q |
|||
integer, intent(inout) :: r |
|||
! |
|||
! This is Knuth, Volume 2, Exercise 4.3.1.16. |
|||
! |
|||
! The divisor is assumed to be positive. |
|||
! |
|||
integer :: n, i |
|||
integer :: ia, ib, iq |
|||
type(big_integer), allocatable :: tmp |
|||
ib = b |
|||
n = size (a%bytes) |
|||
allocate (tmp) |
|||
allocate (tmp%bytes(n)) |
|||
r = 0 |
|||
do i = n, 1, -1 |
|||
ia = (256 * r) + byte2i (a%bytes(i)) |
|||
iq = ia / ib |
|||
r = mod (ia, ib) |
|||
tmp%bytes(i) = i2byte (iq) |
|||
end do |
|||
tmp%sign = a%sign |
|||
call normalize (tmp) |
|||
q = tmp |
|||
end subroutine short_division |
|||
subroutine long_division (a, b, want_quotient, want_remainder, quotient, remainder) |
|||
type(big_integer), intent(in) :: a, b |
|||
logical, intent(in) :: want_quotient, want_remainder |
|||
type(big_integer), intent(inout), allocatable :: quotient |
|||
type(big_integer), intent(inout), allocatable :: remainder |
|||
! |
|||
! This is Knuth, Volume 2, Algorithm 4.3.1D. |
|||
! |
|||
! We do not deal here with the signs of the inputs and outputs. |
|||
! |
|||
! It is assumed size(a%bytes) >= size(b%bytes), and that b has no |
|||
! leading zero-bytes and is at least two bytes long. If b is one |
|||
! byte long and nonzero, use short division. |
|||
! |
|||
! |
|||
! FIXME: THIS ROUTINE IS COMPLICATED AND HAS BRANCHES THAT ARE |
|||
! SELDOM RUN. IT HAS NOT BEEN EXTENSIVELY TESTED. Unit |
|||
! tests and bugfixes for it would be appreciated. |
|||
! |
|||
integer :: na, nb, m, n |
|||
integer :: num_lz, num_nonlz |
|||
integer :: j |
|||
integer :: qhat |
|||
integer :: carry |
|||
! |
|||
! We will NOT be working with VERY large numbers, and so it will |
|||
! be safe to put temporary storage on the stack. (Note: your |
|||
! Fortran might put this storage in a heap instead of the stack.) |
|||
! |
|||
! v = b, normalized to put its most significant 1-bit all the |
|||
! way left. |
|||
! |
|||
! u = a, shifted left by the same amount as b. |
|||
! |
|||
! q = the quotient. |
|||
! |
|||
! The remainder, although shifted left, will end up in u. |
|||
! |
|||
integer(bytekind) :: u(0:size (a%bytes) + size (b%bytes)) |
|||
integer(bytekind) :: v(0:size (b%bytes) - 1) |
|||
integer(bytekind) :: q(0:size (a%bytes) - size (b%bytes)) |
|||
na = size (a%bytes) |
|||
nb = size (b%bytes) |
|||
n = nb |
|||
m = na - nb |
|||
! In the most significant byte of the divisor, find the number of |
|||
! leading zero bits, and the number of bits after that. |
|||
block |
|||
integer(bytekind) :: tmp |
|||
tmp = byte2bk (b%bytes(n)) |
|||
num_nonlz = bit_size (tmp) - leadz (tmp) |
|||
num_lz = 8 - num_nonlz |
|||
end block |
|||
call normalize_v (b%bytes) ! Make the most significant bit of v be one. |
|||
call normalize_u (a%bytes) ! Shifted by the same amount as v. |
|||
! Assure ourselves that the most significant bit of v is a one. |
|||
if (.not. btest (v(n - 1), 7)) error stop |
|||
do j = m, 0, -1 |
|||
call calculate_qhat (qhat) |
|||
call multiply_and_subtract (carry) |
|||
q(j) = i2bk (qhat) |
|||
if (carry /= 0) call add_back |
|||
end do |
|||
if (want_quotient) then |
|||
if (allocated (quotient)) deallocate (quotient) |
|||
allocate (quotient) |
|||
allocate (quotient%bytes(m + 1)) |
|||
quotient%bytes = bk2byte (q) |
|||
call normalize (quotient) |
|||
end if |
|||
if (want_remainder) then |
|||
if (allocated (remainder)) deallocate (remainder) |
|||
allocate (remainder) |
|||
allocate (remainder%bytes(n)) |
|||
call unnormalize_u (remainder%bytes) |
|||
call normalize (remainder) |
|||
end if |
|||
contains |
|||
subroutine normalize_v (b_bytes) |
|||
character, intent(in) :: b_bytes(n) |
|||
! |
|||
! Normalize v so its most significant bit is a one. Any |
|||
! normalization factor that achieves this goal will suffice; we |
|||
! choose 2**num_lz. (Knuth uses (2**32) div (y[n-1] + 1).) |
|||
! |
|||
! Strictly for readability, we use linear stack space for an |
|||
! intermediate result. |
|||
! |
|||
integer :: i |
|||
integer(bytekind) :: btmp(0:n - 1) |
|||
btmp = byte2bk (b_bytes) |
|||
v(0) = lshftbk (btmp(0), num_lz) |
|||
do i = 1, n - 1 |
|||
v(i) = ior (lshftbk (btmp(i), num_lz), & |
|||
& rshftbk (btmp(i - 1), num_nonlz)) |
|||
end do |
|||
end subroutine normalize_v |
|||
subroutine normalize_u (a_bytes) |
|||
character, intent(in) :: a_bytes(m + n) |
|||
! |
|||
! Shift a leftwards to get u. Shift by as much as b was shifted |
|||
! to get v. |
|||
! |
|||
! Strictly for readability, we use linear stack space for an |
|||
! intermediate result. |
|||
! |
|||
integer :: i |
|||
integer(bytekind) :: atmp(0:m + n - 1) |
|||
atmp = byte2bk (a_bytes) |
|||
u(0) = lshftbk (atmp(0), num_lz) |
|||
do i = 1, m + n - 1 |
|||
u(i) = ior (lshftbk (atmp(i), num_lz), & |
|||
& rshftbk (atmp(i - 1), num_nonlz)) |
|||
end do |
|||
u(m + n) = rshftbk (atmp(m + n - 1), num_nonlz) |
|||
end subroutine normalize_u |
|||
subroutine unnormalize_u (r_bytes) |
|||
character, intent(out) :: r_bytes(n) |
|||
! |
|||
! Strictly for readability, we use linear stack space for an |
|||
! intermediate result. |
|||
! |
|||
integer :: i |
|||
integer(bytekind) :: rtmp(0:n - 1) |
|||
do i = 0, n - 1 |
|||
rtmp(i) = ior (rshftbk (u(i), num_lz), & |
|||
& lshftbk (u(i + 1), num_nonlz)) |
|||
end do |
|||
rtmp(n - 1) = rshftbk (u(n - 1), num_lz) |
|||
r_bytes = bk2byte (rtmp) |
|||
end subroutine unnormalize_u |
|||
subroutine calculate_qhat (qhat) |
|||
integer, intent(out) :: qhat |
|||
integer :: itmp, rhat |
|||
logical :: adjust |
|||
itmp = ior (lshfti (bk2i (u(j + n)), 8), & |
|||
& bk2i (u(j + n - 1))) |
|||
qhat = itmp / bk2i (v(n - 1)) |
|||
rhat = mod (itmp, bk2i (v(n - 1))) |
|||
adjust = .true. |
|||
do while (adjust) |
|||
if (rshfti (qhat, 8) /= 0) then |
|||
continue |
|||
else if (qhat * bk2i (v(n - 2)) & |
|||
& > ior (lshfti (rhat, 8), & |
|||
& bk2i (u(j + n - 2)))) then |
|||
continue |
|||
else |
|||
adjust = .false. |
|||
end if |
|||
if (adjust) then |
|||
qhat = qhat - 1 |
|||
rhat = rhat + bk2i (v(n - 1)) |
|||
if (rshfti (rhat, 8) == 0) then |
|||
adjust = .false. |
|||
end if |
|||
end if |
|||
end do |
|||
end subroutine calculate_qhat |
|||
subroutine multiply_and_subtract (carry) |
|||
integer, intent(out) :: carry |
|||
integer :: i |
|||
integer :: qhat_v |
|||
integer :: mul_carry, sub_carry |
|||
integer :: diff |
|||
mul_carry = 0 |
|||
sub_carry = 0 |
|||
do i = 0, n |
|||
! Multiplication. |
|||
qhat_v = mul_carry |
|||
if (i /= n) qhat_v = qhat_v + (qhat * bk2i (v(i))) |
|||
mul_carry = rshfti (qhat_v, 8) |
|||
qhat_v = iand (qhat_v, 255) |
|||
! Subtraction. |
|||
diff = bk2i (u(j + i)) - qhat_v + sub_carry |
|||
sub_carry = -(logical2i (diff < 0)) ! Carry 0 or -1. |
|||
u(j + i) = i2bk (diff) |
|||
end do |
|||
carry = sub_carry |
|||
end subroutine multiply_and_subtract |
|||
subroutine add_back |
|||
integer :: i, carry, sum |
|||
q(j) = q(j) - 1_bytekind |
|||
carry = 0 |
|||
do i = 0, n - 1 |
|||
sum = bk2i (u(i)) + bk2i (v(i)) + carry |
|||
carry = ishft (sum, -8) |
|||
u(i) = i2bk (sum) |
|||
end do |
|||
end subroutine add_back |
|||
end subroutine long_division |
|||
subroutine add_bytes (a, b, carry_in, c, carry_out) |
|||
character, intent(in) :: a, b |
|||
logical, value :: carry_in |
|||
character, intent(inout) :: c |
|||
logical, intent(inout) :: carry_out |
|||
integer :: ia, ib, ic |
|||
ia = byte2i (a) |
|||
if (carry_in) ia = ia + 1 |
|||
ib = byte2i (b) |
|||
ic = ia + ib |
|||
c = i2byte (iand (ic, 255)) |
|||
carry_out = (ic >= 256) |
|||
end subroutine add_bytes |
|||
subroutine sub_bytes (a, b, carry_in, c, carry_out) |
|||
character, intent(in) :: a, b |
|||
logical, value :: carry_in |
|||
character, intent(inout) :: c |
|||
logical, intent(inout) :: carry_out |
|||
integer :: ia, ib, ic |
|||
ia = byte2i (a) |
|||
ib = byte2i (b) |
|||
if (carry_in) ib = ib + 1 |
|||
ic = ia - ib |
|||
carry_out = (ic < 0) |
|||
if (carry_out) ic = ic + 256 |
|||
c = i2byte (iand (ic, 255)) |
|||
end subroutine sub_bytes |
|||
function get_byte (a, i) result (byte) |
|||
type(big_integer), intent(in) :: a |
|||
integer, intent(in) :: i |
|||
character :: byte |
|||
if (size (a%bytes) < i) then |
|||
byte = zero |
|||
else |
|||
byte = a%bytes(i) |
|||
end if |
|||
end function get_byte |
|||
function byteval (a, i) result (v) |
|||
type(big_integer), intent(in) :: a |
|||
integer, intent(in) :: i |
|||
integer :: v |
|||
if (size (a%bytes) < i) then |
|||
v = 0 |
|||
else |
|||
v = byte2i (a%bytes(i)) |
|||
end if |
|||
end function byteval |
|||
subroutine normalize (a) |
|||
type(big_integer), intent(inout) :: a |
|||
integer :: i |
|||
character, allocatable :: fewer_bytes(:) |
|||
! Shorten to the minimum number of bytes. |
|||
i = size (a%bytes) |
|||
do while (1 < i .and. a%bytes(i) == zero) |
|||
i = i - 1 |
|||
end do |
|||
if (i /= size (a%bytes)) then |
|||
allocate (fewer_bytes (i)) |
|||
fewer_bytes = a%bytes(1:i) |
|||
call move_alloc (fewer_bytes, a%bytes) |
|||
end if |
|||
! If the magnitude is zero, then clear the sign bit. |
|||
if (size (a%bytes) == 1) then |
|||
if (a%bytes(1) == zero) then |
|||
a%sign = .false. |
|||
end if |
|||
end if |
|||
end subroutine normalize |
|||
end module big_integers |
|||
!--------------------------------------------------------------------- |
|||
module continued_fractions |
|||
use, non_intrinsic :: big_integers |
|||
implicit none |
|||
private |
|||
public :: continued_fraction |
|||
public :: term_generator |
|||
public :: term_generator_procedure |
|||
public :: make_continued_fraction |
|||
public :: i2cf |
|||
public :: make_integer_continued_fraction |
|||
public :: make_integer_continued_fraction_from_integer |
|||
public :: constant_term_cf |
|||
public :: make_constant_term_continued_fraction |
|||
public :: make_constant_term_continued_fraction_from_integer |
|||
public :: apply_ng8 |
|||
public :: apply_ng8_big_integers |
|||
public :: apply_ng8_integers |
|||
public :: ng8_coefficient_threshold |
|||
public :: ng8_term_threshold |
|||
public :: add_continued_fractions |
|||
public :: subtract_continued_fractions |
|||
public :: multiply_continued_fractions |
|||
public :: divide_continued_fractions |
|||
public :: cf2string |
|||
public :: continued_fraction_to_string_given_max_terms |
|||
public :: continued_fraction_to_string_with_default_max_terms |
|||
public :: default_continued_fraction_max_terms |
|||
type :: continued_fraction |
|||
class(continued_fraction_record), pointer, private :: p => null () |
|||
contains |
|||
procedure, pass :: get_term => get_continued_fraction_term |
|||
procedure, pass :: term_exists => continued_fraction_term_exists |
|||
procedure, pass :: term => continued_fraction_term |
|||
procedure, pass :: to_string => continued_fraction_to_string_with_default_max_terms |
|||
procedure, pass :: add => add_continued_fractions |
|||
generic :: operator(+) => add |
|||
procedure, pass :: subtract => subtract_continued_fractions |
|||
generic :: operator(-) => subtract |
|||
procedure, pass :: multiply => multiply_continued_fractions |
|||
generic :: operator(*) => multiply |
|||
procedure, pass :: divide => divide_continued_fractions |
|||
generic :: operator(/) => divide |
|||
procedure, pass, private :: continued_fraction_make_new_ref |
|||
generic :: assignment(=) => continued_fraction_make_new_ref |
|||
final :: continued_fraction_final |
|||
end type continued_fraction |
|||
type :: continued_fraction_record |
|||
logical, private :: terminated = .false. ! No more terms? |
|||
integer, private :: m = 0 ! No. of terms memoized. |
|||
type(big_integer), private, allocatable :: memo(:) ! Memoized terms. |
|||
class(term_generator), pointer :: gen ! Where terms come from. |
|||
integer :: refcount = 0 |
|||
contains |
|||
procedure, pass :: get_term => get_continued_fraction_record_term |
|||
procedure, pass :: term_exists => continued_fraction_record_term_exists |
|||
procedure, pass :: term => continued_fraction_record_term |
|||
final :: continued_fraction_record_final |
|||
end type continued_fraction_record |
|||
type, abstract :: term_generator |
|||
contains |
|||
procedure(term_generator_procedure), pass, deferred :: generate |
|||
end type term_generator |
|||
interface |
|||
subroutine term_generator_procedure (gen, term_exists, term) |
|||
import term_generator |
|||
import big_integer |
|||
class(term_generator), intent(inout) :: gen |
|||
logical, intent(out) :: term_exists |
|||
type(big_integer), allocatable, intent(out) :: term |
|||
end subroutine term_generator_procedure |
|||
end interface |
|||
type, extends (term_generator) :: integer_term_generator |
|||
type(big_integer), allocatable :: term |
|||
logical :: no_more_terms = .false. |
|||
contains |
|||
procedure, pass :: generate => integer_term_generator_generate |
|||
end type integer_term_generator |
|||
type, extends (term_generator) :: constant_term_generator |
|||
type(big_integer), allocatable :: term |
|||
contains |
|||
procedure, pass :: generate => constant_term_generator_generate |
|||
end type constant_term_generator |
|||
type, extends (term_generator) :: ng8_term_generator |
|||
type(big_integer), allocatable :: a12, a1, a2, a |
|||
type(big_integer), allocatable :: b12, b1, b2, b |
|||
type(continued_fraction) :: x, y |
|||
integer :: ix = 0 |
|||
integer :: iy = 0 |
|||
logical :: x_overflow = .false. |
|||
logical :: y_overflow = .false. |
|||
contains |
|||
procedure, pass :: generate => ng8_term_generator_generate |
|||
end type ng8_term_generator |
|||
interface i2cf |
|||
module procedure make_integer_continued_fraction |
|||
module procedure make_integer_continued_fraction_from_integer |
|||
end interface i2cf |
|||
interface constant_term_cf |
|||
module procedure make_constant_term_continued_fraction |
|||
module procedure make_constant_term_continued_fraction_from_integer |
|||
end interface constant_term_cf |
|||
interface apply_ng8 |
|||
module procedure apply_ng8_big_integers |
|||
module procedure apply_ng8_integers |
|||
end interface apply_ng8 |
|||
interface cf2string |
|||
module procedure continued_fraction_to_string_given_max_terms |
|||
module procedure continued_fraction_to_string_with_default_max_terms |
|||
end interface cf2string |
|||
integer :: default_continued_fraction_max_terms = 20 |
|||
type(big_integer), allocatable :: ng8_coefficient_threshold |
|||
type(big_integer), allocatable :: ng8_term_threshold |
|||
contains |
|||
subroutine continued_fraction_make_new_ref (dst, src) |
|||
class(continued_fraction), intent(inout) :: dst |
|||
class(continued_fraction), intent(in) :: src |
|||
if (associated (dst%p)) deallocate (dst%p) |
|||
dst%p => src%p |
|||
dst%p%refcount = dst%p%refcount + 1 |
|||
end subroutine continued_fraction_make_new_ref |
|||
subroutine continued_fraction_final (cf) |
|||
type(continued_fraction), intent(inout) :: cf |
|||
cf%p%refcount = cf%p%refcount - 1 |
|||
if (cf%p%refcount == 0) deallocate (cf%p) |
|||
end subroutine continued_fraction_final |
|||
function make_continued_fraction (gen) result (cf) |
|||
class(term_generator), pointer, intent(in) :: gen |
|||
type(continued_fraction) :: cf |
|||
allocate (cf%p) |
|||
allocate (cf%p%memo(0:31)) ! The starting size is arbitrary. |
|||
cf%p%gen => gen |
|||
cf%p%refcount = cf%p%refcount + 1 |
|||
end function make_continued_fraction |
|||
subroutine continued_fraction_record_final (cfrec) |
|||
type(continued_fraction_record), intent(inout) :: cfrec |
|||
deallocate (cfrec%gen) |
|||
end subroutine continued_fraction_record_final |
|||
function make_integer_continued_fraction (bigint) result (cf) |
|||
type(big_integer), intent(in) :: bigint |
|||
type(continued_fraction) :: cf |
|||
class(integer_term_generator), pointer :: gen |
|||
allocate (gen) |
|||
gen%term = bigint |
|||
cf = make_continued_fraction (gen) |
|||
end function make_integer_continued_fraction |
|||
function make_integer_continued_fraction_from_integer (i) result (cf) |
|||
integer, intent(in) :: i |
|||
type(continued_fraction) :: cf |
|||
cf = make_integer_continued_fraction (integer2big (i)) |
|||
end function make_integer_continued_fraction_from_integer |
|||
subroutine integer_term_generator_generate (gen, term_exists, term) |
|||
class(integer_term_generator), intent(inout) :: gen |
|||
logical, intent(out) :: term_exists |
|||
type(big_integer), allocatable, intent(out) :: term |
|||
term_exists = (.not. gen%no_more_terms) |
|||
if (term_exists) term = gen%term |
|||
gen%no_more_terms = .true. |
|||
end subroutine integer_term_generator_generate |
|||
function make_constant_term_continued_fraction (bigint) result (cf) |
|||
type(big_integer), intent(in) :: bigint |
|||
type(continued_fraction) :: cf |
|||
class(constant_term_generator), pointer :: gen |
|||
allocate (gen) |
|||
gen%term = bigint |
|||
cf = make_continued_fraction (gen) |
|||
end function make_constant_term_continued_fraction |
|||
function make_constant_term_continued_fraction_from_integer (i) result (cf) |
|||
integer, intent(in) :: i |
|||
type(continued_fraction) :: cf |
|||
cf = make_constant_term_continued_fraction (integer2big (i)) |
|||
end function make_constant_term_continued_fraction_from_integer |
|||
subroutine constant_term_generator_generate (gen, term_exists, term) |
|||
class(constant_term_generator), intent(inout) :: gen |
|||
logical, intent(out) :: term_exists |
|||
type(big_integer), allocatable, intent(out) :: term |
|||
term_exists = .true. |
|||
if (term_exists) term = gen%term |
|||
end subroutine constant_term_generator_generate |
|||
function apply_ng8_big_integers (a12, a1, a2, a, & |
|||
& b12, b1, b2, b, x, y) result (cf) |
|||
type(big_integer), intent(in) :: a12, a1, a2, a |
|||
type(big_integer), intent(in) :: b12, b1, b2, b |
|||
class(continued_fraction), intent(in) :: x, y |
|||
type(continued_fraction) :: cf |
|||
class(ng8_term_generator), pointer :: gen |
|||
allocate (gen) |
|||
gen%a12 = a12; gen%a1 = a1; gen%a2 = a2; gen%a = a |
|||
gen%b12 = b12; gen%b1 = b1; gen%b2 = b2; gen%b = b |
|||
gen%x = x |
|||
gen%y = y |
|||
cf = make_continued_fraction (gen) |
|||
end function apply_ng8_big_integers |
|||
function apply_ng8_integers (a12, a1, a2, a, & |
|||
& b12, b1, b2, b, x, y) result (cf) |
|||
integer, intent(in) :: a12, a1, a2, a |
|||
integer, intent(in) :: b12, b1, b2, b |
|||
class(continued_fraction), intent(in) :: x, y |
|||
type(continued_fraction) :: cf |
|||
cf = apply_ng8_big_integers (integer2big (a12), & |
|||
& integer2big (a1), & |
|||
& integer2big (a2), & |
|||
& integer2big (a), & |
|||
& integer2big (b12), & |
|||
& integer2big (b1), & |
|||
& integer2big (b2), & |
|||
& integer2big (b), x, y) |
|||
end function apply_ng8_integers |
|||
subroutine ng8_term_generator_generate (gen, term_exists, term) |
|||
class(ng8_term_generator), intent(inout) :: gen |
|||
logical, intent(out) :: term_exists |
|||
type(big_integer), allocatable, intent(out) :: term |
|||
logical :: done |
|||
logical :: b12z, b1z, b2z, bz |
|||
type(big_integer), allocatable :: q12, r12 |
|||
type(big_integer), allocatable :: q1, r1 |
|||
type(big_integer), allocatable :: q2, r2 |
|||
type(big_integer), allocatable :: q, r |
|||
logical :: absorb_x, absorb_y, compare_fracs |
|||
done = .false. |
|||
do while (.not. done) |
|||
absorb_x = .false. |
|||
absorb_y = .false. |
|||
compare_fracs = .false. |
|||
b12z = (big_sgn (gen%b12) == 0) |
|||
b1z = (big_sgn (gen%b1) == 0) |
|||
b2z = (big_sgn (gen%b2) == 0) |
|||
bz = (big_sgn (gen%b) == 0) |
|||
if (b12z .and. b1z .and. b2z .and. bz) then |
|||
! There are no more terms. |
|||
term_exists = .false. |
|||
done = .true. |
|||
else if (b2z .and. bz) then |
|||
absorb_x = .true. |
|||
else if (b2z .or. bz) then |
|||
absorb_y = .true. |
|||
else if (b1z) then |
|||
absorb_x = .true. |
|||
else |
|||
call big_divrem (gen%a1, gen%b1, q1, r1) |
|||
call big_divrem (gen%a2, gen%b2, q2, r2) |
|||
call big_divrem (gen%a, gen%b, q, r) |
|||
if (.not. b12z) then |
|||
call big_divrem (gen%a12, gen%b12, q12, r12) |
|||
if (big_cmp (q, q1) /= 0) then |
|||
compare_fracs = .true. |
|||
else if (big_cmp (q, q2) /= 0) then |
|||
compare_fracs = .true. |
|||
else if (big_cmp (q, q12) /= 0) then |
|||
compare_fracs = .true. |
|||
else |
|||
call output_term |
|||
done = .true. |
|||
end if |
|||
end if |
|||
end if |
|||
if (compare_fracs) call compare_fractions (absorb_x, absorb_y) |
|||
if (absorb_x) call absorb_x_term |
|||
if (absorb_y) call absorb_y_term |
|||
end do |
|||
contains |
|||
subroutine output_term |
|||
gen%a12 = gen%b12; gen%a1 = gen%b1; gen%a2 = gen%b2; gen%a = gen%b |
|||
gen%b12 = r12; gen%b1 = r1; gen%b2 = r2; gen%b = r |
|||
term_exists = (.not. treat_as_infinite (q)) |
|||
if (term_exists) term = q |
|||
end subroutine output_term |
|||
subroutine compare_fractions (absorb_x, absorb_y) |
|||
logical, intent(inout) :: absorb_x, absorb_y |
|||
! Rather than compare fractions, we will put the numerators over |
|||
! a common denominator of b1*b2*b, and then compare the new |
|||
! numerators. |
|||
type(big_integer), allocatable :: n1, n2, n |
|||
n1 = gen%a1 * gen%b2 * gen%b |
|||
n2 = gen%a2 * gen%b1 * gen%b |
|||
n = gen%a * gen%b1 * gen%b2 |
|||
if (big_cmpabs (n1 - n, n2 - n) > 0) then |
|||
absorb_x = .true. |
|||
else |
|||
absorb_y = .true. |
|||
end if |
|||
end subroutine compare_fractions |
|||
subroutine absorb_x_term |
|||
logical :: term_exists |
|||
type(big_integer), allocatable :: term |
|||
type(big_integer), allocatable :: new_a12, new_a1, new_a2, new_a |
|||
type(big_integer), allocatable :: new_b12, new_b1, new_b2, new_b |
|||
if (gen%x_overflow) then |
|||
term_exists = .false. |
|||
else |
|||
term_exists = gen%x%term_exists(gen%ix) |
|||
end if |
|||
new_a2 = gen%a12 |
|||
new_a = gen%a1 |
|||
new_b2 = gen%b12 |
|||
new_b = gen%b1 |
|||
if (term_exists) then |
|||
term = gen%x%term(gen%ix) |
|||
new_a12 = gen%a2 + (gen%a12 * term) |
|||
new_a1 = gen%a + (gen%a1 * term) |
|||
new_b12 = gen%b2 + (gen%b12 * term) |
|||
new_b1 = gen%b + (gen%b1 * term) |
|||
if (any_too_big (new_a12, new_a1, new_a2, new_a, & |
|||
& new_b12, new_b1, new_b2, new_b)) then |
|||
gen%x_overflow = .true. |
|||
new_a12 = gen%a12 |
|||
new_a1 = gen%a1 |
|||
new_b12 = gen%b12 |
|||
new_b1 = gen%b1 |
|||
end if |
|||
else |
|||
new_a12 = gen%a12 |
|||
new_a1 = gen%a1 |
|||
new_b12 = gen%b12 |
|||
new_b1 = gen%b1 |
|||
end if |
|||
gen%a12 = new_a12; gen%a1 = new_a1; gen%a2 = new_a2; gen%a = new_a |
|||
gen%b12 = new_b12; gen%b1 = new_b1; gen%b2 = new_b2; gen%b = new_b |
|||
gen%ix = gen%ix + 1 |
|||
end subroutine absorb_x_term |
|||
subroutine absorb_y_term |
|||
logical :: term_exists |
|||
type(big_integer), allocatable :: term |
|||
type(big_integer), allocatable :: new_a12, new_a1, new_a2, new_a |
|||
type(big_integer), allocatable :: new_b12, new_b1, new_b2, new_b |
|||
if (gen%y_overflow) then |
|||
term_exists = .false. |
|||
else |
|||
term_exists = gen%y%term_exists(gen%iy) |
|||
end if |
|||
new_a1 = gen%a12 |
|||
new_a = gen%a2 |
|||
new_b1 = gen%b12 |
|||
new_b = gen%b2 |
|||
if (term_exists) then |
|||
term = gen%y%term(gen%iy) |
|||
new_a12 = gen%a1 + (gen%a12 * term) |
|||
new_a2 = gen%a + (gen%a2 * term) |
|||
new_b12 = gen%b1 + (gen%b12 * term) |
|||
new_b2 = gen%b + (gen%b2 * term) |
|||
if (any_too_big (new_a12, new_a1, new_a2, new_a, & |
|||
& new_b12, new_b1, new_b2, new_b)) then |
|||
gen%y_overflow = .true. |
|||
new_a12 = gen%a12 |
|||
new_a2 = gen%a2 |
|||
new_b12 = gen%b12 |
|||
new_b2 = gen%b2 |
|||
end if |
|||
else |
|||
new_a12 = gen%a12 |
|||
new_a2 = gen%a2 |
|||
new_b12 = gen%b12 |
|||
new_b2 = gen%b2 |
|||
end if |
|||
gen%a12 = new_a12; gen%a1 = new_a1; gen%a2 = new_a2; gen%a = new_a |
|||
gen%b12 = new_b12; gen%b1 = new_b1; gen%b2 = new_b2; gen%b = new_b |
|||
gen%iy = gen%iy + 1 |
|||
end subroutine absorb_y_term |
|||
function any_too_big (a, b, c, d, e, f, g, h) result (yes) |
|||
type(big_integer), intent(in) :: a, b, c, d, e, f, g, h |
|||
logical :: yes |
|||
if (too_big (a)) then |
|||
yes = .true. |
|||
else if (too_big (b)) then |
|||
yes = .true. |
|||
else if (too_big (c)) then |
|||
yes = .true. |
|||
else if (too_big (d)) then |
|||
yes = .true. |
|||
else if (too_big (e)) then |
|||
yes = .true. |
|||
else if (too_big (f)) then |
|||
yes = .true. |
|||
else if (too_big (g)) then |
|||
yes = .true. |
|||
else if (too_big (h)) then |
|||
yes = .true. |
|||
else |
|||
yes = .false. |
|||
end if |
|||
end function any_too_big |
|||
function too_big (coef) result (yes) |
|||
type(big_integer), intent(in) :: coef |
|||
logical :: yes |
|||
if (.not. allocated (ng8_coefficient_threshold)) then |
|||
! 2**512 |
|||
ng8_coefficient_threshold = string2big ('1340780792994259709957& |
|||
&402499820584612747936582059239337772356144372176403007354& |
|||
&697680187429816690342769003185818648605085375388281194656& |
|||
&9946433649006084096') |
|||
end if |
|||
yes = (big_cmpabs (coef, ng8_coefficient_threshold) >= 0) |
|||
end function too_big |
|||
function treat_as_infinite (term) result (yes) |
|||
type(big_integer), intent(in) :: term |
|||
logical :: yes |
|||
if (.not. allocated (ng8_term_threshold)) then |
|||
! 2**64 |
|||
ng8_term_threshold = string2big ('18446744073709551616') |
|||
end if |
|||
yes = (big_cmpabs (term, ng8_term_threshold) >= 0) |
|||
end function treat_as_infinite |
|||
end subroutine ng8_term_generator_generate |
|||
function add_continued_fractions (x, y) result (cf) |
|||
class(continued_fraction), intent(in) :: x, y |
|||
type(continued_fraction) :: cf |
|||
cf = apply_ng8 (0, 1, 1, 0, 0, 0, 0, 1, x, y) |
|||
end function add_continued_fractions |
|||
function subtract_continued_fractions (x, y) result (cf) |
|||
class(continued_fraction), intent(in) :: x, y |
|||
type(continued_fraction) :: cf |
|||
cf = apply_ng8 (0, 1, -1, 0, 0, 0, 0, 1, x, y) |
|||
end function subtract_continued_fractions |
|||
function multiply_continued_fractions (x, y) result (cf) |
|||
class(continued_fraction), intent(in) :: x, y |
|||
type(continued_fraction) :: cf |
|||
cf = apply_ng8 (1, 0, 0, 0, 0, 0, 0, 1, x, y) |
|||
end function multiply_continued_fractions |
|||
function divide_continued_fractions (x, y) result (cf) |
|||
class(continued_fraction), intent(in) :: x, y |
|||
type(continued_fraction) :: cf |
|||
cf = apply_ng8 (0, 1, 0, 0, 0, 0, 1, 0, x, y) |
|||
end function divide_continued_fractions |
|||
subroutine get_continued_fraction_term (cf, i, term_exists, term) |
|||
class(continued_fraction), intent(in) :: cf |
|||
integer, intent(in) :: i |
|||
logical, intent(out) :: term_exists |
|||
type(big_integer), allocatable, intent(out) :: term |
|||
call get_continued_fraction_record_term (cf%p, i, term_exists, term) |
|||
end subroutine get_continued_fraction_term |
|||
subroutine get_continued_fraction_record_term (cfrec, i, term_exists, term) |
|||
class(continued_fraction_record), intent(inout) :: cfrec |
|||
integer, intent(in) :: i |
|||
logical, intent(out) :: term_exists |
|||
type(big_integer), allocatable, intent(out) :: term |
|||
if (i < 0) error stop |
|||
call update (i + 1) |
|||
term_exists = (i < cfrec%m) |
|||
if (term_exists) term = cfrec%memo(i) |
|||
contains |
|||
subroutine update (needed) |
|||
integer :: needed |
|||
logical :: term_exists1 |
|||
type(big_integer), allocatable :: term1 |
|||
if (.not. cfrec%terminated .and. cfrec%m < needed) then |
|||
if (size (cfrec%memo) < needed) then |
|||
block ! Allocate more storage. |
|||
type(big_integer), allocatable :: memo1(:) |
|||
allocate (memo1(0 : (2 * needed) - 1)) |
|||
memo1(0:cfrec%m - 1) = cfrec%memo(0:cfrec%m - 1) |
|||
call move_alloc (memo1, cfrec%memo) |
|||
end block |
|||
end if |
|||
do while (.not. cfrec%terminated .and. cfrec%m < needed) |
|||
call cfrec%gen%generate (term_exists1, term1) |
|||
if (term_exists1) then |
|||
cfrec%memo(cfrec%m) = term1 |
|||
cfrec%m = cfrec%m + 1 |
|||
else |
|||
cfrec%terminated = .true. |
|||
end if |
|||
end do |
|||
end if |
|||
end subroutine update |
|||
end subroutine get_continued_fraction_record_term |
|||
function continued_fraction_term_exists (cf, i) result (term_exists) |
|||
class(continued_fraction), intent(in) :: cf |
|||
integer, intent(in) :: i |
|||
logical :: term_exists |
|||
term_exists = continued_fraction_record_term_exists (cf%p, i) |
|||
end function continued_fraction_term_exists |
|||
function continued_fraction_record_term_exists (cfrec, i) result (term_exists) |
|||
class(continued_fraction_record), intent(inout) :: cfrec |
|||
integer, intent(in) :: i |
|||
logical :: term_exists |
|||
type(big_integer), allocatable :: term |
|||
call get_continued_fraction_record_term (cfrec, i, term_exists, term) |
|||
end function continued_fraction_record_term_exists |
|||
function continued_fraction_term (cf, i) result (term) |
|||
class(continued_fraction), intent(in) :: cf |
|||
integer, intent(in) :: i |
|||
type(big_integer), allocatable :: term |
|||
term = continued_fraction_record_term (cf%p, i) |
|||
end function continued_fraction_term |
|||
function continued_fraction_record_term (cfrec, i) result (term) |
|||
class(continued_fraction_record), intent(inout) :: cfrec |
|||
integer, intent(in) :: i |
|||
type(big_integer), allocatable :: term |
|||
logical :: term_exists |
|||
call get_continued_fraction_record_term (cfrec, i, term_exists, term) |
|||
if (.not. term_exists) error stop |
|||
end function continued_fraction_record_term |
|||
function continued_fraction_to_string_given_max_terms (cf, max_terms) result (s) |
|||
class(continued_fraction), intent(in) :: cf |
|||
integer, intent(in) :: max_terms |
|||
character(len = :), allocatable :: s |
|||
s = continued_fraction_record_to_string_given_max_terms (cf%p, max_terms) |
|||
end function continued_fraction_to_string_given_max_terms |
|||
function continued_fraction_record_to_string_given_max_terms (cfrec, max_terms) result (s) |
|||
class(continued_fraction_record), intent(inout) :: cfrec |
|||
integer, intent(in) :: max_terms |
|||
character(len = :), allocatable :: s |
|||
integer :: i |
|||
logical :: done |
|||
i = 0 |
|||
s = '[' |
|||
done = .false. |
|||
do while (.not. done) |
|||
if (.not. cfrec%term_exists(i)) then |
|||
s = s // "]" |
|||
done = .true. |
|||
else if (i == max_terms) then |
|||
s = s // ",...]" |
|||
done = .true. |
|||
else |
|||
select case (i) |
|||
case (0); continue |
|||
case (1); s = s // ";" |
|||
case default; s = s // "," |
|||
end select |
|||
s = s // big2string (cfrec%term(i)) |
|||
i = i + 1 |
|||
end if |
|||
end do |
|||
end function continued_fraction_record_to_string_given_max_terms |
|||
function continued_fraction_to_string_with_default_max_terms (cf) result (s) |
|||
class(continued_fraction), intent(in) :: cf |
|||
character(len = :), allocatable :: s |
|||
s = continued_fraction_record_to_string_with_default_max_terms (cf%p) |
|||
end function continued_fraction_to_string_with_default_max_terms |
|||
function continued_fraction_record_to_string_with_default_max_terms (cfrec) result (s) |
|||
class(continued_fraction_record), intent(inout) :: cfrec |
|||
character(len = :), allocatable :: s |
|||
integer :: max_terms |
|||
max_terms = max (default_continued_fraction_max_terms, 1) |
|||
s = continued_fraction_record_to_string_given_max_terms (cfrec, max_terms) |
|||
end function continued_fraction_record_to_string_with_default_max_terms |
|||
end module continued_fractions |
|||
!--------------------------------------------------------------------- |
|||
program bivariate_continued_fraction_task |
|||
use, non_intrinsic :: big_integers |
|||
use, non_intrinsic :: continued_fractions |
|||
implicit none |
|||
type(continued_fraction) :: golden_ratio |
|||
type(continued_fraction) :: silver_ratio |
|||
type(continued_fraction) :: sqrt2 |
|||
type(continued_fraction) :: one |
|||
type(continued_fraction) :: two |
|||
type(continued_fraction) :: three |
|||
type(continued_fraction) :: four |
|||
type(continued_fraction) :: method1 |
|||
type(continued_fraction) :: method2 |
|||
type(continued_fraction) :: method3 |
|||
golden_ratio = constant_term_cf (1) |
|||
silver_ratio = constant_term_cf (2) |
|||
one = i2cf (1) |
|||
two = i2cf (2) |
|||
three = i2cf (3) |
|||
four = i2cf (4) |
|||
sqrt2 = silver_ratio - one |
|||
method1 = apply_ng8 (0, 1, 0, 0, 0, 0, 2, 0, silver_ratio, sqrt2) |
|||
method2 = apply_ng8 (1, 0, 0, 1, 0, 0, 0, 8, silver_ratio, silver_ratio) |
|||
method3 = (one / two) * (one + (one / sqrt2)) |
|||
call show ("golden ratio", golden_ratio, "(1 + sqrt(5))/2") |
|||
call show ("silver ratio", silver_ratio, "(1 + sqrt(2))") |
|||
call show ("sqrt(2)", sqrt2, "silver ratio minus 1") |
|||
call show ("13/11", i2cf (13) / i2cf (11), "") |
|||
call show ("22/7", i2cf (22) / i2cf (7), "") |
|||
call show ("1", one, "") |
|||
call show ("2", two, "") |
|||
call show ("3", three, "") |
|||
call show ("4", four, "") |
|||
call show ("(1 + 1/sqrt(2))/2", method1, "method 1") |
|||
call show ("(1 + 1/sqrt(2))/2", method2, "method 2") |
|||
call show ("(1 + 1/sqrt(2))/2", method3, "method 3") |
|||
call show ("sqrt(2) + sqrt(2)", sqrt2 + sqrt2, "") |
|||
call show ("sqrt(2) - sqrt(2)", sqrt2 - sqrt2, "") |
|||
call show ("sqrt(2) * sqrt(2)", sqrt2 * sqrt2, "") |
|||
call show ("sqrt(2) / sqrt(2)", sqrt2 / sqrt2, "") |
|||
contains |
|||
subroutine show (expression, cf, note) |
|||
character(len = *), intent(in) :: expression |
|||
class(continued_fraction), intent(in) :: cf |
|||
character(len = *), intent(in) :: note |
|||
write (*, '(A19, " => ", A, T73, A)') & |
|||
& expression, cf%to_string(), note |
|||
end subroutine show |
|||
end program bivariate_continued_fraction_task |
|||
!--------------------------------------------------------------------- |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>$ gfortran -O2 -g -fbounds-check -Wall -Wextra bivariate_continued_fraction_task.f90 && ./a.out |
|||
golden ratio => [1;1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,...] (1 + sqrt(5))/2 |
|||
silver ratio => [2;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...] (1 + sqrt(2)) |
|||
sqrt(2) => [1;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...] silver ratio minus 1 |
|||
13/11 => [1;5,2] |
|||
22/7 => [3;7] |
|||
1 => [1] |
|||
2 => [2] |
|||
3 => [3] |
|||
4 => [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,...] method 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,...] method 2 |
|||
(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,...] method 3 |
|||
sqrt(2) + sqrt(2) => [2;1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,...] |
|||
sqrt(2) - sqrt(2) => [0] |
|||
sqrt(2) * sqrt(2) => [2] |
|||
sqrt(2) / sqrt(2) => [1] |
|||
</pre> |
</pre> |
||