NAME
Sidef::Types::Number::Quadratic - Elements of a quadratic ring Z[t] / (t^2 - q*t - p)
DESCRIPTION
This class implements elements of a quadratic ring of the form a + b*t, where a and b are real numbers and t is a root of the characteristic polynomial t^2 = p + q*t (equivalently, t^2 - q*t - p = 0).
The ring is parameterised by two integers p and q, so each element carries four components: (a, b, p, q). Arithmetic in the ring reduces products of t using the relation t^2 = p + q*t, keeping everything as a degree-1 polynomial in t.
Common special cases include:
Lucas / Fibonacci sequences:
Quadratic(0, 1, 1, 1)gives the elementtwheret^2 = t + 1(the golden-ratio recurrence).Eisenstein integers:
Quadratic(a, b, -1, -1)which representsa + b*wwherew = (-1 + i*sqrt(3))/2.Quadratic integers of the form
a + b*sqrt(w)are represented byQuadratic(a, b, w).Gaussian-like integers: choosing
p = -1, q = 0givest^2 = -1, i.e.t = i.General second-order linear recurrences defined by
U(n) = q*U(n-1) + p*U(n-2).
For more background see: https://en.wikipedia.org/wiki/Quadratic_integer and https://en.wikipedia.org/wiki/Lucas_sequence.
SYNOPSIS
# t satisfies t^2 = t + 1 (golden-ratio recurrence, p=1, q=1)
var t = Quadratic(0, 1, 1, 1)
say t**10 #=> Quadratic(34, 55, 1, 1) (Fibonacci / Lucas numbers)
say t**20 #=> Quadratic(4181, 6765, 1, 1)
# Eisenstein integers
var w = Quadratic(0, 1, -1, -1)
var z = (3 + 4*w)
say z.to_n #=> 1 + 3.46410161513775[...]i
var x = Quadratic(3, 4, 5) # represents: 3 + 4*sqrt(5)
var y = Quadratic(6, 1, 2) # represents: 6 + sqrt(2)
say x**10 #=> (29578174649, 13203129720, 5)
say y**10 #=> (253025888, 176008128, 2)
# Basic arithmetic
var sum = (x + y) # addition
var prod = (x * y) # multiplication
var quot = (x / y) # division
# p=1, q=0 => t^2 = 1
var u = Quadratic(3, 4, 1, 0)
say u.norm #=> norm: -7
say u.conj #=> conjugate: Quadratic(3, -4, 1, 0)
say (u * u.conj) #=> Quadratic(-7, 0, 1, 0)
say u.abs #=> absolute value: sqrt(-7)
# Modular exponentiation
var v = Quadratic(1, 1, 1, 1)
say v.powmod(100, 97)
INHERITS
Inherits methods from:
* Sidef::Types::Number::Number
METHODS
new
Quadratic(a, b, p, q)
Constructs a new element of the quadratic ring representing a + b*t, where t^2 = p + q*t.
Parameters:
a- The constant part (default: 0)b- The coefficient oft(default: 0)p- The constant term of the minimal polynomialt^2 - q*t - p = 0(default: 1)q- The linear term of the minimal polynomial (default: 0)
Returns: A new Quadratic object.
Example:
var e = Quadratic(3, 4, 1, 1) # 3 + 4t, t^2 = 1 + t
var z = Quadratic(0, 1, -1, 0) # t, t^2 = -1 (like i)
Aliases: call
with_value
self.with_value(a)
self.with_value(a, b)
Creates a new element in the same ring (same p and q), with specified values.
Returns: Quadratic (a, b, p, q).
Example:
var t = Quadratic(3, 4, 1, 1)
say t.with_value(7) #=> Quadratic(7, 0, 1, 1)
say t.with_value(7, 5) #=> Quadratic(7, 5, 1, 1)
eval
self.eval(x)
Evaluates the element as a polynomial by substituting x for t. All four components (a, b, p, q) are individually evaluated at x.
Parameters:
x- The value to substitute.
Returns: Quadratic after substitution.
Example:
var e = Quadratic(Poly([9,0,6]), Poly([3,4,5]), 3, 4)
say e.eval(5) # substitutes x with 5 in the Poly objects
lift
self.lift
Lifts the element by applying lift to each of the four components. This is primarily useful after modular arithmetic to recover the representative in a larger ring.
Returns: Quadratic with lifted components.
Example:
var e = Quadratic(Mod(3, 97), Mod(4, 97), 1, 1)
say e.lift # lift residues back
a
self.a
Returns the constant part a of the element a + b*t.
Example:
say Quadratic(3, 4, 5, 6).a #=> 3
Aliases: re, real
b
self.b
Returns the coefficient b of the t part of the element a + b*t.
Example:
say Quadratic(3, 4, 5, 6).b #=> 4
Aliases: im, imag
p
self.p
Returns the parameter p that defines the ring relation t^2 = p + q*t.
Example:
say Quadratic(3, 4, 5, 6).p #=> 5
q
self.q
Returns the parameter q that defines the ring relation t^2 = p + q*t.
Example:
say Quadratic(3, 4, 5, 6).q #=> 6
reals
self.reals
Returns the two coefficients a and b as a two-element list.
Returns: (Number, Number) — the pair (a, b).
Example:
var (a, b) = Quadratic(3, 4, 1, 1).reals # a=3, b=4
parts
self.parts
Returns all four components as an Array.
Returns: Array [a, b, p, q].
Example:
say Quadratic(3, 4, 1, 1).parts #=> [3, 4, 1, 1]
+
a + b
Addition of two Quadratic values in the same ring, or addition of a Quadratic and a scalar.
For elements sharing the same (p, q):
(a + b*t) + (c + d*t) = (a+c) + (b+d)*t
For scalar y: adds y to the constant part only.
Returns: Quadratic — the sum.
Example:
var x = Quadratic(1, 2, 1, 1)
var y = Quadratic(3, 4, 1, 1)
say (x + y) #=> Quadratic(4, 6, 1, 1)
say (x + 10) #=> Quadratic(11, 2, 1, 1)
Aliases: add
-
a - b
Subtraction of two Quadratic values in the same ring, or subtraction of a scalar from a Quadratic.
For elements sharing the same (p, q):
(a + b*t) - (c + d*t) = (a-c) + (b-d)*t
For scalar y: subtracts y from the constant part only.
Returns: Quadratic — the difference.
Example:
var x = Quadratic(5, 6, 1, 1)
var y = Quadratic(1, 2, 1, 1)
say (x - y) #=> Quadratic(4, 4, 1, 1)
Aliases: sub
*
a * b
Multiplication in the ring, using the reduction rule t^2 = p + q*t.
For elements sharing the same (p, q):
(a + b*t)(c + d*t) = (ac + bd*p) + (ad + bc + bd*q)*t
For scalar y: multiplies both components by y.
Returns: Quadratic — the product.
Example:
var x = Quadratic(1, 1, 1, 1) # 1 + t, t^2 = 1 + t
say (x * x) #=> Quadratic(2, 3, 1, 1) (= (1+t)^2 = 2 + 3t)
Aliases: mul
/
a / b
Division of two Quadratic values in the same ring, or division of a Quadratic by a scalar.
For ring elements, computes x * conj(y) / norm(y).
For scalar y: divides both components by y.
Returns: Quadratic — the quotient.
Example:
var x = Quadratic(3, 4, 1, 0)
var y = Quadratic(7, 5, 1, 0)
say (x / y) #=> Quadratic(1/24, 13/24, 1, 0)
Aliases: ÷, div
**
a ** b
Raises the element to an integer power using binary (square-and-multiply) exponentiation.
Negative integer exponents compute the multiplicative inverse first.
Returns: Quadratic (or Number for non-integer exponent).
Example:
var t = Quadratic(0, 1, 1, 1) # t^2 = 1 + t
say t**10 #=> Quadratic(55, 89, 1, 1)
say t**20 #=> Quadratic(6765, 10946, 1, 1)
# Negative exponent
var e = Quadratic(1, 1, 1, 1)
say e**(-3) # = inv(e^3)
Aliases: pow
%
a % b
Modulo operation.
When b is a plain Number, reduces both components a and b component-wise modulo b.
When b is a Quadratic in the same ring, computes the Euclidean remainder via divmod.
Returns: Quadratic — the remainder.
Example:
var a = Quadratic(5, 3, -1, -1)
var b = Quadratic(4, 7, -1, -1)
say (a % b) #=> Quadratic(-2, 0, -1, -1)
var e = Quadratic(13, 27, 1, 1)
say (e % 7) #=> Quadratic(6, 6, 1, 1)
Aliases: mod
++
++a
Increment: adds 1 to the constant part a, leaving b, p, q unchanged.
Returns: Quadratic — incremented value.
Example:
var e = Quadratic(3, 4, 1, 1)
say ++e #=> Quadratic(4, 4, 1, 1)
Aliases: inc
--
--a
Decrement: subtracts 1 from the constant part a, leaving b, p, q unchanged.
Returns: Quadratic — decremented value.
Example:
var e = Quadratic(3, 4, 1, 1)
say --e #=> Quadratic(2, 4, 1, 1)
Aliases: dec
neg
-x
Returns the negation of the element: (-a) + (-b)*t.
Returns: Quadratic
Example:
var e = Quadratic(3, 4, 1, 1)
say -e #=> Quadratic(-3, -4, 1, 1)
sqr
x.sqr
Computes the square of the element efficiently (equivalent to x * x).
Returns: Quadratic — the square.
Example:
var e = Quadratic(1, 1, 1, 1)
say e.sqr #=> Quadratic(2, 3, 1, 1)
abs
x.abs
Computes the absolute value (magnitude) defined as sqrt(norm(x)).
Returns: Number — the absolute value (may be complex if the norm is negative).
Example:
var e = Quadratic(3, 4, 1, 0) # t^2 = 1
say e.abs # sqrt(norm(e)) = sqrt(3^2 - 4^2) = sqrt(-7)
float
x.float
Converts both the a and b components to floating-point numbers, preserving p and q.
Returns: Quadratic with floating-point components.
Example:
var e = Quadratic(1/3, 1/7, 1, 1)
say e.float # approximate floating-point components
floor
x.floor
Applies the floor function to both a and b independently.
Returns: Quadratic with floored components.
Example:
var e = Quadratic(3.7, 4.2, 1, 1)
say e.floor #=> Quadratic(3, 4, 1, 1)
ceil
x.ceil
Applies the ceiling function to both a and b independently.
Returns: Quadratic with ceiling-applied components.
Example:
var e = Quadratic(3.1, 4.9, 1, 1)
say e.ceil #=> Quadratic(4, 5, 1, 1)
round
x.round(r)
Rounds both a and b to r decimal places.
Parameters:
r- number of decimal places (optional).
Returns: Quadratic with rounded components.
Example:
var e = Quadratic(3.567, 4.321, 1, 1)
say e.round(2) #=> Quadratic(3.57, 4.32, 1, 1)
divmod
self.divmod(other)
Euclidean division with rounding to the nearest element.
Computes the quotient q and remainder r such that self = q * other + r and the norm of r is minimised.
Returns: (Quadratic, Quadratic) — the pair (quotient, remainder).
Example:
var x = Quadratic(7, 5, 1, 1)
var y = Quadratic(2, 1, 1, 1)
var (q, r) = x.divmod(y)
say q # rounded quotient
say r # remainder: x - q*y
idiv
self.idiv(other)
Integer (Euclidean) quotient: returns only the quotient part of divmod.
Returns: Quadratic — the integer quotient.
Example:
var x = Quadratic(7, 5, 1, 1)
var y = Quadratic(2, 1, 1, 1)
say x.idiv(y) # rounded-to-nearest quotient
<<
a << n
Left bit-shift: multiplies the element by 2^n.
Returns: Quadratic — result after left shift.
Example:
var e = Quadratic(1, 1, 1, 1)
say (e << 3) #=> Quadratic(8, 8, 1, 1) (= e * 8)
Aliases: lsft, shift_left
>>
a >> n
Right bit-shift: divides the element by 2^n.
Returns: Quadratic — result after right shift.
Example:
var e = Quadratic(8, 8, 1, 1)
say (e >> 3) #=> Quadratic(1, 1, 1, 1) (= e / 8)
Aliases: rsft, shift_right
powmod
x.powmod(n, m)
Modular exponentiation: computes x^n mod m efficiently using binary exponentiation, reducing modulo m at every step to keep components small.
Supports negative exponents by first computing the modular inverse.
Parameters:
n- the exponent (integer, may be negative).m- the modulus (positive integer).
Returns: Quadratic — the result of x^n mod m.
Example:
var t = Quadratic(0, 1, 1, 1) # t^2 = 1 + t
say t.powmod(100, 97) # fast modular power
# Lucas / Fibonacci mod p
say t.powmod(1000, 101)
conj
x.conj
Returns the conjugate of the element.
For a + b*t, the conjugate uses the other root t' = q - t of the minimal polynomial.
Concretely:
conj(a + b*t) = (a + b*q) + (-b)*t
Returns: Quadratic — the conjugate.
Example:
var e = Quadratic(3, 4, 1, 1)
say e.conj #=> Quadratic(7, -4, 1, 1)
say (e * e.conj) #=> Quadratic(5, 0, 1, 1)
norm
x.norm
Computes the norm of the element.
For a + b*t, the norm is a^2 + a*b*q - b^2*p.
This equals the product of the element with its conjugate and is always a plain number (the b component of the result is zero).
Returns: Number — the norm.
Example:
var e = Quadratic(3, 4, 1, 1)
say e.norm # 3^2 + 3*4*1 - 4^2*1 = 5
say (e.norm == (e * e.conj).a) #=> true
trace
x.trace
Computes the trace of the element: 2*a + b*q.
The trace is the sum of the element and its conjugate:
trace(a + b*t) = (a + b*t) + conj(a + b*t) = 2*a + b*q
Returns: Number — the trace.
Example:
var e = Quadratic(3, 4, 1, 1)
say e.trace # 2*3 + 4*1 = 10
inv
x.inv
Computes the multiplicative inverse of the element.
For a + b*t with norm N = a^2 + a*b*q - b^2*p:
inv(x) = conj(x) / N
Returns: Quadratic — the multiplicative inverse.
Example:
var e = Quadratic(3, 4, 1, 1)
say e.inv # 1/e
say (e * e.inv) #=> Quadratic(1, 0, 1, 1)
invmod
x.invmod(m)
Computes the modular multiplicative inverse modulo m: finds x' such that x * x' ≡ 1 (mod m).
Parameters:
m- the modulus (positive integer).
Returns: Quadratic — the modular inverse.
Example:
var e = Quadratic(3, 4, 1, 1)
var inv = e.invmod(97)
say ((e * inv) % 97) #=> Quadratic(1, 0, 1, 1)
is_zero
x.is_zero
Returns true if the element is zero (both a = 0 and b = 0).
Returns: Bool
Example:
say Quadratic(0, 0, 1, 1).is_zero #=> true
say Quadratic(1, 0, 1, 1).is_zero #=> false
is_one
x.is_one
Returns true if the element equals the multiplicative identity (a = 1, b = 0).
Returns: Bool
Example:
say Quadratic(1, 0, 1, 1).is_one #=> true
say Quadratic(0, 1, 1, 1).is_one #=> false
is_mone
x.is_mone
Returns true if the element equals minus one (a = -1, b = 0).
Returns: Bool
Example:
say Quadratic(-1, 0, 1, 1).is_mone #=> true
is_int
self.is_int
Returns true if the element is a plain integer, i.e. the b component is zero.
Returns: Bool
Example:
say Quadratic(5, 0, 1, 1).is_int #=> true
say Quadratic(5, 3, 1, 1).is_int #=> false
is_unit
x.is_unit
Returns true if the element is a unit (invertible), i.e. |norm(x)| = 1.
Returns: Bool
Example:
# In the ring with t^2 = t + 1, t itself has norm = -1, so it's a unit
var t = Quadratic(0, 1, 1, 1)
say t.is_unit #=> true
is_associate
x.is_associate(y)
Returns true if x and y are associates, i.e. x = u * y for some unit u.
Equivalently, checks that x * inv(y) is a unit (or both are zero).
Parameters:
y- another Quadratic in the same ring.
Returns: Bool
Example:
var x = Quadratic(3, 4, 1, 1)
var u = Quadratic(0, 1, 1, 1) # a unit (norm = -1)
say x.is_associate(x * u) #=> true
is_coprime
x.is_coprime(y)
Returns true if x and y are coprime, i.e. gcd(norm(x), norm(y)) = 1.
Parameters:
y- another Quadratic in the same ring.
Returns: Bool
Example:
var x = Quadratic(3, 1, 1, 1)
var y = Quadratic(2, 1, 1, 1)
say x.is_coprime(y) # true if their norms are coprime
is_prime
x.is_prime
Returns true if the norm of the element (in absolute value) is a prime integer.
This is a sufficient but not necessary condition for the element to be irreducible in the ring.
Returns: Bool
Example:
var e = Quadratic(1, 1, 1, 1)
say e.is_prime # true if |norm(e)| is prime
gcd
self.gcd(other)
Computes the greatest common divisor of two elements using the Euclidean algorithm in the ring (via repeated divmod).
Parameters:
other- another Quadratic in the same ring.
Returns: Quadratic — a GCD (defined up to units).
Example:
var x = Quadratic(6, 0, 1, 1)
var y = Quadratic(4, 0, 1, 1)
say x.gcd(y) # GCD in the ring
lcm
self.lcm(other)
Computes the least common multiple of two elements as:
self * other / gcd(self, other)
Returns zero if either argument is zero.
Parameters:
other- another Quadratic in the same ring.
Returns: Quadratic — the LCM.
Example:
var x = Quadratic(4, 0, 1, 1)
var y = Quadratic(6, 0, 1, 1)
say x.lcm(y)
<=>
a <=> b
Three-way (spaceship) comparison. Compares lexicographically first by a, then b, then p, then q.
Returns: Number — -1, 0, or 1.
Aliases: cmp
==
a == b
Equality test. Two Quadratic values are equal if all four components (a, b, p, q) are equal.
A Quadratic equals a plain number if b = 0 and a equals that number.
Returns: Bool
Example:
say Quadratic(3, 0, 1, 1) == 3 #=> true
say Quadratic(3, 1, 1, 1) == 3 #=> false
Aliases: eq
!=
a != b
Inequality test — negation of ==.
Returns: Bool
Aliases: ne
<
a < b
Less-than comparison based on the lexicographic ordering defined by <=>.
Returns: Bool
Aliases: lt
>
a > b
Greater-than comparison based on the lexicographic ordering defined by <=>.
Returns: Bool
Aliases: gt
≤
a ≤ b
Less-than-or-equal comparison.
Returns: Bool
Aliases: <=, le
≥
a ≥ b
Greater-than-or-equal comparison.
Returns: Bool
Aliases: >=, ge
&
a & b
Bitwise AND applied component-wise to a and b.
For two elements in the same ring, applies AND to each pair of matching components.
For a scalar, applies AND to the a component only.
Returns: Quadratic
Aliases: and
|
a | b
Bitwise OR applied component-wise to a and b.
Returns: Quadratic
Aliases: or
^
a ^ b
Bitwise XOR applied component-wise to a and b.
Returns: Quadratic
Aliases: xor
to_n
self.to_n
Converts the element to a regular Number (or Complex) by evaluating a + b*t at the principal root:
t = (q + sqrt(q^2 + 4*p)) / 2
When the discriminant q^2 + 4*p is negative, the result is complex.
Returns: Number (or Complex).
Example:
var t = Quadratic(0, 1, 1, 1) # t^2 = t + 1
say t.to_n # (1 + sqrt(5)) / 2 ≈ 1.6180339887...
Aliases: to_c
to_s
x.to_s
Returns the constructor-form string representation "Quadratic(a, b, p, q)".
Returns: String
Example:
var e = Quadratic(3, 4, 1, 1)
say e.to_s #=> "Quadratic(3, 4, 1, 1)"
Aliases: dump
pretty
x.pretty
Same as to_s: returns the string "Quadratic(a, b, p, q)".
Returns: String
Aliases: stringify
MATHEMATICAL BACKGROUND
Ring Definition
A Quadratic lives in the quotient ring Z[t] / (t^2 - q*t - p).
Every element is written as a + b*t where t satisfies t^2 = p + q*t.
Products reduce through that relation:
(a + b*t)(c + d*t) = ac + (ad+bc)*t + bd*t^2
= ac + (ad+bc)*t + bd*(p + q*t)
= (ac + bd*p) + (ad + bc + bd*q)*t
Conjugate and Norm
The minimal polynomial x^2 - q*x - p = 0 has two roots, t and t' = q - t. For e = a + b*t:
Conjugate:
conj(e) = a + b*t' = (a + b*q) - b*tNorm:
norm(e) = e * conj(e) = a^2 + a*b*q - b^2*pTrace:
trace(e) = e + conj(e) = 2*a + b*q
The norm and trace are always plain numbers (the t part cancels), which is why they play a central role in divisibility and primality tests.
Connection to Linear Recurrences
If U(n) is the second-order linear recurrence defined by
U(n) = q*U(n-1) + p*U(n-2) with U(0)=0, U(1)=1
then the element t^n has components (p*U(n-1), U(n)). This means fast exponentiation of Quadratic gives Lucas U/V sequences in O(log n) multiplications.
EXAMPLES
# Fibonacci numbers via t^2 = t + 1 (p=1, q=1)
func fib(n) {
var t = Quadratic(0, 1, 1, 1)
(t**n).b
}
say fib(10) #=> 55
say fib(20) #=> 6765
# Eisenstein integers
func Eisenstein(a, b) {
Quadratic(a, b, -1, -1)
}
var w = Eisenstein(0, 1)
say 10.by {|n| (1 - w)**n - 1 -> norm.is_prime }
# Lucas numbers (same ring, different starting point)
func luc(n) {
var t = Quadratic(0, 1, 1, 1)
(t**n).trace
}
say luc(10) #=> 123
# Pell-like sequence t^2 = 2t + 1 (p=1, q=2)
var x = Quadratic(1, 1, 1, 2)
say x**5
# Gaussian integers: t^2 = -1 (p=-1, q=0)
var i = Quadratic(0, 1, -1, 0) # t = i
var z = Quadratic(3, 4, -1, 0) # 3 + 4i
say z.norm #=> 25 (= 3^2 + 4^2)
say z.conj #=> Quadratic(3, -4, -1, 0)
# Modular arithmetic
var e = Quadratic(1, 1, 1, 1)
say e.powmod(100, 97)
say e.invmod(97)
SEE ALSO
https://en.wikipedia.org/wiki/Quadratic_integer
https://en.wikipedia.org/wiki/Lucas_sequence
https://en.wikipedia.org/wiki/Quadratic_field
AUTHOR
Daniel "Trizen" Șuteu
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Sidef itself.