Skip to content

Commit 1ea3e53

Browse files
committed
add range calculation for euclidean modulo
1 parent 63aa41e commit 1ea3e53

File tree

2 files changed

+64
-4
lines changed
  • trunk/source
    • CACSL2BoogieTranslator/src/de/uni_freiburg/informatik/ultimate/plugins/generator/cacsl2boogietranslator
    • Library-UltimateUtil/src/de/uni_freiburg/informatik/ultimate/util/datastructures

2 files changed

+64
-4
lines changed

trunk/source/CACSL2BoogieTranslator/src/de/uni_freiburg/informatik/ultimate/plugins/generator/cacsl2boogietranslator/Boogie2ACSL.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,10 +451,7 @@ private BacktranslatedExpression translateBinaryExpression(
451451
return lhs;
452452
}
453453
}
454-
final var absRightRange = rightRange.abs();
455-
final var maxRightMinusOne =
456-
absRightRange.getMaxValue() == null ? null : absRightRange.getMaxValue().subtract(BigInteger.ONE);
457-
range = new BigInterval(BigInteger.ZERO, maxRightMinusOne);
454+
range = leftRange.euclideanModulo(rightRange);
458455

459456
// TODO: properly backtranslate from euclidean modulo (mod) to C remainder (%)
460457
operator = Operator.ARITHMOD;

trunk/source/Library-UltimateUtil/src/de/uni_freiburg/informatik/ultimate/util/datastructures/BigInterval.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public class BigInterval {
4141
public BigInterval(final BigInteger minValue, final BigInteger maxValue) {
4242
mMinValue = minValue;
4343
mMaxValue = maxValue;
44+
assert mMinValue == null || mMaxValue == null
45+
|| maxValue.compareTo(minValue) >= 0 : "empty interval not supported";
4446
}
4547

4648
public static BigInterval unbounded() {
@@ -99,6 +101,24 @@ public boolean contains(final BigInteger element) {
99101
return minBoundOk && maxBoundOk;
100102
}
101103

104+
public BigInterval join(final BigInterval other) {
105+
final var minValue = mMinValue == null || other.mMinValue == null ? null : mMinValue.min(other.mMinValue);
106+
final var maxValue = mMaxValue == null || other.mMaxValue == null ? null : mMaxValue.max(other.mMaxValue);
107+
return new BigInterval(minValue, maxValue);
108+
}
109+
110+
public BigInterval intersect(final BigInterval other) {
111+
final var minValue = mMinValue == null ? other.mMinValue
112+
: other.mMinValue == null ? mMinValue : mMinValue.max(other.mMinValue);
113+
final var maxValue = mMaxValue == null ? other.mMaxValue
114+
: other.mMaxValue == null ? mMaxValue : mMaxValue.min(other.mMaxValue);
115+
if (maxValue.compareTo(mMinValue) < 0) {
116+
// empty interval not supported
117+
return null;
118+
}
119+
return new BigInterval(minValue, maxValue);
120+
}
121+
102122
public BigInterval negate() {
103123
final var minValue = mMaxValue == null ? null : mMaxValue.negate();
104124
final var maxValue = mMinValue == null ? null : mMinValue.negate();
@@ -232,4 +252,47 @@ public BigInterval euclideanModulo(final BigInteger divisor) {
232252
// k*divisor-1 (modulo will be divisor-1), so the smallest interval encompassing all values is the entire range.
233253
return new BigInterval(BigInteger.ZERO, absDivisor.subtract(BigInteger.ONE));
234254
}
255+
256+
// Code adapted from https://stackoverflow.com/a/56918042 (there shown for truncating remainder)
257+
public BigInterval euclideanModulo(final BigInterval divisor) {
258+
if (divisor.contains(BigInteger.ZERO)) {
259+
// Modulo resp. division by zero is unspecified, so we assume it can yield any number.
260+
return unbounded();
261+
}
262+
263+
if (divisor.isSingleton()) {
264+
return euclideanModulo(divisor.mMinValue);
265+
}
266+
267+
// For euclidean modulo, divisors D and (- D) yield the same result. So we take the absolute value.
268+
// (https://www.microsoft.com/en-us/research/publication/division-and-modulus-for-computer-scientists/)
269+
final var absDivisor = divisor.abs();
270+
271+
final var length = length();
272+
if (length != null && absDivisor.mMaxValue != null && length.compareTo(absDivisor.mMaxValue) >= 0) {
273+
// If length is greater than the largest possible divisor, the interval contains all possible mod values.
274+
return new BigInterval(BigInteger.ZERO, absDivisor.mMaxValue.subtract(BigInteger.ONE));
275+
}
276+
if (length != null && length.compareTo(absDivisor.mMinValue) >= 0) {
277+
// If the length is somewhere inside the interval of possible divisors, we split into two cases:
278+
// all divisors up to length, and all greater divisors.
279+
// For divisors up to length, we know (as in the previous case) that we can get all possible mod values (for
280+
// the new sub-interval of divisors); for divisors greater than length, we use a recursive call.
281+
// (NOTE that the recursion depth should be limited to 2, as length is outside the new sub-interval.)
282+
final var lowerHalf = new BigInterval(BigInteger.ZERO, length.subtract(BigInteger.ONE));
283+
final var upperHalf = euclideanModulo(new BigInterval(length.add(BigInteger.ONE), absDivisor.mMaxValue));
284+
return lowerHalf.join(upperHalf);
285+
}
286+
if (absDivisor.mMinValue.compareTo(mMaxValue) > 0) {
287+
// trivial case: modulo is guaranteed to have no effect
288+
return this;
289+
}
290+
if (absDivisor.mMaxValue != null && absDivisor.mMaxValue.compareTo(mMaxValue) > 0) {
291+
return new BigInterval(BigInteger.ZERO, mMaxValue);
292+
}
293+
294+
// fallback to imprecise approximation
295+
return new BigInterval(BigInteger.ZERO,
296+
absDivisor.mMaxValue == null ? null : absDivisor.mMaxValue.subtract(BigInteger.ONE));
297+
}
235298
}

0 commit comments

Comments
 (0)