NAME

Sidef::Types::Number::Polynomial - Univariate polynomial arithmetic and operations

DESCRIPTION

This class implements univariate polynomials with arbitrary-precision coefficients. Polynomials are represented as mathematical expressions in a single variable (x), supporting arithmetic, evaluation, differentiation, root-finding, and algebraic manipulation.

Internally, polynomials are stored as a sparse hash mapping integer exponents to their coefficients (Sidef::Types::Number::Number objects). Terms with a zero coefficient are never stored, making the representation efficient for sparse polynomials with many zero terms.

SYNOPSIS

# Constructors
var a = Polynomial(5)                  # monomial x^5
var b = Polynomial([1, 2, 3, 4])       # x^3 + 2*x^2 + 3*x + 4
var c = Polynomial(5 => 3, 2 => 10)    # 3*x^5 + 10*x^2
var d = Polynomial("x^3 - 2*x + 1")    # parsed from string

# Arithmetic
var sum  = b + c                       # polynomial addition
var prod = b * c                       # polynomial multiplication
var quot = b / c                       # polynomial division

# Evaluation
say(b.eval(2))                         # evaluate at x=2: 26

# Algebraic operations
say(b.derivative)                      # 3*x^2 + 4*x + 3
say(b.roots)                           # numerical roots
say(b.gcd(c))                          # greatest common divisor

COEFFICIENT ORDER IN ARRAY CONSTRUCTORS

When building a polynomial from an array, coefficients are supplied in descending degree order (highest-degree first):

Polynomial([a_n, a_{n-1}, ..., a_1, a_0])
# represents: a_n*x^n + a_{n-1}*x^{n-1} + ... + a_1*x + a_0

Examples:

Polynomial([1, 2, 3])      # x^2 + 2*x + 3   (NOT 3*x^2 + 2*x + 1)
Polynomial([1, 0, -1])     # x^2 - 1
Polynomial([1, 0, 0, 1])   # x^3 + 1

Individual array elements may also be [exponent, coefficient] pairs to set a coefficient at an explicit degree:

Polynomial([[3, 2], [1, 5]])   # 2*x^3 + 5*x

INHERITS

Inherits methods from:

Sidef::Types::Number::Number

METHODS

new

Polynomial(n)
Polynomial([c_n, ..., c_1, c_0])
Polynomial("expression")
Polynomial(exp1 => coeff1, exp2 => coeff2, ...)
self.new(value)

Constructs a new Polynomial object. The following calling forms are supported:

Monomial — a single Number argument n: creates the monomial x^n.
Polynomial(5)    # x^5
Polynomial(0)    # x^0 = 1
Array — a single array-ref argument: coefficients in descending degree order.
Polynomial([1, 2, 3])    # x^2 + 2*x + 3

Array elements may themselves be [exponent, coeff] pairs for explicit placement:

Polynomial([[2, 1], [0, 3]])   # x^2 + 3
String — a single string argument: parses an algebraic expression. Supports terms of the form [±][coefficient[/denominator]*]x[^exponent], as well as fraction syntax (numerator)/(denominator).
Polynomial("3*x^2 + 2*x + 1")   # 3*x^2 + 2*x + 1
Polynomial("x^3 - 5")           # x^3 - 5
Polynomial("1/2*x^2 + 3/4")     # (1/2)*x^2 + 3/4
Sparse key-value pairs — alternating exponent/coefficient arguments:
Polynomial(5 => 3, 2 => 10)   # 3*x^5 + 10*x^2
Polynomial(0 => 7)            # constant 7

Zero coefficients are silently dropped.

Instance call with one argument — when called as a method on an existing polynomial, evaluates that polynomial at the given value (equivalent to "eval"):
var p = Polynomial([1, 0, -1])   # x^2 - 1
say(p.new(3))                    # 3^2 - 1 = 8

Aliases: call

+

a + b

Returns the sum of a and b. When both arguments are polynomials, corresponding coefficients are added. When b is a scalar, it is added to the constant term (x^0 coefficient), inserting it if absent.

