Package: continuedfractions
continuedfractionsA simple extension of the Pythonfractionsstandard library for working withcontinued fractionsas Python objects.[^1]Prelude$$
\pi = 3 + \frac{1}{7 + \frac{1}{15 + \frac{1}{1 + \frac{1}{292 + \ddots}}}}
$$>>>importdecimal,math;fromcontinuedfractions.continuedfractionimportContinuedFraction>>>cf=ContinuedFraction(math.pi)>>>cfContinuedFraction(884279719003555,281474976710656)>>>cf.elements(3,7,15,1,292,1,1,1,2,1,3,1,14,4,2,3,1,12,5,1,5,20,1,11,1,1,1,2)>>>cf.as_float()3.141592653589793>>>cf.convergentsmappingproxy({0:Fraction(3,1),1:Fraction(22,7),2:Fraction(333,106),3:Fraction(355,113),4:Fraction(103993,33102),5:Fraction(104348,33215),6:Fraction(208341,66317),7:Fraction(312689,99532),8:Fraction(833719,265381),9:Fraction(1146408,364913),10:Fraction(4272943,1360120),11:Fraction(5419351,1725033),12:Fraction(80143857,25510582),13:Fraction(325994779,103767361),14:Fraction(732133415,233045304),15:Fraction(2522395024,802903273),16:Fraction(3254528439,1035948577),17:Fraction(41576736292,13234286197),18:Fraction(211138209899,67207379562),19:Fraction(252714946191,80441665759),20:Fraction(1474712940854,469415708357),21:Fraction(29746973763271,9468755832899),22:Fraction(31221686704125,9938171541256),23:Fraction(373185527508646,118788642786715),24:Fraction(404407214212771,128726814327971),25:Fraction(777592741721417,247515457114686),26:Fraction(1181999955934188,376242271442657),27:Fraction(3141592653589793,1000000000000000)})>>>cf.order27>>>pi_approx=ContinuedFraction.from_elements(3,7,15,1)>>>pi_approxContinuedFraction(355,113)>>>pi_approx.as_float()3.1415929203539825>>>math.pi-pi_approx.as_float()-2.667641894049666e-07>>>importpytest>>>pytest.approx(pi_approx.as_float(),rel=1e-12)==math.piTrueInstallation & DependenciesThe package uses only Python standard libraries, and is supported on Python versions3.10-3.12. It is CI-tested on Ubuntu Linux (22.04.3 LTS), Mac OS (12.7.3) and Windows (Windows Server 2022), but should also install on any other platform supporting these Python versions.The simplest way of installing it is a standardpip/pip3install:pipinstallcontinuedfractionsFor contributors there are development requirements which are specified in theproject TOML- contribution guidelines are described in more detail later.Working with Continued FractionsContinued fractionsare beautiful and interesting mathematical objects, with many connections innumber theoryand also very useful practical applications, including therational approximation of real numbers.Thecontinuedfractionspackage is designed to:make it easy to construct (finite) continued fractions as Python objectsexplore their key properties, such as elements/coefficients, convergents, segments, remainders, and othersoperate on them as rationals and instances of the standard libraryfractions.FractionclassPackage StructureThecontinuedfractionspackage consists of two core libraries:continuedfractions.lib- this contains the core functionality of (1) generating continued fraction representations (as ordered element sequences) of any valid Python number, given as an integer, non-nanfloat, valid numeric string, afractions.Fractionordecimal.Decimalobject, or as a pair of integers and/orfractions.Fractionobjects; and conversely (2) reconstructing rational fractions from continued fraction representations (again, given as ordered element sequences).continuedfractions.continuedfraction- this contains the mainContinuedFractionclass, which subclassesfractions.Fraction. TheContinuedFractionobjects encapsulate a number of key properties, such as the sequences of their elements and convergents, and provide other utility methods.The functions incontinuedfractions.libare standalone and thus useful on their own, but it is easiest to work with objects created from thecontinuedfraction.ContinuedFractionclass.A Simple Introduction with ExamplesThe starting point is thecontinuedfractions.continuedfraction.ContinuedFractionclass. A simple introduction is given below of how it can be used, with a variety of examples.Importing theContinuedFractionClassImport the core class fromcontinuedfractions.continuedfraction.>>>fromcontinuedfractions.continuedfractionimportContinuedFractionCreating Continued Fractions from NumbersWe can take a simple rational number[^2] $\frac{649}{200} = \frac{3 \times 200 + 49}{200} = 3.245$, which has the following finite continued fraction representation:$$
\frac{649}{200} = 3 + \frac{1}{4 + \frac{1}{12 + \frac{1}{4}}}
$$This representation is calledsimplebecause all of the numerators in the fractional terms are equal to $1$, which makes the fractions irreducible. Mathematically, the continued fraction is written as $[3; 4, 12, 4]$.The continued fraction object for $\frac{649}{200}$ can be created as follows.>>>cf=ContinuedFraction(649,200)>>>cfContinuedFraction(649,200)Note: The same object can also be constructed usingContinuedFraction('649/200'),ContinuedFraction('3.245'),ContinuedFraction(Fraction(649, 200)),ContinuedFraction(Fraction(649), 200)),ContinuedFraction(649, Fraction(200))), andContinuedFraction(Decimal('3.245')). But passing a numeric literal such as649/200will result in an evaluation of the decimal integer division usingbinary floating point division, thus producing a fractional approximation, in this case,ContinuedFraction(3653545197704315, 1125899906842624).The float value ofContinuedFraction(649, 200)is available via the.as_float()method, in this case, an exact value of $3.245$.>>>cf.as_float()3.245Note: the.as_float()is unique toContinuedFraction- it is not defined in the superclass,fractions.Fraction.It is known that every finite continued fraction represents a rational number, and conversely that every rational number can be represented as a finite continued fraction. On the other infinite continued fractions represent irrationals, which cannot therefore be represented exactly as binary fractions. Thus,ContinuedFractionobjects for irrational numbers will always have a finite sequence of elements, whose length is determined by the smallest binary fraction that can be represented on the given platform. For example $\sqrt{2}$, which is given by a periodic continued fraction $[1; 2, 2, 2, \ldots]$, we have:>>>sqrt2=ContinuedFraction(math.sqrt(2))>>>sqrt2ContinuedFraction(6369051672525773,4503599627370496)>>>sqrt2.as_float()1.4142135623730951and the fractional part of the float value displayed above is anapproximationbased on the most precise binary fractional representation possible on the system. SoContinuedFraction(x)for irrational numbers $x$ will only be approximate, not exact.Inspecting PropertiesA number of key properties of (finite) continued fractions can be explored usingContinuedFraction, as described below.Elements and OrdersTheelements(or coefficients) of a (possibly infinite) continued fraction $[a_0;a_1,a_2\cdots]$  of a real number $x$ include the leading integer $a_0 = \lfloor x \rfloor$ (largest integer part of $x$), and the whole numbers $a_1,a_2,\cdots$ in the denominators of the fractional terms. ForContinuedFractionobjects the.elementsproperty can be used to look at their elements, e.g. forContinuedFraction(649, 200)we have:>>>cf=ContinuedFraction(649,200)>>>cf.elements(3,4,12,4)Theorderof a continued fraction is defined to be number of its elementsafterthe first. Thus, forContinuedFraction(649, 200)the order is3:>>>cf.order3The elements and order ofContinuedFractionobjects are well behaved with respect to all rational operations supported byfractions.Fraction:>>>ContinuedFraction(415,93).elements(4,2,6,7)>>>ContinuedFraction(649,200)+ContinuedFraction(415,93)ContinuedFraction(143357,18600)>>>(ContinuedFraction(649,200)+ContinuedFraction(415,93)).elements(7,1,2,2,2,1,1,11,1,2,12)>>>(ContinuedFraction(649,200)+ContinuedFraction(415,93)).order10Convergents and Rational ApproximationsFor an integer $k >= 0$ the $k$-thconvergent$C_k$ of a continued fraction $[a_0; a_1,\ldots]$ of a real number $x$ is defined to be the rational number and finite continued fraction represented by $[a_0; a_1,\ldots,a_k]$, formed from the first $k + 1$ elements of the original.$$
C_k = a_0 + \frac{1}{a_1 + \frac{1}{a_2 \ddots \frac{1}{a_{k-1} + \frac{1}{a_k}}}}
$$Each convergent $C_k$ represents a rational approximation $\frac{p_k}{q_k}$ of $x$, and we can define an error term $\epsilon_k = x - C_k = x - \frac{p_k}{q_k}$. If we assume $x > 0$ then the convergents form a strictly increasing sequence of rational numbers converging to $x$ as $n \longrightarrow \infty$. So, formally:$$
\frac{p_0}{q_0} < \frac{p_1}{q_1} < \cdots \frac{p_n}{q_n} < \cdots
$$where$$
\lim_{n \to \infty} \frac{p_n}{q_n} = x
$$This is equivalent to the limit $\lim_{n \to \infty} \epsilon_n = 0$. If $x$ is irrational then the convergents may alternate about $x$, but still converge to it. If and only if $x$ is rational do we have the case that the convergent sequence is finite and terminates in $x$.TheContinuedFractionclass provides a.convergentsproperty for objects, which returns an immutable map (types.MappingProxyType) of all $k$-order convergents, indexed (keyed) by integers $k=0,1,\ldots,n$, where $n$ is the order of the continued fraction.>>>cf.convergentsmappingproxy({0:Fraction(3,1),1:Fraction(13,4),2:Fraction(159,49),3:Fraction(649,200)})>>>cf.convergents[2]Fraction(159,49)>>>importoperator>>># Get the float value of this fraction>>>operator.truediv(*cf.convergents[2].as_integer_ratio())3.2448979591836733Obviously, we can only handle finite continued fractions in Python, so the convergents produced byContinuedFractionwill be finite in number, regardless of whether the real numbers they approximate are rational or irrational. We can verify that $C_0 < C_1 < \cdots < C_n$ forContinuedFraction(649, 200)and alsoContinuedFraction(math.pi):>>>assertcf.convergents[0]<cf.convergents[1]<cf.convergents[2]<cf.convergents[3]==cf# True>>>pi_cf=ContinuedFraction(math.pi)>>>pi_cf.convergentsmappingproxy({0:Fraction(3,1),1:Fraction(22,7),2:Fraction(333,106),3:Fraction(355,113),...,27:Fraction(3141592653589793,1000000000000000)})>>>assertpi_cf.convergents[27]<math.pi# TrueNote: As the convergents are constructed duringContinuedFractionobject initialisation, the objects that represent them cannot be of typeContinuedFraction, due to recursion errors. Thus, it was decided to keep them asfractions.Fractionobjects.Segments and RemaindersConvergents are linked to the concept ofsegments, which are finite subsequences of elements of a given continued fraction. More precisely, we can define the $k$-th segment of a continued fraction represented by $[a_0; a_1,\ldots]$ as the sequence of its first $k + 1$ elements, namely $a_0,a_1,\ldots,a_k$, which uniquely determines the $k$-order convergent of the continued fraction. The segments ofContinuedFractionobjects can be obtained via the.segment()method, which takes a non-negative integer not exceeding the order.>>>cf.segment(0),cf.segment(1),cf.segment(2),cf.segment(3)(ContinuedFraction(3,1),ContinuedFraction(13,4),ContinuedFraction(159,49),ContinuedFraction(649,200))3Note: Unlike the $k$-order convergents the segments areContinuedFractionobjects and uniquely represent them as such.A related concept is that ofremaindersof continued fractions, which are (possibly infinite) subsequences of elements of a given continued fraction, starting a given element. More precisely, we can define the $k$-th remainder of a continued fraction represented by $[a_0; a_1,\ldots]$ as the sequence of elements $a_k,a_{k + 1},\ldots$ starting from the $k$-th element. The remainders ofContinuedFractionobjects can be obtained via the.remainder()method, which takes a non-negative integer not exceeding the order.>>>cf.remainder(0),cf.remainder(1),cf.remainder(2),cf.remainder(3)(ContinuedFraction(649,200),ContinuedFraction(200,49),ContinuedFraction(49,4),ContinuedFraction(4,1))MediantsAnother feature which the package includes ismediants. The (simple) mediant of two rational numbers $\frac{a}{b}$ and $\frac{c}{d}$, where $b, d \neq 0$, is defined as the rational number:$$
\frac{a + c}{b + d}
$$Assuming that $\frac{a}{b} < \frac{c}{d}$ and $bd > 0$ the mediant above has the property that:$$
\frac{a}{b} < \frac{a + c}{b + d} < \frac{c}{d}
$$Mediants can give good rational approximations to real numbers of interest.TheContinuedFractionclass provides a.mediant()method which can be used to calculate mediants with otherContinuedFractionorfractions.Fractionobjects. The result is also aContinuedFractionobject. A few examples are given below of how to calculate mediants.>>>ContinuedFraction('0.5').mediant(Fraction(2,3))ContinuedFraction(3,5)>>>ContinuedFraction('0.6').elements(0,1,1,2)>>>ContinuedFraction(1,2).mediant(ContinuedFraction('2/3'))ContinuedFraction(3,5)>>>assertContinuedFraction(1,2)<ContinuedFraction(1,2).mediant(Fraction(3,4))<ContinuedFraction(3,4)# TrueThe concept of the simple mediant of two fractions of $\frac{a}{b}$ and $\frac{c}{d}$ as given above can be generalised to $k$-th left- and right-mediants, for positive integers $k$: the $k$-th left mediant of $\frac{a}{b}$ and $\frac{c}{d}$ can be defined as:$$
\frac{ka + c}{kb + d}
$$while the $k$-th right mediant can be defined as:$$
\frac{a + nc}{b + nd}
$$For $k = 1$ the left- and right-mediants are identical, but as $k \longrightarrow \infty$ they separate into two distinct, strictly monotonic, sequences converging to opposite limits[^3]: the left-mediants form a strictly decreasing sequence lower-bounded by $\frac{a}{b}$, converging to $\frac{a}{b}$:$$
\frac{a}{b} < \cdots < \frac{3a + c}{3b + d} < \frac{2a + c}{2b + d} < \frac{a + c}{b + d} < \frac{c}{d}
$$$$
\lim_{k \to \infty} \frac{ka + c}{kb + d} = \frac{a}{b}
$$while the right-mediants form a strictly increasing sequence upper-bounded by $\frac{c}{d}$, converging to $\frac{c}{d}$:$$
\frac{a}{b} < \frac{a + c}{b + d} < \frac{a + 2c}{b + 2d} < \frac{a + 3c}{b + 3d} < \cdots < \frac{c}{d}
$$$$
\lim_{k \to \infty} \frac{a + kc}{b + kd} = \frac{c}{d}
$$We can illustrate this using theContinuedFraction.mediantmethod using thediroption to set the "direction" of the mediant - either'left'or'right(default) - and usingkto set the mediant order, which defaults tok=1.# Right mediants>>>c1=ContinuedFraction(1,2)>>>c2=ContinuedFraction(3,5)>>>c1.mediant(c2)ContinuedFraction(4,7)>>>c1.mediant(c2).as_float()0.5714285714285714>>>c1.mediant(c2,k=10)ContinuedFraction(31,52)>>>c1.mediant(c2,k=100)0.599601593625498>>>c1.mediant(c2,k=10**6)ContinuedFraction(3000001,5000002)>>>c1.mediant(c2,k=10**6).as_float()0.599999960000016# Left mediants>>>c1.mediant(c2,dir='left')ContinuedFraction(4,7)>>>c1.mediant(c2,dir='left',k=10)ContinuedFraction(13,25)>>>c1.mediant(c2,dir='left',k=10).as_float()0.52>>>c1.mediant(c2,dir='left',k=100)ContinuedFraction(103,205)>>>c1.mediant(c2,dir='left',k=100).as_float()0.5024390243902439>>>c1.mediant(c2,dir='left',k=10**6)ContinuedFraction(1000003,2000005)>>>c1.mediant(c2,dir='left',k=10**6).as_float()0.500000249999375Mediants have very interesting connections to other areas of number, including sequences and orderings of rational numbers - for example, see theStern-Brocot tree.Constructing Continued Fractions from Element SequencesContinued fractions can also be constructed from element sequences, using theContinuedFraction.from_elements()class method. BecauseContinuedFractionis a subclass offractions.FractionallContinuedFractionobjects are fully operable as rational numbers, including as negative rationals.>>>cf_inverse=ContinuedFraction.from_elements(0,3,4,12,4)>>>cf_inverseContinuedFraction(200,649)>>>cf_inverse.elements(0,3,4,12,4)>>>assertcf_inverse==1/cf# True>>>assertcf*cf_inverse==1# True>>>cf_negative_inverse=ContinuedFraction.from_elements(-1,1,2,4,12,4)>>>cf_negative_inverseContinuedFraction(-200,649)>>>cf_negative_inverse.elements(-1,1,2,4,12,4)>>>assertcf_negative_inverse==-1/cf# True>>>assertcf*cf_negative_inverse==-1>>>assertcf+(-cf)==cf_inverse+cf_negative_inverse==0# TrueContinued Fractions with Negative TermsContinued fractions representations with negative terms are valid, provided we use theEuclidean integer division algorithmto calculate the successive quotients and remainders in each step. For example, $\frac{-415}{93} = \frac{-5 \times 93 + 50}{93}$ has the continued fraction representation $[-5; 1, 1, 6, 7]$:$$
-\frac{415}{93} = -5 + \frac{1}{1 + \frac{1}{1 + \frac{1}{6 + \frac{1}{7}}}}
$$Compare this with $[4; 2, 6, 7]$, which is the continued fraction representation of $\frac{415}{93}$:$$
\frac{415}{93} = 4 + \frac{1}{2 + \frac{1}{6 + \frac{1}{7}}}
$$ContinuedFractionobjects for negative numbers are constructed in the same way as with positive numbers, subject to the validation rules described above. And to avoid zero division problems if a fraction has a negative denominator the minus sign is "transferred" to the numerator. A few examples are given below.>>>ContinuedFraction(415,-93)ContinuedFraction(-415,93)>>>ContinuedFraction(-415,93)ContinuedFraction(-415,93)>>>-ContinuedFraction(415,93)ContinuedFraction(-415,93)>>>ContinuedFraction(-415,93).elements(-5,1,1,6,7)>>>ContinuedFraction(-415,93).convergentsmappingproxy({0:Fraction(-5,1),1:Fraction(-4,1),2:Fraction(-9,2),3:Fraction(-58,13),4:Fraction(-415,93)})>>>ContinuedFraction(-415,93).as_float()-4.462365591397849>>>ContinuedFraction(415,93).as_float()4.462365591397849NoteAs negation of numbers is a unary operation, the minus sign in a "negative"ContinuedFractionobject must be attached to the fraction, before enclosure in parentheses.>>>-ContinuedFraction(415,93).elements...TypeError:badoperandtypeforunary-:'tuple'>>>-(ContinuedFraction(415,93)).elements...TypeError:badoperandtypeforunary-:'tuple'>>>(-ContinuedFraction(415,93)).elements(-5,1,1,6,7)>>>assertContinuedFraction(415,93)+(-ContinuedFraction(415,93))==0# TrueValidationTheContinuedFractionclass validates all inputs during object creation - in the.__new__()class method, not instance initialisation - using the.validate()class method. Inputs that do not meet the following conditions trigger aValueError.a single integer or a non-nan floata single numeric stringa singlefractions.Fractionordecimal.Decimalobjecttwo integers orfractions.Fractionobjects, or a combination of an integer and afractions.Fractionobject, representing the numerator and non-zero denominator of a rational numberA number of examples are given below of validation passes and fails.>>>ContinuedFraction.validate(100)>>>ContinuedFraction.validate(3,-2)>>>ContinuedFraction.validate(1,-2.0)Traceback(mostrecentcalllast):...ValueError:Onlysingleintegers,non-nanfloats,numericstrings,`fractions.Fraction`,or`decimal.Decimal`objects;ortwointegersortwo`fractions.Fraction`objectsorapairwisecombinationofthese,representingthenumeratorandnon-zerodenominator,respectively,ofarationalfraction,arevalid.>>>ContinuedFraction.validate(-.123456789)>>>ContinuedFraction.validate('-.123456789')>>>ContinuedFraction.validate('-649/200')>>>ContinuedFraction.validate(-3/2)>>>ContinuedFraction.validate(-3,0)Traceback(mostrecentcalllast):...ValueError:Onlysingleintegers,non-nanfloats,numericstrings,`fractions.Fraction`,or`decimal.Decimal`objects;ortwointegersortwo`fractions.Fraction`objectsorapairwisecombinationofthese,representingthenumeratorandnon-zerodenominator,respectively,ofarationalfraction,arevalid.>>>ContinuedFraction.validate(Fraction(-415,93))>>>ContinuedFraction.validate(Decimal('12345.6789'))>>>ContinuedFraction.validate(Decimal(12345.6789))>>>ContinuedFraction.validate(Fraction(3,2),2.5)Traceback(mostrecentcalllast):...ValueError:Onlysingleintegers,non-nanfloats,numericstrings,`fractions.Fraction`,or`decimal.Decimal`objects;ortwointegersortwo`fractions.Fraction`objectsorapairwisecombinationofthese,representingthenumeratorandnon-zerodenominator,respectively,ofarationalfraction,arevalid.ContributingContributors and contributions are welcome via pull requests from a fork targeting the parentmainbranch. As this is a new and fairly specialised project a simple Git workflow, using a feature and/or fix branch created off themainbranch of your fork, is recommended.SSH and CloningIf you wish to contribute please first ensure you haveSSH access to GitHub. If you do then this should work:ssh-vTgit@github.comIf not please follow the SSH instructions linked above. This should include ensuring that your SSH config file defines your SSH private key file, and specifies agent forwarding, and public key authentication as the preferred mode.Once you've forked the repository, it is recommended to clone your fork over SSH:gitclonegit+ssh://git@github.com/<forkuser>/continuedfractionsDependencies & PDMAs mentioned earlier, the package has no (production) dependencies, but groups of development requirements are specified in the[tool.pdm.dev-dependencies]section of theproject TOML. Of these only thetestdependencies, includingpytestandpytest-cov, are important.test=["coverage[toml]","pytest","pytest-cov","pytest-xdist",]PDMis used (by myself, currently, the sole maintainer) to manage all dependencies and publish packages to PyPI. It is also used to automate certain tasks, such as running tests, as described in the section.There are norequirements*.txtfiles - however, aPDM lockfilewhich defines metadata for all TOML-defined development dependencies, including the currently empty set of production dependencies, and their sub-dependencies etc., can be used to install the project in editable mode.pdminstall-vFor more information on PDM lockfiles and installing requirements see thePDM documentation.Makefile and TestsTheMakefiledefines three main targets:lintfor Ruff linting,doctestsfor runningdoctestsandunittestsfor running unittests and measuring coverage, usingpytestand thepytest-covplugin:makelint
makedoctests
makeunittestsLinting warnings should be addressed first. The doctests serve as acceptance tests, and should be run first, before the unit tests.Continous Integration and Deployment (CI/CD)The CI/CD pipelines are defined in theCI YML, and pipelines for all branches include a tests stage, consisting of Ruff linting, Python doctests, and unit tests, in that order. This will be amended in the future to ensure that tests are only run on updates to PRs targetingmain, to avoid duplication onmain.Versioning & Package PublishingThe package is currently at version0.10.0(semantic versioningis used), and packages are published manually toPyPIusingtwine. There is currently no CI release pipeline - this will be added later.LicenseThe project islicensedunder theMozilla Public License 2.0.References[1] Baker, Alan. A concise introduction to the theory of numbers. Cambridge: Cambridge Univ. Pr., 2002.[2] Barrow, John D. “Chaos in Numberland: The secret life of continued fractions.” plus.maths.org, 1 June 2000,https://plus.maths.org/content/chaos-numberland-secret-life-continued-fractionsURL.[3] Emory University Math Center. “Continued Fractions.” The Department of Mathematics and Computer Science,https://mathcenter.oxford.emory.edu/site/math125/continuedFractions/. Accessed 19 Feb 2024.[4] Python 3.12.2 Docs. "Floating Point Arithmetic: Issues and Limitations."https://docs.python.org/3/tutorial/floatingpoint.html. Accessed 20 February 2024.[5] Python 3.12.2 Docs. "fractions - Rational numbers."https://docs.python.org/3/library/fractions.html. Accessed 21 February 2024.[6] Python 3.12.2 Docs. "decimal - Decimal fixed point and floating point arithmetic."https://docs.python.org/3/library/decimal.html. Accessed 21 February 2024.[7] Wikipedia. "Stern-Brocot Tree".https://en.wikipedia.org/wiki/Stern%E2%80%93Brocot_tree. Accessed 23 February 2024.[8] Wikipedia. "Continued Fraction".https://en.wikipedia.org/wiki/Continued_fraction. Accessed 19 February 2024.[^1]: Due to the nature ofbinary floating point arithmeticit is not always possible to exactly represent a givenreal number. For the same reason, the continued fraction representations produced by the package will necessarily befinite.[^2]: The definition of "rational number" used here is standard: a ratio $\frac{a}{b}$ of integers $a$ and $b \neq 0$. If $a$ and $b$ have no common denominator then $\frac{a}{b}$ is irreducible and unique.[^3]: This is due to thetheoremthat every bounded monotonic sequence of real numbers converges.