Skip to content

catena.mathlib

mathlib.arithmetic

add_fractions(a, b)

Adds two fractions represented as tuples of (numerator, denominator). Reduces intermediate values by first dividing out gcd(q, s) before multiplying, then simplifying the resulting numerator. It also has the side effect of returning a canonicalized result when the input fractions are in their canonical form, since the denominator since the denominator is guaranteed to be positive.

Parameters:

Name Type Description Default
a IntPair

The first fraction as a tuple (numerator, denominator).

required
b IntPair

The second fraction as a tuple (numerator, denominator).

required

Returns:

Name Type Description
IntPair IntPair

The result of the addition as a simplified fraction.

Source code in catena/mathlib/arithmetic.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def add_fractions(a: IntPair, b: IntPair) -> IntPair:
    """
    Adds two fractions represented as tuples of (numerator, denominator).
    Reduces intermediate values by first dividing out gcd(q, s) before
    multiplying, then simplifying the resulting numerator. It also has the
    side effect of returning a canonicalized result when the input fractions 
    are in their canonical form, since the denominator since the denominator
    is guaranteed to be positive.

    Args:
        a (IntPair): The first fraction as a tuple (numerator, denominator).
        b (IntPair): The second fraction as a tuple (numerator, denominator).

    Returns:
        IntPair: The result of the addition as a simplified fraction.
    """
    return uadd_fractions(*a, *b)

multiply_fractions(a, b)

Multiplies two fractions represented as tuples of (numerator, denominator). Reduces intermediate values by first dividing out gcd(p, s) and gcd(r, q) before multiplying, then simplifying the resulting numerator. It also has the side effect of returning a canonicalized result, since the denominator is guaranteed to be positive.

Parameters:

Name Type Description Default
a IntPair

The first fraction as a tuple (numerator, denominator).

required
b IntPair

The second fraction as a tuple (numerator, denominator).

required

Returns:

Name Type Description
IntPair IntPair

The result of the multiplication as a simplified fraction.

Source code in catena/mathlib/arithmetic.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def multiply_fractions(a: IntPair, b: IntPair) -> IntPair:
    """
    Multiplies two fractions represented as tuples of (numerator, denominator).
    Reduces intermediate values by first dividing out gcd(p, s) and gcd(r, q)
    before multiplying, then simplifying the resulting numerator. It also has
    the side effect of returning a canonicalized result, since the denominator 
    is guaranteed to be positive.

    Args:
        a (IntPair): The first fraction as a tuple (numerator, denominator).
        b (IntPair): The second fraction as a tuple (numerator, denominator).

    Returns:
        IntPair: The result of the multiplication as a simplified fraction.
    """
    return umultiply_fractions(*a, *b)

sandwich_fraction(a, b)

Computes the "sandwich" of two fractions represented as tuples of (numerator, denominator). The sandwich is defined as (ps, qr) for fractions (p/q) and (r/s).

Parameters:

Name Type Description Default
a IntPair

The first fraction as a tuple (numerator, denominator).

required
b IntPair

The second fraction as a tuple (numerator, denominator).

required

Returns:

Name Type Description
IntPair IntPair

The result of the sandwich operation as a fraction.

Source code in catena/mathlib/arithmetic.py
123
124
125
126
127
128
129
130
131
132
133
134
135
def sandwich_fraction(a: IntPair, b: IntPair) -> IntPair:
    """
    Computes the "sandwich" of two fractions represented as tuples of (numerator, denominator).
    The sandwich is defined as (p*s, q*r) for fractions (p/q) and (r/s).

    Args:
        a (IntPair): The first fraction as a tuple (numerator, denominator).
        b (IntPair): The second fraction as a tuple (numerator, denominator).

    Returns:
        IntPair: The result of the sandwich operation as a fraction.
    """
    return usandwich_fraction(*a, *b)

square_fraction(a)

Squares a fraction represented as a tuple of (numerator, denominator). It assumes that the fraction is already in its simplest form, so it simply squares the numerator and denominator without further reduction.

Parameters:

