Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use Euclidean division/modulus to express T-division/modulus for SMT …
…hooks (#4616) Fixes #4606 The following reasoning was applied: Given dividend `D` and divisor `d`, let the `q₋` and `r₋` in ``` D = d · qₑ + rₑ # Euclidean division D = d · qₜ + rₜ # T-division ``` be Euclidean and T division results. In terms of K operations, ``` qₑ = D divInt d rₑ = D modInt d qₜ = D /Int d rₜ = D %Int d ``` According to [1], we can express Euclidean division by T-division: ``` qₑ = qₜ − I rₑ = rₜ + I · d where I = if rₜ >= 0 then 0 else if d > 0 then 1 else −1 ``` To transform the equations to isolate `qₜ` and `rₜ`, we need to express the `where` clause without `rₜ`. We use 1. `rₑ = 0 <=> rₜ = 0`, and 2. `rₜ ≠ 0 => sgn(rₜ) = sgn(D)` (as per [1]) to rewrite the first `if` to `if (rₑ = 0 || D > 0) ...`. Now we can transform the equations to isolate `qₜ` and `rₜ`, getting (written with `let`): ``` let I = if (rₑ = 0 || D > 0) then 0 else if d > 0 then 1 else -1 in qₜ = qₑ + I rₜ = rₑ - I · d ``` We need two independent equations, so lifting the shared `I` is not beneficial. we can write two `if` expressions per equation. ``` qₜ = if (rₑ = 0 || D > 0) then qₑ else if d > 0 then qₑ + 1 else qₑ - 1 rₜ = if (rₑ = 0 || D > 0) then rₑ else if d > 0 then rₑ - d else rₑ + d ``` Written as SMT hook expressions (coherent with [2]): * (`#1 /Int #2`): `ite (or (= 0 (mod #1 #2)) (>= #1 0)) (div #1 #2) (ite (> #2 0) (+ (div #1 #2) 1) (- (div #1 #2) 1))` * (`#1 %Int #2`): `ite (or (= 0 (mod #1 #2)) (>= #1 0)) (mod #1 #2) (ite (> #2 0) (- (mod #1 #2) #2) (+ (mod #1 #2) #2))` [1] https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf [2] https://gist.github.com/Rufflewind/a880e03fb0d13644a1e8
- Loading branch information