say(Polynomial([1, 2]) + Polynomial([3, 4]))   # 4*x + 6

Aliases: add

++

a++

Adds 1 to the constant term of a. Returns a new polynomial with the x^0 coefficient incremented by one.

var p = Polynomial([1, 2, 3])
p++
say(p)   # x^2 + 2*x + 4

Aliases: inc

-

a - b

Returns the difference a - b. When both arguments are polynomials, corresponding coefficients are subtracted. When b is a scalar, it is subtracted from the constant term, inserting a negated term if absent.

say(Polynomial([1, 2, 3]) - Polynomial([0, 1]))   # x^2 + x + 3

Aliases: sub

--

a--

Subtracts 1 from the constant term of a. Returns a new polynomial with the x^0 coefficient decremented by one.

var p = Polynomial([1, 2, 3])
p--
say(p)   # x^2 + 2*x + 2

Aliases: dec

*

a * b

Returns the product of a and b. When both are polynomials, performs full polynomial multiplication (convolution of coefficients): the degree of the result equals deg(a) + deg(b). When b is a scalar, each coefficient is multiplied by b.

say(Polynomial([1, 2, 3]) * Polynomial([4, 5]))
# (x^2 + 2*x + 3) * (4*x + 5) = 4*x^3 + 13*x^2 + 22*x + 15

Aliases: mul

sqr

x.sqr

Returns the square of x. Equivalent to x * x.

say(Polynomial([1, 1]).sqr)   # (x+1)^2 = x^2 + 2*x + 1

**

a ** n

Returns a raised to the power n using binary (square-and-multiply) exponentiation.

  • If n is a non-negative integer, returns the polynomial product.

  • If n is a negative integer, returns Fraction(1, a^|n|) — a rational function.

  • If n is not an integer, falls back to a.to_n.pow(n), converting the polynomial to a scalar first (only valid for constant polynomials).

say(Polynomial([1, 1]) ** 3)
# (x+1)^3 = x^3 + 3*x^2 + 3*x + 1

Aliases: pow

/

a / b

Divides a by b.

  • If b is a polynomial and division is exact (zero remainder), returns the quotient polynomial.

  • If the division is inexact, returns a Sidef::Types::Number::Fraction representing the rational function (q*b + r) / b where q, r are the quotient and remainder.

  • If b is zero, returns a polynomial with an infinite coefficient.

  • If b is a scalar, divides each coefficient by b.

say(Polynomial([1, 0, -1]) / Polynomial([1, -1]))
# (x^2 - 1) / (x - 1) = x + 1  (exact)

Aliases: ÷, div

//

a // b

Returns only the quotient polynomial from Euclidean division, discarding the remainder. Equivalent to the first element of "divmod".

Note: The rounding-mode variants (idiv_ceil, idiv_floor, idiv_round, idiv_trunc) are all currently aliased to the same floor-division behaviour.

Aliases: idiv, idiv_ceil, idiv_floor, idiv_round, idiv_trunc

%

a % b

Returns the remainder polynomial from Euclidean division of a by b, i.e. the polynomial r of degree less than deg(b) satisfying a = q*b + r. When b is a scalar, each coefficient is reduced modulo b by wrapping it in a Sidef::Types::Number::Mod object.

say(Polynomial([1, 0, 0, 0]) % Polynomial([1, 0, 1]))
# x^3 mod (x^2+1) = -x  (since x^3 = x*(x^2+1) - x)

Aliases: mod

divmod

x.divmod(y)

Performs Euclidean polynomial long division and returns a two-element list (quotient, remainder) satisfying:

x == (quotient * y) + remainder
deg(remainder) < deg(y)

Returns (NaN, zero) if the leading coefficient of y is zero or NaN.

var (q, r) = Polynomial([1, 0, 0]) .divmod( Polynomial([1, 1]) )
# x^2 ÷ (x+1)  =>  q = x-1,  r = 1

neg