Name Type Description Default
a IntPair

The fraction to be squared as a tuple (numerator, denominator).

required

Returns:

Name Type Description
IntPair IntPair

The result of the squaring as a fraction.

Source code in catena/mathlib/arithmetic.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def square_fraction(a: IntPair) -> IntPair:
    """
    Squares a fraction represented as a tuple of (numerator, denominator).
    It assumes that the fraction is already in its simplest form, so it simply
    squares the numerator and denominator without further reduction.

    Args:
        a (IntPair): The fraction to be squared as a tuple (numerator, denominator).

    Returns:
        IntPair: The result of the squaring as a fraction.
    """
    return usquare_fraction(*a)

uadd_fractions(p, q, r, s)

Adds two fractions represented as tuples of (numerator, denominator). Reduces intermediate values by first dividing out gcd(q, s) before multiplying, then simplifying the resulting numerator. It also has the side effect of returning a canonicalized result when the input fractions are in their canonical form, since the denominator since the denominator is guaranteed to be positive.

Parameters:

Name Type Description Default
p int

The numerator of the first fraction.

required
q int

The denominator of the first fraction.

required
r int

The numerator of the second fraction.

required
s int

The denominator of the second fraction.

required

Returns:

Name Type Description
IntPair IntPair

The result of the addition as a simplified fraction.

Source code in catena/mathlib/arithmetic.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def uadd_fractions(p: int, q: int, r: int, s: int) -> IntPair:
    """
    Adds two fractions represented as tuples of (numerator, denominator).
    Reduces intermediate values by first dividing out gcd(q, s) before
    multiplying, then simplifying the resulting numerator. It also has the
    side effect of returning a canonicalized result when the input fractions 
    are in their canonical form, since the denominator since the denominator
    is guaranteed to be positive.

    Args:
        p (int): The numerator of the first fraction.
        q (int): The denominator of the first fraction.
        r (int): The numerator of the second fraction.
        s (int): The denominator of the second fraction.

    Returns:
        IntPair: The result of the addition as a simplified fraction.
    """
    g1 = gcd(q, s)
    q1, s1 = q // g1, s // g1
    num = p * s1 + r * q1
    g2 = gcd(num, g1)
    return num // g2, q1 * (s // g2)

umultiply_fractions(p, q, r, s)

Multiplies two fractions represented as separate numerator and denominator integers. Reduces intermediate values by first dividing out gcd(p, s) and gcd(r, q) before multiplying, then simplifying the resulting numerator. It also has the side effect of returning a canonicalized result, since the denominator is guaranteed to be positive.

Parameters:

Name Type Description Default
p int

The numerator of the first fraction.

required
q int

The denominator of the first fraction.

required
r int

The numerator of the second fraction.

required
s int

The denominator of the second fraction.

required

Returns:

Name Type Description
IntPair IntPair

The result of the multiplication as a simplified fraction.

