Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ keywords:
- sequences

license: MPL-2.0
version: 1.9.1
date-released: 2025-10-28
version: 1.10.0
date-released: 2025-11-12
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,4 @@ and the [CodeQL Analysis YML](https://github.com/sr-murthy/continuedfractions/bl

## Versioning and Releases `upload`

The latest release is `1.9.1`, and releases are created, tagged and published manually, not via a workflow.
The latest release is `1.10.0`, and releases are created, tagged and published manually, not via a workflow.
368 changes: 184 additions & 184 deletions docs/requirements.txt

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions docs/sources/continued-fractions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ The **order** of a continued fraction is defined to be number of its tail coeffi
.. code:: python

>>> cf.order
3
1

All :py:class:`~continuedfractions.continuedfraction.ContinuedFraction` instances will have a finite sequence of coefficients and thus a finite order. The integers represent the special case of zero-order continued fractions.

Expand Down Expand Up @@ -653,7 +653,7 @@ The :py:class:`~continuedfractions.continuedfraction.ContinuedFraction` class pr

>>> dict(ContinuedFraction(649, 200).even_order_convergents)
{0: ContinuedFraction(3, 1), 2: ContinuedFraction(159, 49)}
>>> dict(ContinuedFraction(649, 200).odd_convergents)
>>> dict(ContinuedFraction(649, 200).odd_order_convergents)
{1: ContinuedFraction(13, 4), 3: ContinuedFraction(649, 200)}

As with the :py:attr:`~continuedfractions.continuedfraction.ContinuedFraction.convergents` property the result is a generator of enumerated sequence of :py:class:`~continuedfractions.continuedfraction.ContinuedFraction` instances, where the enumeration is by convergent index.
Expand Down Expand Up @@ -929,7 +929,7 @@ Analogous to :ref:`rational points in the plane <rational-points.homogeneous-coo

In this setting, the (projective) height :math:`H(P)` of :math:`P = \frac{a}{c}` is defined as :math:`\text{max}(|a|, |c|)`, and the logarithmic height of :math:`P` is defined as :math:`\text{log}\left(H(P)\right) = \text{log}\left(\text{max}(|a|, |c|)\right)`.

These are implemented by the :py:attr:`~continuedfractions.continuedfraction.ContinuedFraction.height` and :py:attr:`~continuedfractions.continuedfraction.ContinuedFraction.log_height` properties.
These are implemented by the :py:attr:`~continuedfractions.continuedfraction.ContinuedFraction.height` and :py:attr:`~continuedfractions.continuedfraction.ContinuedFraction.log_height` properties respectively.

Some examples are given below.

Expand All @@ -939,10 +939,12 @@ Some examples are given below.
1
>>> ContinuedFraction(2, 3).height
3
>>> ContinuedFraction(3, -2).height
3
>>> ContinuedFraction(0, 1).log_height
Decimal('0')
>>> ContinuedFraction(2, 3).log_height
Decimal('1.0986122886681097821082175869378261268138885498046875')
>>> ContinuedFraction(3, -2).log_height
>>> 3
Decimal('1.0986122886681097821082175869378261268138885498046875')
>>> ContinuedFraction(3, -2).log_height
Decimal('1.0986122886681097821082175869378261268138885498046875')

Expand Down
2 changes: 1 addition & 1 deletion docs/sources/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,4 @@ The main CI workflows are defined in the `CI YML <https://github.com/sr-murthy/c
Versioning and Releases :fas:`upload`
=====================================

The latest release is ``1.9.1``, and releases are created, tagged and published manually, not via a workflow.
The latest release is ``1.10.0``, and releases are created, tagged and published manually, not via a workflow.
33 changes: 13 additions & 20 deletions docs/sources/rational-points.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,15 @@ Some examples are given below:
.. code:: python

>>> from fractions import Fraction as F
>>> from continuedfractions import ContinuedFraction as CF
>>> from continuedfractions.continuedfraction import ContinuedFraction as CF
>>> from continuedfractions.rational_points import RationalPoint as RP
>>> O = RP(0, 0)
>>> O
>>> O = RP(0, 0); O
RationalPoint(0, 0)
>>> I = RP(1, 1)
>>> I
>>> I = RP(1, 1); I
RationalPoint(1, 1)
>>> P = RP(F(3, 5), F(-4, 5))
>>> P
>>> P = RP(F(3, 5), F(-4, 5)); P
RationalPoint(3/5, -4/5)
>>> Q = RP(1, CF(2, 3))
>>> Q
>>> Q = RP(1, CF(2, 3)); Q
RationalPoint(1, 2/3)

It is not possible to create objects with non-rational values, or with fewer or more than 2 values:
Expand All @@ -47,10 +43,10 @@ It is not possible to create objects with non-rational values, or with fewer or

>>> RP(1)
...
ValueError: A `RationalPoint` object must be specified as a pair of rational numbers `r` and `s`, each of type either integer (`int`), or fraction (`Fraction` or `ContinuedFraction`).
TypeError: RationalPoint.__new__() missing 1 required positional argument: 'y'
>>> RP(1, 2, 3)
...
ValueError: A `RationalPoint` object must be specified as a pair of rational numbers `r` and `s`, each of type either integer (`int`), or fraction (`Fraction` or `ContinuedFraction`).
TypeError: RationalPoint.__new__() takes 3 positional arguments but 4 were given
>>> RP(F(1, 3), .5)
...
ValueError: A `RationalPoint` object must be specified as a pair of rational numbers `r` and `s`, each of type either integer (`int`), or fraction (`Fraction` or `ContinuedFraction`).
Expand All @@ -76,14 +72,11 @@ Internally, the rational components of a :py:class:`~continuedfractions.rational
ContinuedFraction(3, 5)
>>> P.y
ContinuedFraction(4, 5)
>>> P.coordinates
Dim2RationalCoordinates(3/5, 4/5)

The :py:attr:`~continuedfractions.rational_points.RationalPoint.coordinates` property returns a :py:class:`~continuedfractions.rational_points.Dim2RationalCoordinates` object, which is a simple custom :py:class:`tuple` type for 2D rational coordinates, which can also be used to access the rational point coordinates:

.. code:: python

>>> P = RP(F(3, 5), F(4, 5))
>>> P.coordinates.x
ContinuedFraction(3, 5)
>>> P.coordinates.y
Expand Down Expand Up @@ -147,7 +140,7 @@ In relation to addition, specifically, there is a :py:meth:`~continuedfractions.

.. code:: python

>>> RP.sum(RP(0, 0), RP(1, F(-1, 2), RP(F(3, 5, F(4, 5)), RP(F(5, 12), 6))))
>>> RP.sum(RP(0, 0), RP(1, F(-1, 2)), RP(F(3, 5), F(4, 5)), RP(F(5, 12), 6))
RationalPoint(121/60, 63/10)

This should be the preferred method as the Python built-in :py:func:`sum` function sets an internal :py:class:`int` start value of ``0``, which causes it to fail on :py:class:`~continuedfractions.rational_points.RationalPoint` instances.
Expand Down Expand Up @@ -185,7 +178,7 @@ The second rational point is optional, but if one is provided it must be a :py:c

.. code:: python

>>> RP(1, 1).gradient(1, 0)
>>> RP(1, 1).gradient(other=RP(1, 0))
...
ValueError: If a second rational point is provided, it must be a `RationalPoint` instance, and non-vertical with respect to this point.

Expand All @@ -209,7 +202,7 @@ The :py:meth:`~continuedfractions.rational_points.RationalPoint.collinear_with_o

.. code:: python

>>> RP(1, 1).collinear_with_origin(RP(2, 2), RP(F(-1, 2), F(-1, 2)), RP(1000, 1000)))
>>> RP(1, 1).collinear_with_origin(RP(2, 2), RP(F(-1, 2), F(-1, 2)), RP(1000, 1000))
True
>>> RP(1, 2).collinear_with_origin(RP(2, 4), RP(F(-1, 2), -1), RP(1000, -1000))
False
Expand Down Expand Up @@ -240,9 +233,9 @@ Below are examples of the second kind (angles between position vectors of two ra
.. code:: python

# Radian angles between the position vectors of two rational points
>>> RP(1, 1).angle(other=RP(0, 1)).angle()
>>> RP(1, 1).angle(other=RP(0, 1))
Decimal('0.78539816339744827899949086713604629039764404296875')
>>> RP(1, 1).angle(other=RP(-1, 1)).angle()
>>> RP(1, 1).angle(other=RP(-1, 1))
Decimal('1.5707963267948965579989817342720925807952880859375')

where the implementation is based on :py:func:`math.acos` and uses the standard formula :math:`\text{cos }\alpha = \frac{P \cdot P'}{\|P\\|\|P'\|}` for the angle :math:`\alpha` between the position vectors of two plane points :math:`P, P'`.
Expand Down Expand Up @@ -339,7 +332,7 @@ Translation in coordinates is available via :py:meth:`~continuedfractions.ration
.. code:: python

>>> RP(F(-1, 2), F(3, 4)).translate(x_by=F(-1, 4), y_by=F(1, 2))
RationalPoint(-3/4, 7/4)
RationalPoint(-3/4, 5/4)
>>> RP(1, 2).translate()
RationalPoint(1, 2)

Expand Down
32 changes: 12 additions & 20 deletions docs/sources/sequences.rst
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ The enumeration can be performed with the :py:func:`~continuedfractions.sequence
>>> rats = rationals(enumeration="rectilinear transposed")
>>> for i, r in enumerate(rats, start=1):
... print(r, end=', ')
... if i == 20:
... if i == 19:
... print(next(rats))
... break
...
Expand Down Expand Up @@ -348,14 +348,12 @@ Here are some examples of constructing left-mediants:
>>> cf1.left_mediant(cf2, k=100)
ContinuedFraction(103, 205)
>>> cf1.left_mediant(cf2, k=100).as_decimal()
Decimal('0.5024390243902439024390243902439024390243902439024390243902439024390243902439024390243902439024390244')
Decimal('0.5024390243902439024390243902')

and right-mediants:
and right-mediants (with the same two fractions `cf1` and `cf2` as above):

.. code:: python

>>> cf1 = ContinuedFraction('1/2')
>>> cf2 = ContinuedFraction(3, 5)
# The default `k = 1` gives you the common, simple mediant of the two rationals
>>> cf1.right_mediant(cf2)
ContinuedFraction(4, 7)
Expand Down Expand Up @@ -391,12 +389,10 @@ thus converging to :math:`\frac{c}{d}`:

\lim_{k \to \infty} \frac{a + kc}{b + kd} = \lim_{k \to \infty} \frac{\frac{a}{k} + c}{\frac{b}{k} + d} = \frac{c}{d}

We can see this with the ``ContinuedFraction(1, 2)`` and ``ContinuedFraction(3, 5)`` instances used in the examples above, starting with the left-mediants:
We can see this using the same two fractions `cf1` and `cf2` as above, starting with the left-mediants:

.. code:: python

>>> cf1 = ContinuedFraction(1, 2)
>>> cf2 = ContinuedFraction(3, 5)
>>> cf1.left_mediant(cf2)
ContinuedFraction(4, 7)
>>> cf1.left_mediant(cf2).as_decimal()
Expand All @@ -410,12 +406,10 @@ We can see this with the ``ContinuedFraction(1, 2)`` and ``ContinuedFraction(3,
>>> cf1.left_mediant(cf2, k=10 ** 6).as_decimal()
Decimal('0.5000002499993750015624960938')

And then the right-mediants:
and then the right-mediants:

.. code:: python

>>> cf1 = ContinuedFraction(1, 2)
>>> cf2 = ContinuedFraction(3, 5)
>>> cf1.right_mediant(cf2).as_decimal()
Decimal('0.5714285714285714285714285714')
>>> cf1.right_mediant(cf2, k=10).as_decimal()
Expand Down Expand Up @@ -449,24 +443,24 @@ The :py:func:`~continuedfractions.sequences.coprime_pairs` function is a simple

.. code:: python

>>> tuple(coprime_integers(5))
>>> tuple(coprime_pairs(5))
(1, 1), (2, 1), (3, 1), (3, 2), (4, 1), (4, 3), (5, 1), (5, 2), (5, 3), (5, 4)

It can be verified that the number of coprime pairs returned by the function here, namely, :math:`10`, is indeed equal to :math:`\phi(1) + \phi(2) + \phi(3) + \phi(4) + \phi(5) = 10`, where :math:`\phi(n)` is Euler's totient function that counts the number of (positive) integers coprime to a given integer :math:`n \geq 1`, and :math:`\phi(1) = 1`. The function that counts the value of :math:`\sum_{k=1}^n \phi(k)` for a given :math:`n` is the summatory totient function :math:`\Phi(n)`, and the number of coprime pairs returned by :py:func:`~continuedfractions.sequences.coprime_pairs` is equal to :math:`\Phi(n)`. Here are a few examples for :math:`n = 1,\ldots,5`:

.. code:: python

>>> sum(1 for _ in coprime_integers(1))
>>> sum(1 for _ in coprime_pairs(1))
1
>>> sum(1 for _ in coprime_integers(2))
>>> sum(1 for _ in coprime_pairs(2))
2
>>> sum(1 for _ in coprime_integers(3))
>>> sum(1 for _ in coprime_pairs(3))
4
>>> sum(1 for _ in coprime_integers(4))
>>> sum(1 for _ in coprime_pairs(4))
6
>>> sum(1 for _ in coprime_integers(5))
>>> sum(1 for _ in coprime_pairs(5))
10
>>> sum(1 for _ in coprime_integers(10))
>>> sum(1 for _ in coprime_pairs(10))
32

.. _sequences.ksrm-trees:
Expand Down Expand Up @@ -551,7 +545,6 @@ The :py:class:`~continuedfractions.sequences.KSRMTree` class contains one main s

.. code:: python

>>> tree = KSRMTree()
>>> list(tree.search(1))
[(1, 1)]
>>> list(tree.search(2))
Expand All @@ -571,7 +564,6 @@ Some examples are given below.

.. code:: python

>>> tree = KSRMTree()
>>> list(tree.search_root(5, (2, 1)))
[(2, 1), (3, 2), (4, 3), (5, 4), (5, 2), (4, 1)]
>>> assert tree.roots[0] == (2, 1)
Expand Down
Loading
Loading