# Source code for sympy.ntheory.continued_fraction

from sympy.core.numbers import Integer, Rational

[docs]def continued_fraction_periodic(p, q, d=0):
r"""
Find the periodic continued fraction expansion of a quadratic irrational.

Compute the continued fraction expansion of a rational or a
quadratic irrational number, i.e. \frac{p + \sqrt{d}}{q}, where
p, q and d \ge 0 are integers.

Returns the continued fraction representation (canonical form) as
a list of integers, optionally ending (for quadratic irrationals)
with repeating block as the last term of this list.

Parameters
==========

p : int
the rational part of the number's numerator
q : int
the denominator of the number
d : int, optional
the irrational part (discriminator) of the number's numerator

Examples
========

>>> from sympy.ntheory.continued_fraction import continued_fraction_periodic
>>> continued_fraction_periodic(3, 2, 7)
[2, [1, 4, 1, 1]]

Golden ratio has the simplest continued fraction expansion:

>>> continued_fraction_periodic(1, 2, 5)
[]

If the discriminator is zero or a perfect square then the number will be a
rational number:

>>> continued_fraction_periodic(4, 3, 0)
[1, 3]
>>> continued_fraction_periodic(4, 3, 49)
[3, 1, 2]

========

continued_fraction_iterator, continued_fraction_reduce

References
==========

..  http://en.wikipedia.org/wiki/Periodic_continued_fraction
..  K. Rosen. Elementary Number theory and its applications.
Addison-Wesley, 3 Sub edition, pages 379-381, January 1992.

"""
from sympy.core.compatibility import as_int
from sympy.functions import sqrt

p, q, d = list(map(as_int, [p, q, d]))
sd = sqrt(d)

if q == 0:
raise ValueError("The denominator is zero.")

if d < 0:
raise ValueError("Delta supposed to be a non-negative "
"integer, got %d" % d)
elif d == 0 or sd.is_integer:
# the number is a rational number
return list(continued_fraction_iterator(Rational(p + sd, q)))

if (d - p**2)%q:
d *= q**2
sd *= q
p *= abs(q)
q *= abs(q)

terms = []
pq = {}

while (p, q) not in pq:
pq[(p, q)] = len(terms)
terms.append(int((p + sd)/q))
p = terms[-1]*q - p
q = (d - p**2)/q

i = pq[(p, q)]
return terms[:i] + [terms[i:]]

[docs]def continued_fraction_reduce(cf):
"""
Reduce a continued fraction to a rational or quadratic irrational.

Compute the rational or quadratic irrational number from its
terminating or periodic continued fraction expansion.  The
continued fraction expansion (cf) should be supplied as a
terminating iterator supplying the terms of the expansion.  For
terminating continued fractions, this is equivalent to
list(continued_fraction_convergents(cf))[-1], only a little more
efficient.  If the expansion has a repeating part, a list of the
repeating terms should be returned as the last element from the
iterator.  This is the format returned by
continued_fraction_periodic.

For quadratic irrationals, returns the largest solution found,
which is generally the one sought, if the fraction is in canonical
form (all terms positive except possibly the first).

Examples
========

>>> from sympy.ntheory.continued_fraction import continued_fraction_reduce
>>> continued_fraction_reduce([1, 2, 3, 4, 5])
225/157
>>> continued_fraction_reduce([-2, 1, 9, 7, 1, 2])
-256/233
>>> continued_fraction_reduce([2, 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8]).n(10)
2.718281835
>>> continued_fraction_reduce([1, 4, 2, [3, 1]])
(sqrt(21) + 287)/238
>>> continued_fraction_reduce([])
1/2 + sqrt(5)/2
>>> from sympy.ntheory.continued_fraction import continued_fraction_periodic
>>> continued_fraction_reduce(continued_fraction_periodic(8, 5, 13))
(sqrt(13) + 8)/5

========

continued_fraction_periodic

"""
from sympy.core.symbol import Dummy
from sympy.solvers import solve

period = []
x = Dummy('x')

def untillist(cf):
for nxt in cf:
if isinstance(nxt, list):
period.extend(nxt)
yield x
break
yield nxt

a = Integer(0)
for a in continued_fraction_convergents(untillist(cf)):
pass

if period:
y = Dummy('y')
solns = solve(continued_fraction_reduce(period + [y]) - y, y)
solns.sort()
pure = solns[-1]
else:
return a

[docs]def continued_fraction_iterator(x):
"""
Return continued fraction expansion of x as iterator.

Examples
========

>>> from sympy.core import Rational, pi
>>> from sympy.ntheory.continued_fraction import continued_fraction_iterator

>>> list(continued_fraction_iterator(Rational(3, 8)))
[0, 2, 1, 2]
>>> list(continued_fraction_iterator(Rational(-3, 8)))
[-1, 1, 1, 1, 2]

>>> for i, v in enumerate(continued_fraction_iterator(pi)):
...     if i > 7:
...         break
...     print(v)
3
7
15
1
292
1
1
1

References
==========

..  http://en.wikipedia.org/wiki/Continued_fraction

"""
from sympy.functions import floor

while True:
i = floor(x)
yield i
x -= i
if not x:
break
x = 1/x

[docs]def continued_fraction_convergents(cf):
"""
Return an iterator over the convergents of a continued fraction (cf).

The parameter should be an iterable returning successive
partial quotients of the continued fraction, such as might be
returned by continued_fraction_iterator.  In computing the
convergents, the continued fraction need not be strictly in
canonical form (all integers, all but the first positive).
Rational and negative elements may be present in the expansion.

Examples
========

>>> from sympy.core import Rational, pi
>>> from sympy import S
>>> from sympy.ntheory.continued_fraction import \
continued_fraction_convergents, continued_fraction_iterator

>>> list(continued_fraction_convergents([0, 2, 1, 2]))
[0, 1/2, 1/3, 3/8]

>>> list(continued_fraction_convergents([1, S('1/2'), -7, S('1/4')]))
[1, 3, 19/5, 7]

>>> it = continued_fraction_convergents(continued_fraction_iterator(pi))
>>> for n in range(7):
...     print(next(it))
3
22/7
333/106
355/113
103993/33102
104348/33215
208341/66317