diff --git a/lmfdb/tests/test_utils.py b/lmfdb/tests/test_utils.py index 96eebd7444..9fdc279bef 100644 --- a/lmfdb/tests/test_utils.py +++ b/lmfdb/tests/test_utils.py @@ -309,6 +309,30 @@ def test_integer_set(self): self.assertEqual((A + A).bound_under(X), 50000) self.assertEqual((A * B).bound_under(X), 1000) + def test_mod_operator(self): + """ + Test that $mod operator in queries is handled correctly + + Note: psycodict uses [remainder, divisor] format for $mod, so + {'$mod': [0, 7]} means "values where value % 7 == 0" (multiples of 7) + """ + from lmfdb.utils.completeness import to_rset, IntegerSet + + # Test that $mod creates an unbounded set (full real line) + # {'$mod': [0, 7]} means multiples of 7 in psycodict format + mod_query = {'$mod': [0, 7]} + rset = to_rset(mod_query) + self.assertEqual(str(rset), "(-oo, +oo)") + + # Test that IntegerSet can be created from $mod query without error + iset = IntegerSet(mod_query) + self.assertEqual(str(iset.rset), "(-oo, +oo)") + + # Test that unbounded set is not a subset of bounded set + bounded = IntegerSet([1, 500000]) + self.assertEqual(iset.is_subset(bounded), False) + self.assertEqual(bounded.is_subset(iset), True) + def test_complete(self): from lmfdb import db for tup in [ @@ -399,5 +423,9 @@ def test_complete(self): ("hgm_families", {'degree': 8}), ("gps_transitive", {'n': 32, 'solv': 1}), ("gps_st", {'rational': True, 'weight': 1, 'degree': 8}), + # Test $mod operator (multiples of n) - should be incomplete + # Note: psycodict format is [remainder, divisor], so [0, 7] means multiples of 7 + ("ec_curvedata", {'conductor': {'$mod': [0, 7]}}), + ("mf_newforms", {'level': {'$mod': [0, 23]}}), ]: self.assertEqual(results_complete(tbl, query, db)[0], False) diff --git a/lmfdb/utils/completeness.py b/lmfdb/utils/completeness.py index 743362f5fa..6a6b26409c 100644 --- a/lmfdb/utils/completeness.py +++ b/lmfdb/utils/completeness.py @@ -155,6 +155,12 @@ def to_rset(query): ans = ans.intersection(RealSet(*[RealSet.point(x) for x in val])) elif k == "$nin": ans = ans.intersection(RealSet(*[RealSet.point(x) for x in val]).complement()) + elif k == "$mod": + # $mod represents an arithmetic progression (e.g., multiples of n) + # which is unbounded, so we return the full real line + # This will cause completeness checks to fail, as expected + # Note: psycodict format is {'$mod': [remainder, divisor]} + pass else: raise ValueError(f"Unsupported key {k}") return ans