x.neg

Returns the additive inverse of x: all coefficients are negated. Adding x and x.neg gives the zero polynomial.

say(Polynomial([1, 2, 3]).neg)   # -x^2 - 2*x - 3

abs

x.abs

Returns x * x.sgn, i.e. the polynomial multiplied by the sign of its leading coefficient. This ensures the leading coefficient is positive. For the zero polynomial, returns zero.

say(Polynomial([-1, 2, -3]).abs)
# -x^2 + 2*x - 3  has leading coeff -1, so abs = x^2 - 2*x + 3

sgn

x.sgn

Returns the sign of the leading coefficient of x: -1, 0, or 1 as a Sidef::Types::Number::Number. Returns 0 for the zero polynomial.

inv

x.inv

Returns a Sidef::Types::Number::Fraction representing 1/x. This creates the rational function 1 / x(t). Meaningful as an exact inverse only when x is a constant polynomial; for non-constant polynomials in a quotient ring, see Sidef::Types::Number::PolynomialMod.

invmod

x.invmod(m)

Computes x.mod(m).inv: reduces x modulo the polynomial m and then returns its multiplicative inverse. Equivalent to creating PolynomialMod(x, m).inv.

powmod

x.powmod(n, m)

Computes x^n mod m using binary (square-and-multiply) exponentiation, reducing modulo polynomial m after each multiplication step. More efficient than computing x**n and then taking % m separately for large n.

say(Polynomial([1, 1]).powmod(100, Polynomial([1, 0, 1])))
# (x+1)^100 mod (x^2+1)

gcd

x.gcd(y)

Returns the greatest common divisor of polynomials x and y using the polynomial Euclidean algorithm. Handles zero polynomials explicitly: gcd(x, 0) = x and gcd(0, y) = y. The result is normalized to monic (leading coefficient equal to 1) before being returned.

var p = Polynomial([1, 0, -1])   # x^2 - 1
var q = Polynomial([1, -1])      # x - 1
say(p.gcd(q))                    # x - 1  (monic)

Reference: https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclid's_algorithm

gcdext

x.gcdext(y)

Extended Euclidean algorithm for polynomials. Returns a list of five values:

(g, u, v, a1, b1)

where g = gcd(x, y) and u, v are Bézout coefficient polynomials satisfying:

(u * x) + (v * y) == g

The additional values a1 and b1 are the final-step remainder polynomials scaled by a sign factor (-1)^i; they are the cofactors of the last non-trivial step and satisfy x/g = ±a1, y/g = ±b1 in the monic-normalised sense.

Note: The return list has five elements. If you only need the GCD and Bézout pair, capture just the first three:

var (g, u, v) = x.gcdext(y)
say(((u * x) + (v * y)) == g)   # true

Reference: https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#B%C3%A9zout's_identity_and_extended_GCD_algorithm

lcm

x.lcm(y)

Returns the least common multiple of polynomials x and y, computed as:

lcm(x, y) = abs(x * y) / gcd(x, y)

The abs ensures the leading coefficient of the result is positive.

normalize_to_monic

x.normalize_to_monic

Returns a monic version of x by dividing all coefficients by the leading coefficient. If the leading coefficient is already 1, returns x unchanged. If the leading coefficient is zero or the polynomial is empty, returns x unchanged.

say(Polynomial([3, 6, 9]).normalize_to_monic)   # x^2 + 2*x + 3

is_squarefree

x.is_squarefree

Returns true if x has no repeated irreducible factors, i.e. if gcd(x, x.derivative) equals 1 (the constant polynomial 1, monic of degree 0). Equivalently, x is square-free if and only if it shares no root with its own derivative.

say(Polynomial([1, 0, -1]).is_squarefree)    # true  (x^2-1 = (x-1)(x+1))
say(Polynomial([1, -2, 1]).is_squarefree)    # false (x^2-2x+1 = (x-1)^2)

squarefree_part

x.squarefree_part

