From c24aa2f59a77c15794db2d81c2574a5dcec177e5 Mon Sep 17 00:00:00 2001 From: dasm Date: Fri, 31 Oct 2025 17:14:10 -0700 Subject: [PATCH 1/2] Add hash function and re-enable hash tests. --- src/flint/test/test_arb.py | 64 ++++++++++++++++++-------------------- src/flint/types/arb.pyx | 4 +++ 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/flint/test/test_arb.py b/src/flint/test/test_arb.py index 785b0ef2..7863b348 100644 --- a/src/flint/test/test_arb.py +++ b/src/flint/test/test_arb.py @@ -110,37 +110,36 @@ def test_contains(): ]: assert (x in y) == expected -# TODO: Re-enable this if we ever add the ability to hash arbs. -# def test_hash(): -# """`x` and `y` hash to the same value if they have the same midpoint and radius. - -# Args: -# x: An arb. -# y: An arb. -# expected: Whether `x` and `y` should hash to the same value. -# """ -# def arb_pi(prec): -# """Helper to calculate arb to a given precision.""" -# with ctx.workprec(prec): -# return arb.pi() -# for x, y, expected in [ -# (arb(10), arb(10), True), -# (arb(10), arb(11), False), -# (arb(10.0), arb(10), True), -# ( -# arb(mid=10, rad=2), -# arb(mid=10, rad=2), -# True, -# ), -# ( -# arb(mid=10, rad=2), -# arb(mid=10, rad=3), -# False, -# ), -# (arb_pi(100), arb_pi(100), True), -# (arb_pi(100), arb_pi(1000), False), -# ]: -# assert (hash(x) == hash(y)) == expected +def test_hash(): + """`x` and `y` hash to the same value if they have the same midpoint and radius. + + Args: + x: An arb. + y: An arb. + expected: Whether `x` and `y` should hash to the same value. + """ + def arb_pi(prec): + """Helper to calculate arb to a given precision.""" + with ctx.workprec(prec): + return arb.pi() + for x, y, expected in [ + (arb(10), arb(10), True), + (arb(10), arb(11), False), + (arb(10.0), arb(10), True), + ( + arb(mid=10, rad=2), + arb(mid=10, rad=2), + True, + ), + ( + arb(mid=10, rad=2), + arb(mid=10, rad=3), + False, + ), + (arb_pi(100), arb_pi(100), True), + (arb_pi(100), arb_pi(1000), False), + ]: + assert (hash(x) == hash(y)) == expected @@ -338,8 +337,7 @@ def test_no_tests_missing(): test_lower, test_upper, test_contains, - # TODO: Re-enable this if we ever add the ability to hash arbs. - # test_hash, + test_hash, test_arb_sub, test_arb_add, test_arb_mul, diff --git a/src/flint/types/arb.pyx b/src/flint/types/arb.pyx index d552dc7a..643e1dab 100644 --- a/src/flint/types/arb.pyx +++ b/src/flint/types/arb.pyx @@ -527,6 +527,10 @@ cdef class arb(flint_scalar): arb_clear(tval) return res + def __hash__(self): + """Hash.""" + return hash((self.mid().man_exp(), self.rad().man_exp())) + def __contains__(self, other): other = any_as_arb(other) return arb_contains(self.val, (other).val) From 621b0add5af81f0a20f423b97f5e314d8981f9de Mon Sep 17 00:00:00 2001 From: dasm Date: Tue, 4 Nov 2025 15:15:32 -0800 Subject: [PATCH 2/2] Make hashing fail for inexact arbs. --- src/flint/test/test_arb.py | 23 +++++++++++------------ src/flint/types/arb.pyx | 4 +++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/flint/test/test_arb.py b/src/flint/test/test_arb.py index 7863b348..66fce5f2 100644 --- a/src/flint/test/test_arb.py +++ b/src/flint/test/test_arb.py @@ -126,21 +126,20 @@ def arb_pi(prec): (arb(10), arb(10), True), (arb(10), arb(11), False), (arb(10.0), arb(10), True), - ( - arb(mid=10, rad=2), - arb(mid=10, rad=2), - True, - ), - ( - arb(mid=10, rad=2), - arb(mid=10, rad=3), - False, - ), - (arb_pi(100), arb_pi(100), True), - (arb_pi(100), arb_pi(1000), False), ]: assert (hash(x) == hash(y)) == expected + for x in [ + arb(mid=10, rad=2), + arb_pi(100), + ]: + try: + hash(x) + except ValueError: + pass + else: + assert False, f"Expected {x} to raise an error if hashed, but succeeded." + # Tests for arithmetic functions in `flint.arb`. diff --git a/src/flint/types/arb.pyx b/src/flint/types/arb.pyx index 643e1dab..dbb5c066 100644 --- a/src/flint/types/arb.pyx +++ b/src/flint/types/arb.pyx @@ -529,7 +529,9 @@ cdef class arb(flint_scalar): def __hash__(self): """Hash.""" - return hash((self.mid().man_exp(), self.rad().man_exp())) + if self.is_exact(): + return hash((self.mid().man_exp(), self.rad().man_exp())) + raise ValueError(f"Cannot hash non-exact arb: {self}. See pull/341 for details.") def __contains__(self, other): other = any_as_arb(other)