Source code in catena/mathlib/arithmetic.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
def umultiply_fractions(p: int, q: int, r: int, s: int) -> IntPair:
    """
    Multiplies two fractions represented as separate numerator and denominator integers.
    Reduces intermediate values by first dividing out gcd(p, s) and gcd(r, q)
    before multiplying, then simplifying the resulting numerator. It also has
    the side effect of returning a canonicalized result, since the denominator 
    is guaranteed to be positive.

    Args:
        p (int): The numerator of the first fraction.
        q (int): The denominator of the first fraction.
        r (int): The numerator of the second fraction.
        s (int): The denominator of the second fraction.

    Returns:
        IntPair: The result of the multiplication as a simplified fraction.
    """
    g1 = gcd(p, s)
    g2 = gcd(r, q)
    return (p // g1) * (r // g2), (q // g2) * (s // g1)

usandwich_fraction(p, q, r, s)

Computes the "sandwich" of two fractions represented as separate numerator and denominator integers. The sandwich is defined as (ps, qr) for fractions (p/q) and (r/s).

Parameters:

Name Type Description Default
p int

The numerator of the first fraction.

required
q int

The denominator of the first fraction.

required
r int

The numerator of the second fraction.

required
s int

The denominator of the second fraction.

required

Returns:

Name Type Description
IntPair IntPair

The result of the sandwich operation as a fraction.

Source code in catena/mathlib/arithmetic.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def usandwich_fraction(p: int, q: int, r: int, s: int) -> IntPair:
    """
    Computes the "sandwich" of two fractions represented as separate numerator and denominator integers.
    The sandwich is defined as (p*s, q*r) for fractions (p/q) and (r/s).

    Args:
        p (int): The numerator of the first fraction.
        q (int): The denominator of the first fraction.
        r (int): The numerator of the second fraction.
        s (int): The denominator of the second fraction.

    Returns:
        IntPair: The result of the sandwich operation as a fraction.
    """
    return umultiply_fractions(p, q, s, r)

usquare_fraction(p, q)

Squares a fraction represented as separate numerator and denominator integers. It assumes that the fraction is already in its simplest form, so it simply squares the numerator and denominator without further reduction.

Parameters:

Name Type Description Default
p int

The numerator of the fraction.

required
q int

The denominator of the fraction.

required

Returns:

Name Type Description
IntPair IntPair

The result of the squaring as a fraction.

Source code in catena/mathlib/arithmetic.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def usquare_fraction(p: int, q: int) -> IntPair:
    """
    Squares a fraction represented as separate numerator and denominator integers.
    It assumes that the fraction is already in its simplest form, so it simply
    squares the numerator and denominator without further reduction.

    Args:
        p (int): The numerator of the fraction.
        q (int): The denominator of the fraction.

    Returns:
        IntPair: The result of the squaring as a fraction.
    """
    return p * p, q * q

mathlib.convert

from_decimal_to_rational(d)

Converts a decimal string to an exact rational number in lowest terms.

The string must represent a finite decimal (e.g. "3.14" or "42").
The resulting fraction is simplified via :func:~catena.mathlib.core.simplify.

Parameters:

Name Type Description Default
d str

A finite decimal string to convert.

required

Returns:

Name Type Description
Rational Rational

A (numerator, denominator) tuple in lowest terms.

Raises:

Type Description
ValueError

If d is not a string or does not represent a finite decimal.

Source code in catena/mathlib/convert.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def from_decimal_to_rational(d: str) -> Rational:
    """
    Converts a decimal string to an exact rational number in lowest terms.

    The string must represent a finite decimal (e.g. ``"3.14"`` or ``"42"``).  
    The resulting fraction is simplified via :func:`~catena.mathlib.core.simplify`.

    Args:
        d (str): A finite decimal string to convert.

    Returns:
        Rational: A ``(numerator, denominator)`` tuple in lowest terms.

    Raises:
        ValueError: If ``d`` is not a string or does not represent a finite decimal.
    """
    if not isinstance(d, str):
        raise ValueError(f"Input must be a string, got {type(d)}")

    if not _is_finite_decimal(d):
        raise ValueError(f"Input must be a finite decimal string, got '{d}'")

    if '.' in d:
        integer_part, fractional_part = d.split('.')
        numerator = int(integer_part + fractional_part)
        denominator = 10 ** len(fractional_part)
        numerator, denominator = simplify(numerator, denominator)

    else:
        numerator = int(d)
        denominator = 1
    return numerator, denominator

from_float_to_rational(f, limit_denominator=None)

Converts a floating-point number to a rational approximation.

Parameters:

Name Type Description Default
f float

The float to convert.

required
limit_denominator int

If provided, the denominator of the resulting fraction is bounded by this value. Defaults to None (exact conversion via fractions.Fraction).

None

Returns:

Name Type Description
Rational Rational

A (numerator, denominator) tuple representing the fraction.

Source code in catena/mathlib/convert.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def from_float_to_rational(f: float, limit_denominator: Optional[int] = None) -> Rational:
    """
    Converts a floating-point number to a rational approximation.

    Args:
        f (float): The float to convert.
        limit_denominator (int, optional): If provided, the denominator of the
            resulting fraction is bounded by this value. Defaults to None
            (exact conversion via ``fractions.Fraction``).

    Returns:
        Rational: A ``(numerator, denominator)`` tuple representing the fraction.
    """
    if limit_denominator is None:
        r = Fraction(f)
    else:
        r = Fraction(f).limit_denominator(limit_denominator)
    return r.numerator, r.denominator

from_float_to_scf(f, limit_denominator=None)

Converts a floating-point number to its simple continued fraction (SCF) representation.

Delegates to :func:from_float_to_rational followed by :func:from_rational_to_scf.

Parameters:

Name Type Description Default
f float

The float to convert.

required
limit_denominator int

If provided, the rational approximation uses a denominator bounded by this value. Defaults to None.

None

Returns:

Type Description
tuple[int, list[int]]

tuple[int, list[int]]: A pair (a0, [a1, a2, ...]) representing the SCF.

Source code in catena/mathlib/convert.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def from_float_to_scf(f: float, limit_denominator: Optional[int] = None) -> tuple[int, list[int]]:
    """
    Converts a floating-point number to its simple continued fraction (SCF) representation.

    Delegates to :func:`from_float_to_rational` followed by :func:`from_rational_to_scf`.

    Args:
        f (float): The float to convert.
        limit_denominator (int, optional): If provided, the rational approximation
            uses a denominator bounded by this value. Defaults to None.

    Returns:
        tuple[int, list[int]]: A pair ``(a0, [a1, a2, ...])`` representing the SCF.
    """
    return from_rational_to_scf(from_float_to_rational(f, limit_denominator))

from_rational_to_scf(r)

Converts a rational number to its simple continued fraction (SCF) representation.

Uses the Euclidean algorithm to compute the sequence of partial quotients.

Parameters:

Name Type Description Default
r Rational

The rational number to convert. Accepted types: - Fraction: a fractions.Fraction instance. - tuple[int, int]: a (numerator, denominator) pair. - int: treated as the fraction r/1.

required

Returns:

Type Description
int

tuple[int, list[int]]: A pair (a0, [a1, a2, ...]) where a0 is

list[int]

the integer part and the list contains the remaining partial quotients.

Raises:

Type Description
ValueError

If r is not one of the accepted types.

Source code in catena/mathlib/convert.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def from_rational_to_scf(r: Rational) -> tuple[int, list[int]]:
    """
    Converts a rational number to its simple continued fraction (SCF) representation.

    Uses the Euclidean algorithm to compute the sequence of partial quotients.

    Args:
        r (Rational): The rational number to convert. Accepted types:
            - ``Fraction``: a ``fractions.Fraction`` instance.
            - ``tuple[int, int]``: a ``(numerator, denominator)`` pair.
            - ``int``: treated as the fraction ``r/1``.

    Returns:
        tuple[int, list[int]]: A pair ``(a0, [a1, a2, ...])`` where ``a0`` is
        the integer part and the list contains the remaining partial quotients.

    Raises:
        ValueError: If ``r`` is not one of the accepted types.
    """
    if isinstance(r, Fraction):
        p, q = r.numerator, r.denominator
    elif isinstance(r, tuple) and len(r) == 2:
        p, q = r
    elif isinstance(r, int):
        p, q = r, 1
    else:
        raise ValueError(f"Invalid input type: {type(r)}. Expected Fraction, tuple[int, int], or int.")

    if p == 0:
        return 0, []

    if q == 0:
        raise ValueError("Denominator cannot be zero.")

    scf = []
    while abs(p) > 0 and q != 0:
        h, q, p = euclidean_step(p, q)
        scf.append(h)

    return scf[0], scf[1:]

mathlib.core

canonicalize(p, q)

Canonicalizes a fraction by simplifying it and ensuring the denominator is positive.

Parameters:

Name Type Description Default
p int

The numerator.

required
q int

The denominator.

required

Returns:

Name Type Description
IntPair IntPair

A tuple containing the canonicalized numerator and denominator.

Source code in catena/mathlib/core.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def canonicalize(p: int, q: int) -> IntPair:
    """
    Canonicalizes a fraction by simplifying it and ensuring the denominator is positive.

    Args:
        p (int): The numerator.
        q (int): The denominator.

    Returns:
        IntPair: A tuple containing the canonicalized numerator and denominator.
    """
    simplified_p, simplified_q = simplify(p, q)
    if simplified_q < 0:
        return -simplified_p, -simplified_q
    return simplified_p, simplified_q

euclidean_step(p, q)

Breaks down a division operation into quotient, remainder, and divisor.

Parameters:

Name Type Description Default
p int

The dividend.

required
q int

The divisor.

required

Returns:

Name Type Description
IntTriplet IntTriplet

A tuple containing the quotient, remainder, and divisor.

Source code in catena/mathlib/core.py
61
62
63
64
65
66
67
68
69
70
71
72
def euclidean_step(p: int, q: int) -> IntTriplet:
    """
    Breaks down a division operation into quotient, remainder, and divisor.

    Args:
        p (int): The dividend.
        q (int): The divisor.

    Returns:
        IntTriplet: A tuple containing the quotient, remainder, and divisor.
    """
    return p//q, p%q, q

get_sign(n)

Returns the sign of a number.

Parameters:

Name Type Description Default
n int

The number to check.

required

Returns:

Name Type Description
int int

1 if positive, -1 if negative, 0 if zero.

Source code in catena/mathlib/core.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def get_sign(n: int) -> int:
    """
    Returns the sign of a number. 

    Args:
        n (int): The number to check.

    Returns:
        int: 1 if positive, -1 if negative, 0 if zero.
    """
    if n > 0:
        return 1
    elif n < 0:
        return -1
    else:
        return 0

quotent_sign(p, q)

Returns the sign of the quotient of a and b without performing the division.

Parameters:

Name Type Description Default
p int

The numerator.

required
q int

The denominator.

required

Returns:

Name Type Description
int int | None

1 if the quotient is positive, -1 if negative, 0 if zero, None if undefined (division by zero).

Source code in catena/mathlib/core.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def quotent_sign(p: int, q: int) -> int|None:
    """
    Returns the sign of the quotient of a and b without performing the division.

    Args:
        p (int): The numerator.
        q (int): The denominator.

    Returns:
        int: 1 if the quotient is positive, -1 if negative, 0 if zero, None if undefined (division by zero).
    """
    if q == 0:
        return None
    return get_sign(p) * get_sign(q)

simplify(p, q)

Simplifies a fraction by dividing both the numerator and denominator by their greatest common divisor (GCD). Signs are preserved: - If the numerator is negative, the simplified numerator will be negative. - If the denominator is negative, the simplified denominator will be negative. - If both are negative, both will be simplified to negative.

Parameters:

Name Type Description Default
p int

The numerator.

required
q int

The denominator.

required

Returns:

Name Type Description
IntPair IntPair

A tuple containing the simplified numerator and denominator.

Source code in catena/mathlib/core.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def simplify(p: int, q: int) -> IntPair:
    """
    Simplifies a fraction by dividing both the numerator and denominator by their greatest common divisor (GCD).
    Signs are preserved:
        - If the numerator is negative, the simplified numerator will be negative.
        - If the denominator is negative, the simplified denominator will be negative.
        - If both are negative, both will be simplified to negative.

    Args:
        p (int): The numerator.
        q (int): The denominator.

    Returns:
        IntPair: A tuple containing the simplified numerator and denominator.
    """
    d = gcd(p, q)
    return p//d, q//d

mathlib.metric

average_digit_count(arr)

Computes the average number of digits in the product of a sequence of positive integers.

Parameters:

Name Type Description Default
arr Sequence[int]

A sequence of positive integers.

required

Returns:

Name Type Description
float float

The average number of digits in the product of the sequence.

Source code in catena/mathlib/metric.py
45
46
47
48
49
50
51
52
53
54
55
def average_digit_count(arr: Sequence[int]) -> float:
    """
    Computes the average number of digits in the product of a sequence of positive integers.

    Args:
        arr (Sequence[int]): A sequence of positive integers.

    Returns:
        float: The average number of digits in the product of the sequence.
    """
    return product_digit_count(arr) / len(arr)

product_digit_count(arr)

Computes the total number of digits in the product of a sequence of positive integers.

Parameters:

Name Type Description Default
arr Sequence[int]

A sequence of positive integers.

required

Returns:

Name Type Description
int int

The total number of digits in the product of the sequence.

Source code in catena/mathlib/metric.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def product_digit_count(arr: Sequence[int]) -> int:
    """
    Computes the total number of digits in the product of a sequence of positive integers.

    Args:
        arr (Sequence[int]): A sequence of positive integers.

    Returns:
        int: The total number of digits in the product of the sequence.
    """
    tot = 0.0
    for num in arr:
        tot += log10(num)

    return floor(tot + 1.0) #ceil(tot) + tot.is_integer() # Add 1 if tot is an integer, otherwise add 0

product_digit_count_high_precision(arr)

Alternative (and much slower) implementation of product_digit_count using Decimal for high precision.

Parameters:

Name Type Description Default
arr Sequence[int]

A sequence of positive integers.

required

Returns: int: The total number of digits in the product of the sequence.

Source code in catena/mathlib/metric.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def product_digit_count_high_precision(arr: Sequence[int]) -> int:
    """
    Alternative (and much slower) implementation of product_digit_count using Decimal for high precision.

    Args:
        arr (Sequence[int]): A sequence of positive integers.
    Returns:
        int: The total number of digits in the product of the sequence.
    """
    from decimal import Decimal, getcontext
    getcontext().prec = 20

    tot = Decimal(0)
    for num in arr:
        tot += 0#Decimal(num).log10()

    return floor(tot + Decimal(1.0))

mathlib.quadratic

normalize_quadratic_surd(P, Q, D)

Normalizes the quadratic surd (P + √D) / Q by multiplying the parameters by a common factor k such that the resulting denominator is positive and the radicand is non-negative. Preseves signs.

Parameters:

Name Type Description Default
P int

Coefficient of the surd.

required
Q int

Denominator.

required
D int

Radicand.

required

Returns:

Type Description
int

tuple[int, int, int]: (P', Q', D') such that (P' + √D') / Q' is

int

the normalized form of the original surd.

Note

No typechecking is performed on the input. The function assumes that Q is non-zero and D is non-negative.

Source code in catena/mathlib/quadratic.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def normalize_quadratic_surd(P: int, Q: int, D: int) -> tuple[int, int, int]:
    """
    Normalizes the quadratic surd ``(P + √D) / Q`` by multiplying the parameters
    by a common factor ``k`` such that the resulting denominator is positive and
    the radicand is non-negative. Preseves signs.

    Args:
        P (int): Coefficient of the surd.
        Q (int): Denominator.
        D (int): Radicand.

    Returns:
        tuple[int, int, int]: ``(P', Q', D')`` such that ``(P' + √D') / Q'`` is 
        the normalized form of the original surd.

    Note:
        No typechecking is performed on the input.  The function assumes that
        ``Q`` is non-zero and ``D`` is non-negative.
    """
    t = abs(D - P**2) # We most preserve the sign of the input.
    l = lcm(Q, t)
    k = l // t
    return P * k, Q * k, D * k**2

quadratic_roots_from_coefficients(A, B, C)

Returns the two real roots of the quadratic equation A·x² + B·x + C = 0 as :class:~decimal.Decimal values, or None if the discriminant is negative. x₀ is the larger root and x₁ is the smaller root, so that x₀ ≥ x₁.

Parameters:

Name Type Description Default
A int

Leading coefficient.

required
B int

Linear coefficient.

required
C int

Constant term.

required

Returns:

Type Description
Optional[Tuple[Decimal, Decimal]]

tuple[Decimal, Decimal] | None: (x₀, x₁) where x₀ ≥ x₁,

Optional[Tuple[Decimal, Decimal]]

or None if B² - 4AC < 0.

Source code in catena/mathlib/quadratic.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def quadratic_roots_from_coefficients(A: int, B: int, C: int) -> Optional[Tuple[Decimal, Decimal]]:
    """
    Returns the two real roots of the quadratic equation ``A·x² + B·x + C = 0``
    as :class:`~decimal.Decimal` values, or ``None`` if the discriminant is
    negative. x₀ is the larger root and x₁ is the smaller root, so that x₀ ≥ x₁.

    Args:
        A (int): Leading coefficient.
        B (int): Linear coefficient.
        C (int): Constant term.

    Returns:
        tuple[Decimal, Decimal] | None: ``(x₀, x₁)`` where ``x₀ ≥ x₁``,
        or ``None`` if ``B² - 4AC < 0``.
    """
    sign = get_sign(A)
    Av, Bv = Decimal(A * sign), Decimal(B * sign)
    discriminant = B**2 - 4 * A * C
    if discriminant < 0:
        return None  # No real roots
    sqrt_disc = Decimal(discriminant).sqrt()
    x0 = (-Bv + sqrt_disc) / (2 * Av)
    x1 = (-Bv - sqrt_disc) / (2 * Av)
    return x0, x1

quadratic_surd_from_coefficients(A, B, C)

Converts the quadratic equation A·x² + B·x + C = 0 into the canonical surd form (P + √D) / Q by reducing coefficients by their GCD and normalising the sign so that Q > 0.

Parameters:

Name Type Description Default
A int

Leading coefficient.

required
B int

Linear coefficient.

required
C int

Constant term.

required

Returns:

Type Description
Optional[Tuple[int, int, int]]

tuple[int, int, int] | None: (P, Q, D) such that the larger root

Optional[Tuple[int, int, int]]

equals (P + √D) / Q, or None if B² - 4AC < 0.

Source code in catena/mathlib/quadratic.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def quadratic_surd_from_coefficients(A: int, B: int, C: int) -> Optional[Tuple[int, int, int]]:
    """
    Converts the quadratic equation ``A·x² + B·x + C = 0`` into the canonical
    surd form ``(P + √D) / Q`` by reducing coefficients by their GCD and
    normalising the sign so that ``Q > 0``.

    Args:
        A (int): Leading coefficient.
        B (int): Linear coefficient.
        C (int): Constant term.

    Returns:
        tuple[int, int, int] | None: ``(P, Q, D)`` such that the larger root
        equals ``(P + √D) / Q``, or ``None`` if ``B² - 4AC < 0``.
    """
    g = gcd(A, B, C)
    A //= g
    B //= g
    C //= g
    discriminant = B**2 - 4 * A * C

    sign = get_sign(A)

    P = -B * sign
    Q = 2 * A * sign
    D = discriminant
    if D < 0:
        return None  # No real roots

    return simplify_quadratic_surd(P, Q, D)

simplify_quadratic_surd(P, Q, D)

Simplifies the quadratic surd (P + √D) / Q by dividing the parameters by their GCD g such that g | P, Q and g² | D. Preseves signs.

Parameters:

Name Type Description Default
P int

Coefficient of the surd.

required
Q int

Denominator.

required
D int

Radicand.

required

Returns:

Type Description
int

tuple[int, int, int]: (P', Q', D') such that (P' + √D') / Q'

int

is the simplified form of the original surd.

Source code in catena/mathlib/quadratic.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def simplify_quadratic_surd(P: int, Q: int, D: int) -> Tuple[int, int, int]:
    """
    Simplifies the quadratic surd ``(P + √D) / Q`` by dividing the parameters
    by their GCD ``g`` such that ``g | P, Q`` and ``g² | D``. Preseves signs.

    Args:
        P (int): Coefficient of the surd.
        Q (int): Denominator.
        D (int): Radicand.

    Returns:
        tuple[int, int, int]: ``(P', Q', D')`` such that ``(P' + √D') / Q'``
        is the simplified form of the original surd.
    """
    g = gcd(P, Q)
    g = gcd(g, _square_part_sqrt(D))
    return P // g, Q // g, D // (g * g)