Returns the square-free part of x: the result of dividing x by gcd(x, x.derivative). The square-free part has the same roots as x but each root occurs with multiplicity at most 1.

say(Polynomial([1, -2, 1]).squarefree_part)
# (x-1)^2 → squarefree part is (x-1)

say(Polynomial([1, 0, 0, 0]).squarefree_part)
# x^3 → squarefree part is x

derivative

x.derivative

Returns the formal derivative of x using the power rule: each term c_k * x^k maps to k * c_k * x^{k-1}. The constant term (k=0) is dropped. Works with any coefficient type, including complex or rational coefficients.

var p = Polynomial([1, 0, -3, 2])   # x^3 - 3*x + 2
say(p.derivative)                   # 3*x^2 - 3

eval

x.eval(value)

Evaluates the polynomial at value by computing the sum:

sum of:  coeff_k * value^k  for each nonzero term k

Each coefficient is itself evaluated at value via its own .eval method, allowing polynomials with polynomial coefficients (multivariate use). Returns 0 for the zero polynomial.

Operator-precedence note: when chaining say with eval, wrap in parentheses:

var p = Polynomial([1, 2, 3])   # x^2 + 2*x + 3
say(p.eval(5))                  # 25 + 10 + 3 = 38

newton_method

f.newton_method
f.newton_method(x0)
f.newton_method(x0, df)

Applies Newton's method to find a root of f, starting from initial guess x0. Both x0 and df (the derivative polynomial) are optional:

  • x0 defaults to Number::i (the complex unit i = sqrt(-1)).

  • df defaults to f.derivative.

The iteration x_{n+1} = x_n - f(x_n) / f'(x_n) runs for up to PREC steps (the current working precision). Returns the last refined approximation, which equals an actual root when convergence is detected (f(x) rounds to zero within precision).

var f  = Polynomial([1, 0, -2])   # x^2 - 2
var df = f.derivative
say(f.newton_method(1.5, df))     # ≈ 1.41421356...  (√2)

roots

f.roots

Attempts to find all roots of f numerically. Returns an Sidef::Types::Array::Array of root values (which may be complex numbers).

The algorithm:

1. Uses deg(f) evenly-spaced roots of unity as initial guesses.
2. Applies Newton's method from each starting point.
3. If fewer than deg(f) distinct roots are found, retries with transformed starting points (e.g. multiplied by i, exp, etc.).

Only distinct roots are returned (duplicates within working precision are collapsed). For polynomials with repeated roots, some roots may be missed.

say(Polynomial([1, 0, -1]).roots)    # [-1, 1]  (roots of x^2 - 1)
say(Polynomial([1, 0, 0, -1]).roots) # roots of x^3 - 1

binomial

n.binomial(k)

Computes the generalised binomial coefficient C(n, k) where n is a Polynomial and k is a non-negative integer, using the falling-factorial definition:

C(n, k) = (n * (n-1) * (n-2) * ... * (n-k+1)) / k!

The numerator product is computed via binary splitting for efficiency. Returns the constant polynomial 1 when k = 0.

var x = Polynomial(1)             # x  (monomial x^1)
say(x.binomial(2))                # x*(x-1)/2

degree

x.degree

Returns the degree of the polynomial: the highest exponent with a non-zero coefficient, as a Sidef::Types::Number::Number. Returns 0 for the zero polynomial (the empty polynomial has no keys, so the fallback value is 0).

say(Polynomial([1, 2, 3]).degree)   # 2
say(Polynomial([]).degree)          # 0  (zero polynomial)

Aliases: deg

coeff

x.coeff(k)

Returns the coefficient of the term with exponent k. Returns 0 if no such term exists.

var p = Polynomial([3, 2, 1])   # 3*x^2 + 2*x + 1
say(p.coeff(0))                 # 1  (constant term)
say(p.coeff(1))                 # 2  (coefficient of x)
say(p.coeff(2))                 # 3  (coefficient of x^2)
say(p.coeff(5))                 # 0  (absent term)

coeffs

