Jump to content

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

Line 2,626:
sqrt(2) * sqrt(2) => [2]
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>
 
1,448

edits

Cookies help us deliver our services. By using our services, you agree to our use of cookies.