qpms/qpms/cypolynomials.pyx

147 lines
5.1 KiB
Cython

from libc.limits cimport INT_MIN, INT_MAX, UINT_MAX, LONG_MIN, LONG_MAX, ULONG_MAX
from gmpy2 cimport * # requires gmpy2>=2.1.0 or later (pip has currently 2.0.something)
cdef extern from "gmp.h":
# cdef struct __mpq_struct:
# pass
# ctypedef __mpq_struct mpq_t[1]
# cdef struct __mpz_struct:
# pass
# ctypedef __mpz_struct mpz_t[1]
double mpz_get_d(const mpz_t op)
void mpq_init(mpq_t q)
void mpq_clear(mpq_t q)
int mpq_sgn(mpq_t q)
cdef extern from "polynomials.h":
cdef struct qpq_t:
int order
int offset
int capacity
mpq_t *coeffs
void qpq_init(qpq_t *p, int capacity)
void qpq_extend(qpq_t *p, int new_capacity)
void qpq_set(qpq_t *copy, const qpq_t *orig)
void qpq_set_elem(qpq_t *p, int exponent, const mpq_t coeff)
void qpq_set_elem_si(qpq_t *p, int exponent, long numerator, unsigned long denominator)
void qpq_get_elem(mpq_t coeff, const qpq_t *p, int exponent)
int qpq_get_elem_si(long *numerator, unsigned long *denominator, const qpq_t *p, int exponent)
void qpq_clear(qpq_t *p)
void qpq_add(qpq_t *sum, const qpq_t *addend1, const qpq_t *addend2)
void qpq_sub(qpq_t *difference, const qpq_t *minuend, const qpq_t *substrahend)
void qpq_mul(qpq_t *product, const qpq_t *multiplier, const qpq_t *multiplicand)
void qpq_div(qpq_t *quotient, qpq_t *remainder, const qpq_t *dividend, const qpq_t *divisor)
void qpq_deriv(qpq_t *dPdx, const qpq_t *P)
bint qpq_nonzero(const qpq_t *)
import_gmpy2() # needed to initialise the C-API
cdef class qpq:
""" Polynomials with rational coefficients """
cdef qpq_t p
def __cinit__(self, *args, **kwargs):
qpq_init(&self.p, 0)
def __init__(self, *args, **kwargs):
cdef int offset, order
cdef dict thedict
cdef mpq coeff
if len(args) > 0 and isinstance(args[0], dict) and len(args[0]) > 0:
thedict = args[0]
keys = thedict.keys()
for key in keys:
if not isinstance(key, int) or key < 0 or key > INT_MAX:
raise TypeError("Coefficient dictionary keys must be non-negative integers.")
offset = min(keys)
order = max(keys)
qpq_extend(&self.p, order - offset + 1)
self.p.order = order
self.p.offset = offset
for key, val in thedict.items():
if MPQ_Check(val):
coeff = val
else:
coeff = mpq(val)
qpq_set_elem(&self.p, key, MPQ(coeff))
return
def __dealloc__(self):
qpq_clear(&self.p)
def __getitem__(self, key):
# Only one coefficient a time supported right now
cdef mpq q
cdef mpq_t cq
if isinstance(key, int):
if key >= 0 and key <= INT_MAX:
""" Can we alternatively do it like:
q = mpq() # or GMPy_MPQ_New(NULL) ?
qpq_get_elem(MPQ(q), &self.p, key)
return q
? """
mpq_init(cq)
qpq_get_elem(cq, &self.p, key)
q = GMPy_MPQ_From_mpq(cq)
mpq_clear(cq)
return q
else: raise IndexError("Only non-negative int exponents (indices) allowed.")
else: raise TypeError("Only integer exponents (indices) allowed.")
def __setitem__(self, key, value):
# Only one coefficient a time supported right now
if not MPQ_Check(value):
value = mpq(value)
if (isinstance(key, int)):
if key >= 0 and key <= INT_MAX:
qpq_set_elem(&self.p, key, MPQ(value))
else: raise IndexError("Only non-negative int exponents (indices) allowed.")
else: raise TypeError("Only integer exponents (indices) allowed.")
def __add__(qpq self, qpq other):
cdef qpq result = qpq()
qpq_add(&result.p, &self.p, &other.p)
return result
def __sub__(qpq self, qpq other):
cdef qpq result = qpq()
qpq_sub(&result.p, &self.p, &other.p)
return result
def __mul__(qpq self, qpq other):
cdef qpq result = qpq()
qpq_mul(&result.p, &self.p, &other.p)
return result
def __divmod__(qpq self, qpq other):
cdef qpq q = qpq(), r = qpq()
qpq_div(&q.p, &r.p, &self.p, &other.p)
return (q, r)
def derivative(self):
cdef qpq result = qpq()
qpq_deriv(&result.p, &self.p)
return result
@property
def order(self):
return self.p.order
@property
def offset(self):
return self.p.offset
@property
def capacity(self):
return self.p.capacity
def coeffdict(self):
""" Returns a dictionary of all non-zero coefficients """
cdef int exponent, i
cdef dict result = dict()
cdef mpq coeff
if not qpq_nonzero(&self.p):
return result
for exponent in range(self.p.offset, self.p.order + 1):
i = exponent - self.p.offset
if mpq_sgn(self.p.coeffs[i]):
result[exponent] = GMPy_MPQ_From_mpq(self.p.coeffs[i])
return result