Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
47efe7b
Add ordinary and reified GCD propagators
Aug 12, 2026
ac2bf84
Add reified divides propagator
Aug 12, 2026
e2302d2
Add n-ary product propagators
Aug 12, 2026
379e436
Add fixed-modulus product propagators
Aug 12, 2026
d743d30
Use bounds propagation for n-ary product
Aug 12, 2026
f544ce4
Plan variable-modulus product propagators
Aug 12, 2026
d74327c
Add variable-modulus product propagator
Aug 12, 2026
6688970
Add reified variable-modulus product propagator
Aug 12, 2026
2bf1efb
Plan multiplication special-case propagation
Aug 12, 2026
838e3fd
Plan removal of support enumeration
Aug 12, 2026
f6f821d
Strengthen fixed product-mod propagation
Aug 12, 2026
78ab331
Strengthen variable product-mod propagation
Aug 12, 2026
c4ce30a
Clarify enumeration-free GCD and divides
Aug 12, 2026
b5e36ce
Complete zero-aware product bounds
Aug 12, 2026
300ce54
Simplify product signs and units
Aug 12, 2026
2ffffaa
Exploit product powers and aliases
Aug 12, 2026
5662326
Remove propagator support enumeration
Aug 12, 2026
f7ebd89
Correct GCD propagation documentation
Aug 15, 2026
bf109af
Strengthen enumeration-free number theory propagation
Aug 24, 2026
06a6851
Strengthen variable product-mod bounds
Sep 8, 2026
7888795
Fix arithmetic review findings and record release readiness
Sep 8, 2026
94b2829
Exercise arithmetic activation cloning and audit checks
Sep 8, 2026
143fcaa
Check number theory limits with independent identities
Sep 8, 2026
70c81ea
Recognize reified product identities and harden arithmetic tests
Sep 8, 2026
00d8d06
Merge remote-tracking branch 'origin/main' into feature/gcd
Sep 8, 2026
8c132a6
Record readiness validation and product scaling measurements
Sep 8, 2026
0b69d37
Build audit test target and include number theory in CI checks
Sep 8, 2026
778ec06
Run propagator checks directly through Gecode test framework
Sep 8, 2026
829b1a6
Use normal test integration for integer arithmetic propagators
Sep 9, 2026
e671332
Remove development records before squash merge
Sep 9, 2026
11cc8c3
Reuse product grouping across bounds propagation
Sep 10, 2026
672f0f2
Rewrite small products using existing arithmetic and equality propaga…
Sep 10, 2026
4820e52
Keep fixpoint checks for ordinary GCD and modular products
Sep 10, 2026
4052487
Report arithmetic regression scenarios and remove obsolete cutoff tests
Sep 10, 2026
6b3e1e7
Document arithmetic actor state and lifecycle declarations
Sep 10, 2026
3d0fbd0
Remove PR-only review and benchmark artifacts before merge
Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -2144,6 +2144,8 @@ gecode/int/count$(OBJSUFFIX) gecode/int/count$(SBJSUFFIX): \
./gecode/support/sort.hpp ./gecode/support/static-stack.hpp ./gecode/support/thread.hpp \
./gecode/support/thread/thread.hpp ./gecode/support/timer.hpp
gecode/int/arithmetic$(OBJSUFFIX) gecode/int/arithmetic$(SBJSUFFIX): \
./gecode/int/arithmetic/gcd.hpp ./gecode/int/arithmetic/divides.hpp \
./gecode/int/arithmetic/product.hpp ./gecode/int/arithmetic/product-mod.hpp \
./gecode/int.hh ./gecode/int/arithmetic.hh ./gecode/int/arithmetic/abs.hpp \
./gecode/int/arithmetic/argmax.hpp ./gecode/int/arithmetic/divmod.hpp ./gecode/int/arithmetic/max.hpp \
./gecode/int/arithmetic/mult.hpp ./gecode/int/arithmetic/nroot.hpp ./gecode/int/arithmetic/pow-ops.hpp \
Expand Down Expand Up @@ -4441,6 +4443,8 @@ gecode/int/branch/chb$(OBJSUFFIX) gecode/int/branch/chb$(SBJSUFFIX): \
./gecode/support/sort.hpp ./gecode/support/static-stack.hpp ./gecode/support/thread.hpp \
./gecode/support/thread/thread.hpp ./gecode/support/timer.hpp
gecode/int/arithmetic/mult$(OBJSUFFIX) gecode/int/arithmetic/mult$(SBJSUFFIX): \
./gecode/int/arithmetic/gcd.hpp ./gecode/int/arithmetic/divides.hpp \
./gecode/int/arithmetic/product.hpp ./gecode/int/arithmetic/product-mod.hpp \
./gecode/int.hh ./gecode/int/arithmetic.hh ./gecode/int/arithmetic/abs.hpp \
./gecode/int/arithmetic/argmax.hpp ./gecode/int/arithmetic/divmod.hpp ./gecode/int/arithmetic/max.hpp \
./gecode/int/arithmetic/mult.hpp ./gecode/int/arithmetic/nroot.hpp ./gecode/int/arithmetic/pow-ops.hpp \
Expand Down
3 changes: 2 additions & 1 deletion Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ INTHDR0 = \
idx-view.hh idx-view.hpp div.hh div.hpp \
exec.hh exec/when.hpp \
arithmetic/abs.hpp arithmetic/max.hpp arithmetic/argmax.hpp \
arithmetic/mult.hpp arithmetic/divmod.hpp \
arithmetic/mult.hpp arithmetic/gcd.hpp arithmetic/divides.hpp \
arithmetic/product.hpp arithmetic/product-mod.hpp arithmetic/divmod.hpp \
arithmetic/pow-ops.hpp arithmetic/pow.hpp arithmetic/nroot.hpp \
bool/or.hpp bool/eq.hpp bool/lq.hpp bool/eqv.hpp bool/base.hpp \
bool/clause.hpp bool/ite.hpp \
Expand Down
13 changes: 13 additions & 0 deletions changelog.in
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ Date: unreleased
[DESCRIPTION]
This is the development changelog for the next Gecode release.