x.coeffs

Returns an Array of [exponent, coefficient] pairs for all nonzero terms, sorted in ascending order by exponent (lowest degree first).

var p = Polynomial([1, 2, 3])   # x^2 + 2*x + 3
say(p.coeffs)
# [[0, 3], [1, 2], [2, 1]]
# (constant term first, highest-degree term last)

exponents

x.exponents

Returns an Array of all exponents that have nonzero coefficients, sorted in ascending order.

say(Polynomial(5 => 3, 2 => 1).exponents)   # [2, 5]

content

x.content

Returns the content of the polynomial: the GCD of all its coefficients. Useful for extracting the largest scalar factor.

say(Polynomial([6, 9, 12]).content)   # 3  (gcd(6,9,12))

Aliases: cont

primitive_part

x.primitive_part

Returns the primitive part of x: the polynomial divided by its content. The result has integer coefficients whose GCD is 1.

say(Polynomial([6, 9, 12]).primitive_part)
# (6*x^2 + 9*x + 12) / 3 = 2*x^2 + 3*x + 4

Aliases: prim_part, primpart

leading_coefficient

x.leading_coefficient

Returns the coefficient of the highest-degree term. Returns 0 for the zero polynomial.

say(Polynomial([3, 2, 1]).leading_coefficient)   # 3

Aliases: leading_coeff

leading_term

x.leading_term

Returns the leading term of x as a Polynomial consisting of only the highest-degree term (with its coefficient intact).

say(Polynomial([3, 2, 1]).leading_term)   # 3*x^2

leading_monomial

x.leading_monomial

Returns the leading monomial of x: the highest-degree term with its coefficient replaced by 1.

say(Polynomial([3, 2, 1]).leading_monomial)   # x^2

height

x.height

Returns the maximum absolute value among all coefficients of x. Also called the infinity norm or Chebyshev norm of the coefficient vector.

say(Polynomial([3, -5, 2]).height)   # 5

re

x.re

Returns the constant term (coefficient of x^0) of the polynomial, as a Sidef::Types::Number::Number. Returns 0 if no constant term is present. This reflects the embedding of scalars into the polynomial ring.

say(Polynomial([1, 2, 3]).re)   # 3  (a Number, not a Polynomial)

Aliases: real

is_real

x.is_real

Returns true if and only if x is effectively a real scalar constant: either the zero polynomial, or a polynomial with a single term at degree 0 whose coefficient satisfies .is_real. Returns false for any polynomial with a non-zero higher-degree term.

say(Polynomial([5]).is_real)     # true   (constant real)
say(Polynomial([1, 0]).is_real)  # false  (non-constant)

is_zero

x.is_zero

Returns true if x is the zero polynomial (no nonzero terms).

say(Polynomial([]).is_zero)      # true   (empty polynomial)
say(Polynomial([0, 1]).is_zero)  # false  (x is not zero)

is_one

x.is_one

Returns true if x equals the constant polynomial 1 (the multiplicative identity).

say(Polynomial([1]).is_one)    # true
say(Polynomial([1, 0]).is_one) # false  (this is x, not 1)

is_mone

x.is_mone

Returns true if x equals the constant polynomial -1.

is_nan

x.is_nan

Returns true if any coefficient of the polynomial is NaN. A NaN coefficient can appear as a result of invalid operations such as division by zero or failed modular inversion.

is_inf

x.is_inf

Returns true if any coefficient of the polynomial is positive infinity.

is_ninf

x.is_ninf

Returns true if any coefficient of the polynomial is negative infinity.

norm

x.norm

Returns the norm of the constant term: x.real.norm. This is the norm of the x^0 coefficient as a number, not the Euclidean norm of the coefficient vector.

to_n

x.to_n

Converts the polynomial to a scalar Sidef::Types::Number::Number:

  • Zero polynomial (no terms): returns 0.

  • Constant polynomial (only a x^0 term, all others zero or absent): returns the constant coefficient.

  • Non-constant polynomial: returns NaN.

