diff --git a/src/flint/_flint.pxd b/src/flint/_flint.pxd index c31026c8..166e14a6 100644 --- a/src/flint/_flint.pxd +++ b/src/flint/_flint.pxd @@ -267,6 +267,7 @@ cdef extern from "flint/fmpz.h": void fmpz_pow_ui(fmpz_t f, fmpz_t g, ulong exp) void fmpz_powm_ui(fmpz_t f, fmpz_t g, ulong exp, fmpz_t m) void fmpz_powm(fmpz_t f, fmpz_t g, fmpz_t e, fmpz_t m) + int fmpz_pow_fmpz(fmpz_t f, const fmpz_t g, const fmpz_t x) int fmpz_sqrtmod(fmpz_t b, fmpz_t a, fmpz_t p) void fmpz_sqrt(fmpz_t f, fmpz_t g) void fmpz_sqrtrem(fmpz_t f, fmpz_t r, fmpz_t g) @@ -310,6 +311,10 @@ cdef extern from "flint/fmpz.h": int fmpz_jacobi(const fmpz_t a, const fmpz_t p) int fmpz_is_prime(const fmpz_t n) int fmpz_is_probabprime(const fmpz_t n) + void fmpz_complement(fmpz_t r, const fmpz_t f) + void fmpz_and(fmpz_t r, const fmpz_t a, const fmpz_t b) + void fmpz_or(fmpz_t r, const fmpz_t a, const fmpz_t b) + void fmpz_xor(fmpz_t r, const fmpz_t a, const fmpz_t b) cdef extern from "flint/fmpz_factor.h": ctypedef struct fmpz_factor_struct: @@ -547,6 +552,7 @@ cdef extern from "flint/fmpq.h": void fmpq_div(fmpq_t res, fmpq_t op1, fmpq_t op2) void fmpq_div_fmpz(fmpq_t res, fmpq_t op, fmpz_t x) int fmpq_mod_fmpz(fmpz_t res, fmpq_t x, fmpz_t mod) + int fmpq_pow_fmpz(fmpq_t a, const fmpq_t b, const fmpz_t e) int fmpq_reconstruct_fmpz(fmpq_t res, fmpz_t a, fmpz_t m) int fmpq_reconstruct_fmpz_2(fmpq_t res, fmpz_t a, fmpz_t m, fmpz_t N, fmpz_t D) mp_bitcnt_t fmpq_height_bits(fmpq_t x) diff --git a/src/flint/fmpq.pyx b/src/flint/fmpq.pyx index f0f384f4..d70a68f2 100644 --- a/src/flint/fmpq.pyx +++ b/src/flint/fmpq.pyx @@ -101,6 +101,13 @@ cdef class fmpq(flint_scalar): p = property(numer) q = property(denom) + # These are the property names in the numeric tower. + numerator = property(numer) + denominator = property(denom) + + def __reduce__(self): + return (fmpq, (int(self.p), int(self.q))) + def repr(self): if self.q == 1: return "fmpq(%s)" % self.p @@ -122,9 +129,24 @@ cdef class fmpq(flint_scalar): else: return "%s/%s" % (self.p.str(**kwargs), self.q.str(**kwargs)) + def __int__(self): + return int(self.trunc()) + + def __floor__(self): + return self.floor() + + def __ceil__(self): + return self.ceil() + + def __trunc__(self): + return self.trunc() + def __nonzero__(self): return not fmpq_is_zero(self.val) + def __round__(self, ndigits=None): + return self.round(ndigits) + def __pos__(self): return self @@ -335,6 +357,37 @@ cdef class fmpq(flint_scalar): fmpz_cdiv_q(r.val, fmpq_numref(self.val), fmpq_denref(self.val)) return r + def trunc(self): + """ + Truncation function. + + >>> fmpq(3,2).trunc() + 1 + >>> fmpq(-3,2).trunc() + -1 + """ + cdef fmpz r = fmpz.__new__(fmpz) + fmpz_tdiv_q(r.val, fmpq_numref(self.val), fmpq_denref(self.val)) + return r + + def round(self, ndigits=None): + """ + Rounding function. + + >>> fmpq(3,2).round() + 2 + >>> fmpq(-3,2).round() + -2 + """ + from fractions import Fraction + fself = Fraction(int(self.p), int(self.q)) + if ndigits is not None: + fround = round(fself, ndigits) + return fmpq(fround.numerator, fround.denominator) + else: + fround = round(fself) + return fmpz(fround) + def __hash__(self): from fractions import Fraction return hash(Fraction(int(self.p), int(self.q), _normalize=False)) @@ -359,20 +412,28 @@ cdef class fmpq(flint_scalar): return max(b1, b2) def __pow__(self, n, z): + cdef fmpz_struct nval[1] + cdef int ntype = FMPZ_UNKNOWN cdef fmpq v + cdef int success cdef long e + assert z is None - e = n - if type(self) is fmpq: - v = fmpq.__new__(fmpq) - if e >= 0: - fmpz_pow_ui(fmpq_numref(v.val), fmpq_numref((self).val), e) - fmpz_pow_ui(fmpq_denref(v.val), fmpq_denref((self).val), e) - else: - if fmpq_is_zero((self).val): - raise ZeroDivisionError - fmpz_pow_ui(fmpq_denref(v.val), fmpq_numref((self).val), -e) - fmpz_pow_ui(fmpq_numref(v.val), fmpq_denref((self).val), -e) - return v - return NotImplemented + ntype = fmpz_set_any_ref(nval, n) + if ntype == FMPZ_UNKNOWN: + return NotImplemented + + if fmpq_is_zero((self).val) and fmpz_sgn(nval) == -1: + if ntype == FMPZ_TMP: fmpz_clear(nval) + raise ZeroDivisionError + + v = fmpq.__new__(fmpq) + success = fmpq_pow_fmpz(v.val, (self).val, nval) + + if ntype == FMPZ_TMP: fmpz_clear(nval) + + if success: + return v + else: + raise OverflowError("fmpq_pow_fmpz(): exponent too large") diff --git a/src/flint/fmpz.pyx b/src/flint/fmpz.pyx index ee689de0..5900aa0e 100644 --- a/src/flint/fmpz.pyx +++ b/src/flint/fmpz.pyx @@ -91,6 +91,17 @@ cdef class fmpz(flint_scalar): return raise TypeError("cannot create fmpz from type %s" % type(val)) + @property + def numerator(self): + return self + + @property + def denominator(self): + return fmpz(1) + + def __reduce__(self): + return (fmpz, (int(self),)) + # XXX: improve! def __int__(self): return fmpz_get_intlong(self.val) @@ -101,6 +112,24 @@ cdef class fmpz(flint_scalar): def __index__(self): return fmpz_get_intlong(self.val) + def __float__(self): + return float(fmpz_get_intlong(self.val)) + + def __floor__(self): + return self + + def __ceil__(self): + return self + + def __trunc__(self): + return self + + def __round__(self, ndigits=None): + if ndigits is None: + return self + else: + return fmpz(round(int(self), ndigits)) + def __richcmp__(s, t, int op): cdef bint res = 0 cdef long tl @@ -334,28 +363,197 @@ cdef class fmpz(flint_scalar): return u def __pow__(s, t, m): - cdef ulong exp - u = NotImplemented - if m is not None: - raise NotImplementedError("modular exponentiation") - c = t - u = fmpz.__new__(fmpz) - fmpz_pow_ui((u).val, (s).val, c) - return u - - def __rpow__(s, t, m): cdef fmpz_struct tval[1] - cdef int stype = FMPZ_UNKNOWN - cdef ulong exp + cdef fmpz_struct mval[1] + cdef int ttype = FMPZ_UNKNOWN + cdef int mtype = FMPZ_UNKNOWN + cdef int success u = NotImplemented - if m is not None: - raise NotImplementedError("modular exponentiation") ttype = fmpz_set_any_ref(tval, t) - if ttype != FMPZ_UNKNOWN: + if ttype == FMPZ_UNKNOWN: + return NotImplemented + + if m is None: + # fmpz_pow_fmpz throws if x is negative + if fmpz_sgn(tval) == -1: + if ttype == FMPZ_TMP: fmpz_clear(tval) + raise ValueError("negative exponent") + u = fmpz.__new__(fmpz) - s_ulong = fmpz_get_ui(s.val) - fmpz_pow_ui((u).val, tval, s_ulong) + success = fmpz_pow_fmpz((u).val, (s).val, tval) + + if not success: + if ttype == FMPZ_TMP: fmpz_clear(tval) + raise OverflowError("fmpz_pow_fmpz: exponent too large") + else: + # Modular exponentiation + mtype = fmpz_set_any_ref(mval, m) + if mtype != FMPZ_UNKNOWN: + + if fmpz_is_zero(mval): + if ttype == FMPZ_TMP: fmpz_clear(tval) + if mtype == FMPZ_TMP: fmpz_clear(mval) + raise ValueError("pow(): modulus cannot be zero") + + # The Flint docs say that fmpz_powm will throw if m is zero + # but it also throws if m is negative. Python generally allows + # e.g. pow(2, 2, -3) == (2^2) % (-3) == -2. We could implement + # that here as well but it is not clear how useful it is. + if fmpz_sgn(mval) == -1: + if ttype == FMPZ_TMP: fmpz_clear(tval) + if mtype == FMPZ_TMP: fmpz_clear(mval) + raise ValueError("pow(): negative modulua not supported") + + u = fmpz.__new__(fmpz) + fmpz_powm((u).val, (s).val, tval, mval) + if ttype == FMPZ_TMP: fmpz_clear(tval) + if mtype == FMPZ_TMP: fmpz_clear(mval) + return u + + def __rpow__(s, t, m): + t = any_as_fmpz(t) + if t is NotImplemented: + return t + return t.__pow__(s, m) + + def __lshift__(self, other): + if typecheck(other, fmpz): + other = int(other) + if typecheck(other, int): + if other < 0: + raise ValueError("negative shift count") + u = fmpz.__new__(fmpz) + fmpz_mul_2exp((u).val, self.val, other) + return u + else: + return NotImplemented + + def __rlshift__(self, other): + iself = int(self) + if iself < 0: + raise ValueError("negative shift count") + if typecheck(other, int): + u = fmpz.__new__(fmpz) + fmpz_mul_2exp((u).val, fmpz(other).val, iself) + return u + else: + return NotImplemented + + def __rshift__(self, other): + if typecheck(other, fmpz): + other = int(other) + if typecheck(other, int): + if other < 0: + raise ValueError("negative shift count") + u = fmpz.__new__(fmpz) + fmpz_fdiv_q_2exp((u).val, self.val, other) + return u + else: + return NotImplemented + + def __rrshift__(self, other): + iself = int(self) + if iself < 0: + raise ValueError("negative shift count") + if typecheck(other, int): + u = fmpz.__new__(fmpz) + fmpz_fdiv_q_2exp((u).val, fmpz(other).val, iself) + return u + else: + return NotImplemented + + def __and__(self, other): + cdef fmpz_struct tval[1] + cdef int ttype = FMPZ_UNKNOWN + ttype = fmpz_set_any_ref(tval, other) + if ttype == FMPZ_UNKNOWN: + return NotImplemented + u = fmpz.__new__(fmpz) + fmpz_and((u).val, self.val, tval) + if ttype == FMPZ_TMP: + fmpz_clear(tval) + return u + + def __rand__(self, other): + cdef fmpz_struct tval[1] + cdef int ttype = FMPZ_UNKNOWN + ttype = fmpz_set_any_ref(tval, other) + if ttype == FMPZ_UNKNOWN: + return NotImplemented + u = fmpz.__new__(fmpz) + fmpz_and((u).val, tval, self.val) + if ttype == FMPZ_TMP: + fmpz_clear(tval) + return u + + # This is the correct code when fmpz_or is fixed (in flint 3.0.0) + # + #def __or__(self, other): + # cdef fmpz_struct tval[1] + # cdef int ttype = FMPZ_UNKNOWN + # ttype = fmpz_set_any_ref(tval, other) + # if ttype == FMPZ_UNKNOWN: + # return NotImplemented + # u = fmpz.__new__(fmpz) + # fmpz_or((u).val, self.val, tval) + # if ttype == FMPZ_TMP: + # fmpz_clear(tval) + # return u + # + #def __ror__(self, other): + # cdef fmpz_struct tval[1] + # cdef int ttype = FMPZ_UNKNOWN + # ttype = fmpz_set_any_ref(tval, other) + # if ttype == FMPZ_UNKNOWN: + # return NotImplemented + # u = fmpz.__new__(fmpz) + # fmpz_or((u).val, tval, self.val) + # if ttype == FMPZ_TMP: + # fmpz_clear(tval) + # return u + + def __or__(self, other): + if typecheck(other, fmpz): + other = int(other) + if typecheck(other, int): + return fmpz(int(self) | other) + else: + return NotImplemented + + def __ror__(self, other): + if typecheck(other, int): + return fmpz(other | int(self)) + else: + return NotImplemented + + def __xor__(self, other): + cdef fmpz_struct tval[1] + cdef int ttype = FMPZ_UNKNOWN + ttype = fmpz_set_any_ref(tval, other) + if ttype == FMPZ_UNKNOWN: + return NotImplemented + u = fmpz.__new__(fmpz) + fmpz_xor((u).val, self.val, tval) + if ttype == FMPZ_TMP: + fmpz_clear(tval) + return u + + def __rxor__(self, other): + cdef fmpz_struct tval[1] + cdef int ttype = FMPZ_UNKNOWN + ttype = fmpz_set_any_ref(tval, other) + if ttype == FMPZ_UNKNOWN: + return NotImplemented + u = fmpz.__new__(fmpz) + fmpz_xor((u).val, tval, self.val) + if ttype == FMPZ_TMP: + fmpz_clear(tval) + return u + + def __invert__(self): + u = fmpz.__new__(fmpz) + fmpz_complement((u).val, self.val) return u def gcd(self, other): diff --git a/test/test.py b/test/test.py index 0234c68b..bb626590 100644 --- a/test/test.py +++ b/test/test.py @@ -1,8 +1,11 @@ import sys -import flint +import math import operator +import pickle import doctest +import flint + if sys.version_info[0] >= 3: long = int @@ -82,9 +85,22 @@ def test_fmpz(): for t in L: for ltype in (flint.fmpz, int, long): for rtype in (flint.fmpz, int, long): + + assert (ltype(s) == rtype(t)) == (s == t) + assert (ltype(s) != rtype(t)) == (s != t) + assert (ltype(s) < rtype(t)) == (s < t) + assert (ltype(s) <= rtype(t)) == (s <= t) + assert (ltype(s) > rtype(t)) == (s > t) + assert (ltype(s) >= rtype(t)) == (s >= t) + assert ltype(s) + rtype(t) == s + t assert ltype(s) - rtype(t) == s - t assert ltype(s) * rtype(t) == s * t + assert ltype(s) & rtype(t) == s & t + assert ltype(s) | rtype(t) == s | t + assert ltype(s) ^ rtype(t) == s ^ t + assert ~ltype(s) == ~s + if t == 0: assert raises(lambda: ltype(s) // rtype(t), ZeroDivisionError) assert raises(lambda: ltype(s) % rtype(t), ZeroDivisionError) @@ -93,14 +109,65 @@ def test_fmpz(): assert ltype(s) // rtype(t) == s // t assert ltype(s) % rtype(t) == s % t assert divmod(ltype(s), rtype(t)) == divmod(s, t) - assert (ltype(s) == rtype(t)) == (s == t) - assert (ltype(s) != rtype(t)) == (s != t) - assert (ltype(s) < rtype(t)) == (s < t) - assert (ltype(s) <= rtype(t)) == (s <= t) - assert (ltype(s) > rtype(t)) == (s > t) - assert (ltype(s) >= rtype(t)) == (s >= t) + if 0 <= t < 10: assert (ltype(s) ** rtype(t)) == (s ** t) + assert ltype(s) << rtype(t) == s << t + assert ltype(s) >> rtype(t) == s >> t + elif -10 <= t < 0: + assert raises(lambda: ltype(s) << rtype(t), ValueError) + assert raises(lambda: ltype(s) >> rtype(t), ValueError) + + assert 2 ** flint.fmpz(2) == 4 + assert type(2 ** flint.fmpz(2)) == flint.fmpz + assert raises(lambda: () ** flint.fmpz(1), TypeError) + assert raises(lambda: flint.fmpz(1) ** (), TypeError) + assert raises(lambda: flint.fmpz(1) ** -1, ValueError) + + mega = flint.fmpz(2) ** 8000000 + assert raises(lambda: mega ** mega, OverflowError) + + pow_mod_examples = [ + (2, 2, 3, 1), + (2, -1, 5, 3), + (2, 0, 5, 1), + ] + for a, b, c, ab_mod_c in pow_mod_examples: + assert pow(a, b, c) == ab_mod_c + assert pow(flint.fmpz(a), b, c) == ab_mod_c + assert pow(a, flint.fmpz(b), c) == ab_mod_c + assert pow(flint.fmpz(a), flint.fmpz(b), c) == ab_mod_c + assert pow(flint.fmpz(a), flint.fmpz(b), flint.fmpz(c)) == ab_mod_c + + assert raises(lambda: pow(flint.fmpz(2), 2, 0), ValueError) + # XXX: Handle negative modulus like int? + assert raises(lambda: pow(flint.fmpz(2), 2, -1), ValueError) + + f = flint.fmpz(2) + assert f.numerator == f + assert type(f.numerator) is flint.fmpz + assert f.denominator == 1 + assert type(f.denominator) is flint.fmpz + + assert int(f) == 2 + assert type(int(f)) is int + assert operator.index(f) == 2 + assert type(operator.index(f)) is int + assert float(f) == 2.0 + assert type(float(f)) is float + assert round(f) == 2 + assert type(round(f)) is flint.fmpz + assert round(f, 1) == 2 + assert type(round(f, 1)) is flint.fmpz + assert round(f, -1) == 0 + assert type(round(f, -1)) is flint.fmpz + assert math.trunc(f) == 2 + assert type(math.trunc(f)) is flint.fmpz + assert math.floor(f) == 2 + assert type(math.floor(f)) is flint.fmpz + assert math.ceil(f) == 2 + assert type(math.ceil(f)) is flint.fmpz + assert flint.fmpz(2) != [] assert +flint.fmpz(0) == 0 assert +flint.fmpz(1) == 1 @@ -111,35 +178,72 @@ def test_fmpz(): assert abs(flint.fmpz(0)) == 0 assert abs(flint.fmpz(1)) == 1 assert abs(flint.fmpz(-1)) == 1 - assert int(flint.fmpz(2)) == 2 - assert isinstance(int(flint.fmpz(2)), int) - assert long(flint.fmpz(2)) == 2 - assert isinstance(long(flint.fmpz(2)), long) - l = [1, 2, 3] - l[flint.fmpz(1)] = -2 - assert l == [1, -2, 3] - d = {flint.fmpz(2): 3} - d[flint.fmpz(2)] = -1 - assert d == {flint.fmpz(2): -1} + + assert bool(flint.fmpz(0)) == False + assert bool(flint.fmpz(1)) == True + assert flint.fmpz(2).bit_length() == 2 assert flint.fmpz(-2).bit_length() == 2 assert flint.fmpz(2).height_bits() == 2 assert flint.fmpz(-2).height_bits() == 2 assert flint.fmpz(2).height_bits(signed=True) == 2 assert flint.fmpz(-2).height_bits(signed=True) == -2 + + f1 = flint.fmpz(1) + f2 = flint.fmpz(2) + f3 = flint.fmpz(3) + f8 = flint.fmpz(8) + + assert f2 << 2 == 8 + assert f2 << f2 == 8 + assert 2 << f2 == 8 + assert raises(lambda: f2 << -1, ValueError) + assert raises(lambda: 2 << -f1, ValueError) + + assert f8 >> 2 == f2 + assert f8 >> f2 == f2 + assert 8 >> f2 == f2 + assert raises(lambda: f2 >> -1, ValueError) + assert raises(lambda: 2 >> -f1, ValueError) + + assert f2 & 3 == 2 + assert f2 & f3 == 2 + assert 2 & f3 == 2 + assert f2 | 3 == 3 + assert f2 | f3 == 3 + assert 2 | f3 == 3 + assert f2 ^ 3 == 1 + assert f2 ^ f3 == 1 + assert 2 ^ f3 == 1 + + assert raises(lambda: f2 << (), TypeError) + assert raises(lambda: () << f2, TypeError) + assert raises(lambda: f2 >> (), TypeError) + assert raises(lambda: () >> f2, TypeError) + assert raises(lambda: f2 & (), TypeError) + assert raises(lambda: () & f2, TypeError) + assert raises(lambda: f2 | (), TypeError) + assert raises(lambda: () | f2, TypeError) + assert raises(lambda: f2 ^ (), TypeError) + assert raises(lambda: () ^ f2, TypeError) + + l = [1, 2, 3] + l[flint.fmpz(1)] = -2 + assert l == [1, -2, 3] + d = {flint.fmpz(2): 3} + d[flint.fmpz(2)] = -1 + + assert d == {flint.fmpz(2): -1} ctx.pretty = False assert repr(flint.fmpz(0)) == "fmpz(0)" assert repr(flint.fmpz(-27)) == "fmpz(-27)" ctx.pretty = True assert repr(flint.fmpz(0)) == "0" assert repr(flint.fmpz(-27)) == "-27" - assert bool(flint.fmpz(0)) == False - assert bool(flint.fmpz(1)) == True bigstr = '1' * 100 big = flint.fmpz(bigstr) assert big.str() == bigstr assert big.str(condense=10) == '1111111111{...80 digits...}1111111111' - assert raises(lambda: pow(flint.fmpz(2), 2, 3), NotImplementedError) def test_fmpz_factor(): assert flint.fmpz(6).gcd(flint.fmpz(9)) == 3 @@ -619,6 +723,7 @@ def test_fmpq(): assert 0 == Q(0) assert Q(2) != 1 assert 1 != Q(2) + assert Q(1) != () assert Q(1,2) != 1 assert Q(2,3) == Q(flint.fmpz(2),long(3)) assert Q(-2,-4) == Q(1,2) @@ -656,6 +761,10 @@ def test_fmpq(): # XXX: This should NotImplementedError or something. assert raises(lambda: pow(Q(1,2),2,3), AssertionError) + megaz = flint.fmpz(2) ** 8000000 + megaq = Q(megaz) + assert raises(lambda: megaq ** megaz, OverflowError) + assert raises(lambda: Q(1,2) + [], TypeError) assert raises(lambda: Q(1,2) - [], TypeError) assert raises(lambda: Q(1,2) * [], TypeError) @@ -684,14 +793,16 @@ def test_fmpq(): assert (Q(1,2) >= Q(1,2)) is True assert raises(lambda: Q(1,2) > [], TypeError) assert raises(lambda: [] < Q(1,2), TypeError) + ctx.pretty = False assert repr(Q(-2,3)) == "fmpq(-2,3)" assert repr(Q(3)) == "fmpq(3)" ctx.pretty = True assert str(Q(-2,3)) == "-2/3" assert str(Q(3)) == "3" - assert Q(2,3).p == Q(2,3).numer() == 2 - assert Q(2,3).q == Q(2,3).denom() == 3 + + assert Q(2,3).p == Q(2,3).numer() == Q(2,3).numerator == 2 + assert Q(2,3).q == Q(2,3).denom() == Q(2,3).denominator == 3 assert +Q(5,7) == Q(5,7) assert -Q(5,7) == Q(-5,7) assert -Q(-5,7) == Q(5,7) @@ -705,12 +816,32 @@ def test_fmpq(): assert Q(-5,3).floor() == flint.fmpz(-2) assert Q(5,3).ceil() == flint.fmpz(2) assert Q(-5,3).ceil() == flint.fmpz(-1) - # XXX: Need __floor__ etc. - # - # assert math.floor(Q(5,3)) == flint.fmpz(1) - # assert math.ceil(Q(5,3)) == flint.fmpz(2) - # assert math.trunc(Q(5,3)) == flint.fmpz(2) - # assert round(Q(5,3)) == 2 + + assert type(int(Q(5,3))) is int + assert type(math.floor(Q(5,3))) is flint.fmpz + assert type(math.ceil(Q(5,3))) is flint.fmpz + assert type(math.trunc(Q(5,3))) is flint.fmpz + assert type(round(Q(5,3))) is flint.fmpz + assert type(round(Q(5,3))) is flint.fmpz + assert type(round(Q(5,3), 0)) is flint.fmpq + assert type(round(Q(5,3), 1)) is flint.fmpq + + assert int(Q(5,3)) == 1 + assert math.floor(Q(5,3)) == flint.fmpz(1) + assert math.ceil(Q(5,3)) == flint.fmpz(2) + assert math.trunc(Q(5,3)) == flint.fmpz(1) + assert round(Q(5,3)) == flint.fmpz(2) + + assert int(Q(-5,3)) == flint.fmpz(-1) + assert math.floor(Q(-5,3)) == flint.fmpz(-2) + assert math.ceil(Q(-5,3)) == flint.fmpz(-1) + assert math.trunc(Q(-5,3)) == flint.fmpz(-1) + assert round(Q(-5,3)) == -2 + + assert round(Q(100,3), 2) == Q(3333,100) + assert round(Q(100,3), 0) == Q(33,1) + assert round(Q(100,3), -1) == Q(30,1) + assert round(Q(100,3), -2) == Q(0) d = {} d[Q(1,2)] = 3 @@ -1413,6 +1544,17 @@ def test_arb(): assert A(3) != A(2) assert not (A("1.1") == A("1.1")) +def test_pickling(): + objects = [ + flint.fmpz(1), + flint.fmpq(1,2), + # XXX: Add pickling for everything else + ] + for obj in objects: + s = pickle.dumps(obj) + obj2 = pickle.loads(s) + assert obj == obj2 + if __name__ == "__main__": sys.stdout.write("test_pyflint..."); test_pyflint(); print("OK") sys.stdout.write("test_fmpz..."); test_fmpz(); print("OK")