[ENTRY]
Module: int
What: new
Rank: major
[DESCRIPTION]
Add ordinary and reified gcd, n-ary product, and n-ary product_mod
constraints, and reified divides. GCD uses nonnegative results and
gcd(0,0)=0; zero divides zero. Modular products accept a fixed positive
modulus or a variable modulus and use nonnegative Euclidean residues,
including for negative products. All reification modes are supported.
Propagation uses bounds and algebraic reasoning; the propagation-level
argument does not select different consistency strengths.

[RELEASE]
Version: 6.4.0
Date: 2026-07-15
Expand Down
42 changes: 42 additions & 0 deletions docs/integer-number-theory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Integer number theory constraints

These APIs require `<gecode/int.hh>`. They use bounds and algebraic reasoning;
the `IntPropLevel` argument currently does not select different strengths.
They do not promise bounds or domain consistency.

Inside a `Space` constructor, this model has `g=6`, `p=-216`, and `r=1`:

```cpp
IntVar x(*this,-12,-12), y(*this,18,18);
IntVar g(*this,0,18), p(*this,-300,300), r(*this,0,6);
gcd(*this,x,y,g);
product(*this,IntVarArgs({x,y}),p);
product_mod(*this,IntVarArgs({x,y}),7,r);
```

Call `status()` on the space to run propagation. `product_mod` uses the
Euclidean residue: `-216 = -31*7 + 1`. In contrast, Gecode's `mod` uses a
dividend-signed remainder and would return `-6` for `-216 mod 7`.

To require divisibility, pass a true reification variable:

```cpp
BoolVar yes(*this,1,1);
IntVar divisor(*this,6,6), dividend(*this,-30,30);
divides(*this,divisor,dividend,Reify(yes));
```

This relation means that some integer multiplier exists. Zero divides zero,
but does not divide a nonzero value. GCD is always nonnegative, with
`gcd(0,0)=0`. The exact product of an empty array is one; its modular product
is `1 mod m`, which is zero when `m=1`.

Every reified overload supports equivalence (`RM_EQV`), `b` implying the
relation (`RM_IMP`), and the relation implying `b` (`RM_PMI`). For example,
`product_mod(*this,factors,m,result,Reify(enabled,RM_IMP))` requires the
modular relation only when `enabled=1`.