to_poly

x.to_poly

Returns the polynomial itself. Provides a uniform interface for type conversion (e.g. PolynomialMod overrides this to return the underlying residue polynomial).

lift

x.lift

Returns a new Polynomial with each coefficient replaced by its .lift value. For plain Number coefficients this is a no-op (returns an equivalent copy). For polynomials with Mod-valued coefficients (e.g. after calling .mod(n) on a polynomial over a modular ring), lift unwraps each coefficient from its modular context.

float

x.float

Returns a new Polynomial with each coefficient converted to a floating-point approximation.

rat

x.rat

Returns a new Polynomial with each coefficient converted to an exact rational number (Math::GMPq).

rat_approx

x.rat_approx

Returns a new Polynomial with each floating-point coefficient replaced by a nearby rational approximation.

floor

x.floor

Returns a new Polynomial with floor() applied to each coefficient, rounding each one down to the nearest integer.

say(Polynomial([1.8, 2.3]).floor)   # x + 2

ceil

x.ceil

Returns a new Polynomial with ceil() applied to each coefficient, rounding each one up to the nearest integer.

say(Polynomial([1.5, 2.3]).ceil)   # 2*x + 3

round

x.round(r)

Returns a new Polynomial with each coefficient rounded to r decimal places (or to the nearest integer when r is zero).

say(Polynomial([1.456, 2.789]).round(1))   # 1.5*x + 2.8

!=

a != b

Returns true if a and b are not equal. Implemented as the logical negation of "==". See "==" for equality semantics.

Aliases: ne

==

a == b

Returns true if a equals b.

  • Polynomial vs Polynomial: true if every coefficient of a equals the corresponding coefficient of b (missing coefficients are treated as zero). Comparison is symmetric.

  • Polynomial vs scalar: true if a is a constant polynomial whose x^0 coefficient equals the scalar, and all other coefficients are zero.

Aliases: eq

<=>

a <=> b

Three-way comparison. Returns -1, 0, or 1.

  • Polynomial vs Polynomial:

    1. Polynomials with more nonzero terms are considered greater.
    2. Among polynomials with the same number of terms, comparison proceeds from the highest degree downward: compare exponents (higher exponent → greater), then compare coefficients at matching degrees.
  • Polynomial vs scalar: only defined for constant polynomials (all non-constant coefficients zero). Returns undef for non-constant polynomials when compared against a scalar.

Aliases: cmp

<

a < b

Returns true if a is strictly less than b under the ordering defined by "<=">.

Aliases: lt

>

a > b

Returns true if a is strictly greater than b.

Aliases: gt

a ≤ b

Returns true if a is less than or equal to b.

Aliases: <=, le

a ≥ b

Returns true if a is greater than or equal to b.

Aliases: >=, ge

to_s

x.to_s

Returns a Sidef::Types::String::String representation of the polynomial in standard mathematical notation, with terms ordered from highest to lowest degree:

say(Polynomial([1, 2, 3]).to_s)   # "x^2 + 2*x + 3"
say(Polynomial([1, 0, -1]).to_s)  # "x^2 - 1"
say(Polynomial([]).to_s)          # "0"

Coefficient 1 is suppressed (x not 1*x); negative signs replace leading +. Parentheses are added around complex coefficients.

Aliases: stringify

pretty

x.pretty

Like "to_s" but calls the .pretty method on each coefficient rather than .to_s. This may produce Unicode formatting (e.g. Unicode fractions or superscripts) depending on the coefficient type.

dump

x.dump

Returns a Sidef::Types::String::String showing the polynomial's internal sparse representation, suitable for debugging and round-tripping:

say(Polynomial([1, 2, 3]).dump)
# Polynomial(0 => 3, 1 => 2, 2 => 1)

Terms are listed in ascending exponent order with each coefficient shown via its own .dump.

BOOLEAN CONTEXT

In boolean context (if, while, etc.), a Polynomial is truthy if its constant term (x^0 coefficient) is truthy, and falsy if zero or absent.