A fixed integer modulus must lie in `1..Int::Limits::max`; an invalid constant
throws `Int::OutOfLimits` even when an implication is inactive. With an
`IntVar` modulus, positivity is part of the reified proposition. Consequently,
an inactive implication does not constrain that modulus or the result.
112 changes: 112 additions & 0 deletions gecode/int.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3031,6 +3031,118 @@ namespace Gecode {
mult(Home home, IntVar x0, IntVar x1, IntVar x2,
IntPropLevel ipl=IPL_DEF);

/** \brief Post propagator for \f$\gcd(x_0,x_1)=x_2\f$
*
* The greatest common divisor is nonnegative, with
* \f$\gcd(0,0)=0\f$. Negative operands are interpreted by absolute
* value. Uses sound bounds and algebraic propagation; bounds consistency
* is not guaranteed. The propagation level \a ipl is currently ignored.
*/
GECODE_INT_EXPORT void
gcd(Home home, IntVar x0, IntVar x1, IntVar x2,
IntPropLevel ipl=IPL_DEF);

/** \brief Post propagator for
* \f$(\gcd(x_0,x_1)=x_2)\leftrightarrow r\f$
*
* Supports all reification modes. The greatest common divisor is
* nonnegative, with \f$\gcd(0,0)=0\f$. Uses conservative algebraic
* entailment and disentailment tests. The propagation level \a ipl is
* currently ignored.
*/
GECODE_INT_EXPORT void
gcd(Home home, IntVar x0, IntVar x1, IntVar x2, Reify r,
IntPropLevel ipl=IPL_DEF);

/** \brief Reify whether \a divisor divides \a dividend
*
* Divisibility means that an integer \f$k\f$ exists such that
* \f$dividend=divisor\cdot k\f$. Consequently, zero divides zero, but
* zero does not divide a nonzero integer. Supports all reification modes.
* Uses bounds and conservative algebraic propagation. The propagation
* level \a ipl is currently ignored.
*/
GECODE_INT_EXPORT void
divides(Home home, IntVar divisor, IntVar dividend, Reify r,
IntPropLevel ipl=IPL_DEF);

/** \brief Constrain \a y to the exact product of the variables in \a x
*
* The product of an empty array is one.
* Uses bounds and algebraic propagation, without guaranteeing bounds
* consistency. The propagation level \a ipl is currently ignored.
* \ingroup TaskModelInt
*/
GECODE_INT_EXPORT void
product(Home home, const IntVarArgs& x, IntVar y,
IntPropLevel ipl=IPL_DEF);

/** \brief Reify whether \a y is the exact product of the variables in \a x
*
* The product of an empty array is one.
* Supports all reification modes using conservative algebraic tests.
* The propagation level \a ipl is currently ignored.
* \ingroup TaskModelInt
*/
GECODE_INT_EXPORT void
product(Home home, const IntVarArgs& x, IntVar y, Reify r,
IntPropLevel ipl=IPL_DEF);

/** \brief Constrain \a y to the product of \a x modulo \a m
*
* The modulus \a m must be positive. The result uses the canonical
* Euclidean residue in the range zero through \a m minus one. The product
* of an empty array is one.
* Unlike mod(), a negative product still has a nonnegative residue.
* Uses bounds and algebraic propagation; \a ipl is currently ignored.
* Throws Int::OutOfLimits if \a m is nonpositive or exceeds Int::Limits::max.
* \ingroup TaskModelInt
*/
GECODE_INT_EXPORT void
product_mod(Home home, const IntVarArgs& x, int m, IntVar y,
IntPropLevel ipl=IPL_DEF);

/** \brief Reify whether \a y is the product of \a x modulo \a m
*
* The modulus \a m must be positive. The result uses the canonical
* Euclidean residue in the range zero through \a m minus one. The product
* of an empty array is one.
* Supports all reification modes using conservative algebraic tests;
* \a ipl is currently ignored.
* Throws Int::OutOfLimits if \a m is nonpositive or exceeds Int::Limits::max,
* even for an inactive implication.
* \ingroup TaskModelInt
*/
GECODE_INT_EXPORT void
product_mod(Home home, const IntVarArgs& x, int m, IntVar y, Reify r,
IntPropLevel ipl=IPL_DEF);

/** \brief Constrain \a y to the product of \a x modulo \a m
*
* The variable modulus \a m is constrained to be positive and \a y uses
* the canonical Euclidean residue, so that \f$0\leq y<m\f$. The product
* of an empty array is one.
* Uses bounds and algebraic propagation; \a ipl is currently ignored.
* \ingroup TaskModelInt
*/
GECODE_INT_EXPORT void
product_mod(Home home, const IntVarArgs& x, IntVar m, IntVar y,
IntPropLevel ipl=IPL_DEF);

/** \brief Reify whether \a y is the product of \a x modulo \a m
*
* The reified proposition includes \f$m>0\f$, the canonical range
* \f$0\leq y<m\f$, and congruence with the product. The product of an
* empty array is one.
* Supports all reification modes using conservative algebraic tests;
* \a ipl is currently ignored. An inactive implication does not constrain
* the modulus or result.
* \ingroup TaskModelInt
*/
GECODE_INT_EXPORT void
product_mod(Home home, const IntVarArgs& x, IntVar m, IntVar y, Reify r,
IntPropLevel ipl=IPL_DEF);

/** \brief Post propagator for \f$x_0\ \mathrm{div}\ x_1=x_2 \land x_0\ \mathrm{mod}\ x_1 = x_3\f$
*
* Supports bounds consistency (\a ipl = IPL_BND, default).
Expand Down
150 changes: 150 additions & 0 deletions gecode/int/arithmetic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,156 @@ namespace Gecode {
}
}

void
gcd(Home home, IntVar x0, IntVar x1, IntVar x2, IntPropLevel) {
using namespace Int;
GECODE_POST;
GECODE_ES_FAIL(Arithmetic::Gcd::post(home,x0,x1,x2));
}

void
gcd(Home home, IntVar x0, IntVar x1, IntVar x2, Reify r,
IntPropLevel) {
using namespace Int;
GECODE_POST;
switch (r.mode()) {
case RM_EQV:
GECODE_ES_FAIL((Arithmetic::ReGcd<RM_EQV>
::post(home,x0,x1,x2,r.var())));
break;
case RM_IMP:
GECODE_ES_FAIL((Arithmetic::ReGcd<RM_IMP>
::post(home,x0,x1,x2,r.var())));
break;
case RM_PMI:
GECODE_ES_FAIL((Arithmetic::ReGcd<RM_PMI>
::post(home,x0,x1,x2,r.var())));
break;
default: GECODE_NEVER;
}
}

void
divides(Home home, IntVar divisor, IntVar dividend, Reify r,
IntPropLevel) {
using namespace Int;
GECODE_POST;
switch (r.mode()) {
case RM_EQV:
GECODE_ES_FAIL((Arithmetic::ReDivides<RM_EQV>
::post(home,divisor,dividend,r.var())));
break;
case RM_IMP:
GECODE_ES_FAIL((Arithmetic::ReDivides<RM_IMP>
::post(home,divisor,dividend,r.var())));
break;
case RM_PMI:
GECODE_ES_FAIL((Arithmetic::ReDivides<RM_PMI>
::post(home,divisor,dividend,r.var())));
break;
default: GECODE_NEVER;
}
}

void
product(Home home, const IntVarArgs& x, IntVar y, IntPropLevel) {
using namespace Int;
GECODE_POST;
ViewArray<IntView> xv(home,x);
GECODE_ES_FAIL(Arithmetic::Product::post(home,xv,y));
}

void
product(Home home, const IntVarArgs& x, IntVar y, Reify r,
IntPropLevel) {
using namespace Int;
GECODE_POST;
ViewArray<IntView> xv(home,x);
switch (r.mode()) {
case RM_EQV:
GECODE_ES_FAIL((Arithmetic::ReProduct<RM_EQV>
::post(home,xv,y,r.var())));
break;
case RM_IMP:
GECODE_ES_FAIL((Arithmetic::ReProduct<RM_IMP>
::post(home,xv,y,r.var())));
break;
case RM_PMI:
GECODE_ES_FAIL((Arithmetic::ReProduct<RM_PMI>
::post(home,xv,y,r.var())));
break;
default: GECODE_NEVER;
}
}

void
product_mod(Home home, const IntVarArgs& x, int m, IntVar y,
IntPropLevel) {
using namespace Int;
Limits::positive(m,"Int::product_mod");
GECODE_POST;
IntView yv(y);
ViewArray<IntView> xv(home,x);
GECODE_ES_FAIL(Arithmetic::ProductMod::post(home,xv,m,yv));
}

void
product_mod(Home home, const IntVarArgs& x, int m, IntVar y, Reify r,
IntPropLevel) {
using namespace Int;
Limits::positive(m,"Int::product_mod");
GECODE_POST;
IntView yv(y);
ViewArray<IntView> xv(home,x);
switch (r.mode()) {
case RM_EQV:
GECODE_ES_FAIL((Arithmetic::ReProductMod<RM_EQV>
::post(home,xv,m,yv,r.var())));
break;
case RM_IMP:
GECODE_ES_FAIL((Arithmetic::ReProductMod<RM_IMP>
::post(home,xv,m,yv,r.var())));
break;
case RM_PMI:
GECODE_ES_FAIL((Arithmetic::ReProductMod<RM_PMI>
::post(home,xv,m,yv,r.var())));
break;
default: GECODE_NEVER;
}
}

void
product_mod(Home home, const IntVarArgs& x, IntVar m, IntVar y,
IntPropLevel) {
using namespace Int;
GECODE_POST;
ViewArray<IntView> xv(home,x);
GECODE_ES_FAIL(Arithmetic::ProductModVar::post(home,xv,m,y));
}

void
product_mod(Home home, const IntVarArgs& x, IntVar m, IntVar y, Reify r,
IntPropLevel) {
using namespace Int;
GECODE_POST;
ViewArray<IntView> xv(home,x);
switch (r.mode()) {
case RM_EQV:
GECODE_ES_FAIL((Arithmetic::ReProductModVar<RM_EQV>
::post(home,xv,m,y,r.var())));
break;
case RM_IMP:
GECODE_ES_FAIL((Arithmetic::ReProductModVar<RM_IMP>
::post(home,xv,m,y,r.var())));
break;
case RM_PMI:
GECODE_ES_FAIL((Arithmetic::ReProductModVar<RM_PMI>
::post(home,xv,m,y,r.var())));
break;
default: GECODE_NEVER;
}
}


void
divmod(Home home, IntVar x0, IntVar x1, IntVar x2, IntVar x3,
Expand Down
Loading
Loading