if (Polynomial([1, 0])) { ... }   # falsy: no constant term
if (Polynomial([1, 3])) { ... }   # truthy: constant term is 3

EXAMPLES

Basic arithmetic

var p = Polynomial([1, -2, 1])   # x^2 - 2*x + 1  =  (x-1)^2
var q = Polynomial([1, -1])      # x - 1

var sum = (p + q)
say(sum)                         # x^2 - x

var prod = (p * q)
say(prod)                        # x^3 - 3*x^2 + 3*x - 1

var (quot, rem) = p.divmod(q)
say(quot)                        # x - 1
say(rem)                         # 0

say(p.eval(3))                   # 9 - 6 + 1 = 4
say(p.eval(1))                   # 0  (1 is a double root)

Differentiation

var p = Polynomial([1, 0, -3, 2])   # x^3 - 3*x + 2
var dp = p.derivative
say(dp)                             # 3*x^2 - 3
say(dp.derivative)                  # 6*x

Root finding

say(Polynomial([1, 0, -1]).roots)   # roots of x^2-1: [-1, 1]
say(Polynomial([1, 0, 1]).roots)    # roots of x^2+1: [-i, i]

GCD, extended GCD, and Bézout identity

var p = Polynomial([1, 0, -1])   # x^2 - 1
var q = Polynomial([1, -1])      # x - 1

say(p.gcd(q))    # x - 1

var (g, u, v) = p.gcdext(q)
say(g)           # x - 1
say(((u * p) + (v * q)) == g)   # true

Content and primitive part

var r = Polynomial([6, 9, 12])   # 6*x^2 + 9*x + 12
say(r.content)                   # 3
say(r.primitive_part)            # 2*x^2 + 3*x + 4

Square-free decomposition

var p = Polynomial([1, -2, 1])   # (x-1)^2
say(p.is_squarefree)             # false
say(p.squarefree_part)           # x - 1

Modular polynomial exponentiation

var base = Polynomial([1, 1])             # x + 1
var mod  = Polynomial([1, 0, 1])          # x^2 + 1
say(base.powmod(10, mod))
# (x+1)^10 mod (x^2+1)

Sparse construction and inspection

var p = Polynomial(7 => 1, 3 => 5, 0 => 2)   # x^7 + 5*x^3 + 2
say(p.degree)                                  # 7
say(p.exponents)                               # [0, 3, 7]
say(p.coeff(3))                                # 5
say(p.height)                                  # 5

String constructor

var p = Polynomial("3*x^2 - 2*x + 1")
say(p.eval(0))    # 1
say(p.eval(1))    # 2

Newton's method

var f  = Polynomial([1, 0, -2])   # x^2 - 2
var df = f.derivative             # 2*x
say(f.newton_method(1.5, df))     # ≈ 1.41421356...  (√2)

NOTES

  • Zero polynomial: The zero polynomial is represented as an empty hash (no keys). Its degree is 0, not -1 or -∞. Use .is_zero to test for it explicitly.

  • Coefficient ring: Coefficients are arbitrary Sidef::Types::Number::Number objects — integers, rationals, floats, or even complex numbers. There is no restriction to any particular coefficient ring.

  • No operator precedence: See the "IMPORTANT: OPERATOR PRECEDENCE" section at the top of this document. All operators share equal precedence and associate left-to-right. When in doubt, add parentheses.

  • Division: The / operator returns a Sidef::Types::Number::Fraction when division is inexact. Use // (idiv) to always get just the quotient polynomial, or divmod to get both quotient and remainder.

  • Comparison vs scalar: The <=> operator returns undef (not a defined value) when comparing a non-constant polynomial against a scalar. Guard with a defined-check when this is possible.

SEE ALSO

Sidef::Types::Number::Number, Sidef::Types::Number::PolynomialMod, Sidef::Types::Number::Fraction, Sidef::Types::Number::Mod

AUTHOR

Daniel Șuteu