Theory Voblint_Analysis_Interval.Interval_Arithmetic
theory Interval_Arithmetic
imports Interval_Warrowing
begin
section ‹Adding, subtracting and multiplying whole ranges›
text ‹
Addition and subtraction of intervals move the two endpoints independently:
the sum of ‹[a, b]› and ‹[c, d]› runs from ‹a + c› to ‹b + d›. Multiplication
cannot, because a negative factor swaps which endpoint is larger, so
‹ivl_times_core› multiplies all four corner pairs and keeps their minimum and
maximum. Every operation first ‹normalize_ivl›s its arguments --- an interval
whose lower bound exceeds its upper one denotes no number at all and collapses
to ‹bot› --- which is what keeps an unreachable operand from producing a
spurious range.
Each operation is proved sound (concrete values drawn from the operands land
in the computed interval) and monotone (widening either operand widens the
result). Those two facts are what ‹Interval_Backward› later needs to
interpret the shared ‹sound_arith_ops› locale at Interval.
›
instantiation ivl :: plus begin
fun plus_ivl :: "ivl => ivl => ivl" where
"plus_ivl (Ivl l1 u1) (Ivl l2 u2) =
(case (normalize_ivl (Ivl l1 u1), normalize_ivl (Ivl l2 u2)) of
(Ivl a b, Ivl c d) ⇒ normalize_ivl (Ivl (a + c) (b + d)))"
instance ..
end
instantiation ivl :: minus begin
fun minus_ivl :: "ivl => ivl => ivl" where
"minus_ivl (Ivl l1 u1) (Ivl l2 u2) =
(case (normalize_ivl (Ivl l1 u1), normalize_ivl (Ivl l2 u2)) of
(Ivl a b, Ivl c d) ⇒ normalize_ivl (Ivl (a - d) (b - c)))"
instance ..
end
text ‹
‹ivl_min›/‹ivl_max› abstract the two-argument ‹Min›/‹Max› special calls:
componentwise ‹min›/‹max› of the bounds, exact whenever both operands are
non-empty (‹min›/‹max› of two non-empty ranges is again non-empty, unlike
multiplication's corner case), normalized like ‹plus_ivl›/‹minus_ivl› so an
empty operand still collapses to the canonical @{const bot}.
›
fun ivl_min :: "ivl => ivl => ivl" where
"ivl_min (Ivl l1 u1) (Ivl l2 u2) = normalize_ivl (Ivl (min l1 l2) (min u1 u2))"
fun ivl_max :: "ivl => ivl => ivl" where
"ivl_max (Ivl l1 u1) (Ivl l2 u2) = normalize_ivl (Ivl (max l1 l2) (max u1 u2))"
lemma ivl_min_sound:
assumes "i ∈ gamma_ivl a" "j ∈ gamma_ivl b"
shows "min i j ∈ gamma_ivl (ivl_min a b)"
using assms
by (cases a; cases b)
(auto simp: normalize_ivl_gamma Fin_min intro: eint_min_mono)
lemma ivl_max_sound:
assumes "i ∈ gamma_ivl a" "j ∈ gamma_ivl b"
shows "max i j ∈ gamma_ivl (ivl_max a b)"
using assms
by (cases a; cases b)
(auto simp: normalize_ivl_gamma Fin_max intro: eint_max_mono)
lemma ivl_min_mono1: "a1 ≤ a2 ⟹ ivl_min a1 b ≤ ivl_min a2 (b::ivl)"
proof -
assume le: "a1 ≤ a2"
obtain x1 x2 where a1: "a1 = Ivl x1 x2" by (rule ivl_exhaustE)
obtain x1a x2a where a2: "a2 = Ivl x1a x2a" by (rule ivl_exhaustE)
obtain x1b x2b where b: "b = Ivl x1b x2b" by (rule ivl_exhaustE)
from le a1 a2 have h: "eint_le x1a x1" "eint_le x2 x2a"
by (simp_all add: less_eq_ivl_def)
have "Ivl (min x1 x1b) (min x2 x2b) ≤ Ivl (min x1a x1b) (min x2a x2b)"
unfolding less_eq_ivl_def using h by (auto intro: eint_min_mono)
then show ?thesis unfolding a1 a2 b ivl_min.simps by (rule normalize_ivl_mono)
qed
lemma ivl_min_mono2: "b1 ≤ b2 ⟹ ivl_min a b1 ≤ ivl_min a (b2::ivl)"
proof -
assume le: "b1 ≤ b2"
obtain x1 x2 where a: "a = Ivl x1 x2" by (rule ivl_exhaustE)
obtain x1a x2a where b1: "b1 = Ivl x1a x2a" by (rule ivl_exhaustE)
obtain x1b x2b where b2: "b2 = Ivl x1b x2b" by (rule ivl_exhaustE)
from le b1 b2 have h: "eint_le x1b x1a" "eint_le x2a x2b"
by (simp_all add: less_eq_ivl_def)
have "Ivl (min x1 x1a) (min x2 x2a) ≤ Ivl (min x1 x1b) (min x2 x2b)"
unfolding less_eq_ivl_def using h by (auto intro: eint_min_mono)
then show ?thesis unfolding a b1 b2 ivl_min.simps by (rule normalize_ivl_mono)
qed
lemma ivl_min_combine_mono:
"⟦a1 ≤ a2; b1 ≤ b2⟧ ⟹ ivl_min a1 b1 ≤ ivl_min a2 (b2::ivl)"
by (meson order.trans ivl_min_mono1 ivl_min_mono2)
lemma ivl_max_mono1: "a1 ≤ a2 ⟹ ivl_max a1 b ≤ ivl_max a2 (b::ivl)"
proof -
assume le: "a1 ≤ a2"
obtain x1 x2 where a1: "a1 = Ivl x1 x2" by (rule ivl_exhaustE)
obtain x1a x2a where a2: "a2 = Ivl x1a x2a" by (rule ivl_exhaustE)
obtain x1b x2b where b: "b = Ivl x1b x2b" by (rule ivl_exhaustE)
from le a1 a2 have h: "eint_le x1a x1" "eint_le x2 x2a"
by (simp_all add: less_eq_ivl_def)
have "Ivl (max x1 x1b) (max x2 x2b) ≤ Ivl (max x1a x1b) (max x2a x2b)"
unfolding less_eq_ivl_def using h by (auto intro: eint_max_mono)
then show ?thesis unfolding a1 a2 b ivl_max.simps by (rule normalize_ivl_mono)
qed
lemma ivl_max_mono2: "b1 ≤ b2 ⟹ ivl_max a b1 ≤ ivl_max a (b2::ivl)"
proof -
assume le: "b1 ≤ b2"
obtain x1 x2 where a: "a = Ivl x1 x2" by (rule ivl_exhaustE)
obtain x1a x2a where b1: "b1 = Ivl x1a x2a" by (rule ivl_exhaustE)
obtain x1b x2b where b2: "b2 = Ivl x1b x2b" by (rule ivl_exhaustE)
from le b1 b2 have h: "eint_le x1b x1a" "eint_le x2a x2b"
by (simp_all add: less_eq_ivl_def)
have "Ivl (max x1 x1a) (max x2 x2a) ≤ Ivl (max x1 x1b) (max x2 x2b)"
unfolding less_eq_ivl_def using h by (auto intro: eint_max_mono)
then show ?thesis unfolding a b1 b2 ivl_max.simps by (rule normalize_ivl_mono)
qed
lemma ivl_max_combine_mono:
"⟦a1 ≤ a2; b1 ≤ b2⟧ ⟹ ivl_max a1 b1 ≤ ivl_max a2 (b2::ivl)"
by (meson order.trans ivl_max_mono1 ivl_max_mono2)
lemma int_mult_in_corners_lo:
fixes l1 u1 l2 u2 i j :: int
assumes "l1 ≤ i" "i ≤ u1" "l2 ≤ j" "j ≤ u2"
shows "min (l1*l2) (min (l1*u2) (min (u1*l2) (u1*u2))) ≤ i*j"
proof -
consider (A) "0 ≤ i" "0 ≤ l2"
| (B) "0 ≤ i" "l2 < 0"
| (C) "i < 0" "0 ≤ u2"
| (D) "i < 0" "u2 < 0"
by linarith
then show ?thesis
proof cases
case A
have "l1*l2 ≤ i*l2"
using assms(1) A(2)
by (simp add: mult_right_mono)
also have "… ≤ i*j"
by (simp add: A(1) assms(3) mult_left_mono)
finally show ?thesis by simp
next
case B
have "u1*l2 ≤ i*l2"
using assms(2) B(2) by simp
also have "… ≤ i*j"
using assms(3) B(1) by (rule mult_left_mono)
finally show ?thesis by simp
next
case C
have "l1*u2 ≤ i*u2"
using assms(1) C(2) by (rule mult_right_mono)
also have "… ≤ i*j"
using assms(4) C(1) by simp
finally show ?thesis by simp
next
case D
have "u1*u2 ≤ i*u2"
using assms(2) D(2) by simp
also have "… ≤ i*j"
using assms(4) D(1) by simp
finally show ?thesis by simp
qed
qed
lemma int_mult_in_corners_hi:
fixes l1 u1 l2 u2 i j :: int
assumes "l1 ≤ i" "i ≤ u1" "l2 ≤ j" "j ≤ u2"
shows "i * j ≤ max (l1*l2) (max (l1*u2) (max (u1*l2) (u1*u2)))"
proof -
have negated:
"min ((-u1)*l2) (min ((-u1)*u2) (min ((-l1)*l2) ((-l1)*u2)))
≤ (-i)*j"
by (rule int_mult_in_corners_lo) (use assms in simp_all)
from negated show ?thesis
by (simp add: min_def max_def; linarith)
qed
text ‹
Precise multiplication on non-empty intervals. For all-finite operands the
product range is the min / max over the four corner products (the box product
attains its extrema at the corners); any infinite bound falls back to
@{const ivl_top}. An empty operand yields @{term bot} -- the proper bottom
handling that keeps the operator monotone (mapping empties to @{const ivl_top}
would destroy the ordering).
›
fun ivl_times_core :: "ivl => ivl => ivl" where
"ivl_times_core (Ivl (Fin l1) (Fin u1)) (Ivl (Fin l2) (Fin u2)) =
Ivl (Fin (min (l1*l2) (min (l1*u2) (min (u1*l2) (u1*u2)))))
(Fin (max (l1*l2) (max (l1*u2) (max (u1*l2) (u1*u2)))))"
| "ivl_times_core _ _ = ivl_top"
instantiation ivl :: times begin
definition times_ivl :: "ivl => ivl => ivl" where
"times_ivl a b = (if ivl_nonempty a ∧ ivl_nonempty b then ivl_times_core a b else bot)"
instance ..
end
text ‹
‹times_ivl_def› is deliberately not ‹[simp]›: its conditional body would make
‹simp› split on ‹ivl_nonempty› before ‹ivl_times_sound› or ‹ivl_times_mono›
can fire. A proof that must unfold ‹*› directly adds it explicitly.
›
lemma ivl_plus_sound:
assumes "i ∈ gamma_ivl a" "j ∈ gamma_ivl b"
shows "i + j ∈ gamma_ivl (a + b)"
proof (cases a; cases b)
fix l1 u1 l2 u2 :: eint
assume "a = Ivl l1 u1" "b = Ivl l2 u2"
with assms have bnds:
"eint_le l1 (Fin i)" "eint_le (Fin i) u1"
"eint_le l2 (Fin j)" "eint_le (Fin j) u2"
by auto
show "i + j ∈ gamma_ivl (a + b)"
unfolding ‹a = Ivl l1 u1› ‹b = Ivl l2 u2›
using bnds normalize_ivl_def
by (cases l1; cases l2; cases u1; cases u2) auto
qed
lemma ivl_minus_sound:
assumes "i ∈ gamma_ivl a" "j ∈ gamma_ivl b"
shows "i - j ∈ gamma_ivl (a - b)"
proof (cases a; cases b)
fix l1 u1 l2 u2 :: eint
assume "a = Ivl l1 u1" "b = Ivl l2 u2"
with assms have bnds:
"eint_le l1 (Fin i)" "eint_le (Fin i) u1"
"eint_le l2 (Fin j)" "eint_le (Fin j) u2"
by auto
show "i - j ∈ gamma_ivl (a - b)"
unfolding ‹a = Ivl l1 u1› ‹b = Ivl l2 u2›
using bnds normalize_ivl_def
by (cases l1; cases l2; cases u1; cases u2) auto
qed
lemma ivl_times_sound:
assumes "i ∈ gamma_ivl a" "j ∈ gamma_ivl b"
shows "i * j ∈ gamma_ivl (a * b)"
proof -
from assms have ne: "ivl_nonempty a" "ivl_nonempty b"
by (auto intro: gamma_ivl_nonempty)
hence eq: "a * b = ivl_times_core a b" by (simp add: times_ivl_def)
show ?thesis
proof (cases a; cases b)
fix l1 u1 l2 u2 assume ab: "a = Ivl l1 u1" "b = Ivl l2 u2"
show ?thesis
proof (cases l1; cases u1; cases l2; cases u2)
fix n1 m1 n2 m2 assume fin: "l1 = Fin n1" "u1 = Fin m1" "l2 = Fin n2" "u2 = Fin m2"
from assms ab fin have bnds: "n1 ≤ i" "i ≤ m1" "n2 ≤ j" "j ≤ m2" by auto
show ?thesis using eq ab fin
int_mult_in_corners_lo[OF bnds] int_mult_in_corners_hi[OF bnds] by simp
qed (use eq ab ne(1) ne(2) in ‹simp_all add: ivl_top_def›)
qed
qed
text ‹Both operators route their operands through \<^const>‹normalize_ivl›; these rewrites
expose that shape without the constructor-pattern gate, so monotonicity factors through
\<^const>‹normalize_ivl›'s monotonicity and the pointwise \<^typ>‹eint› bounds.›
lemma plus_ivl_norm:
"a + b = (case (normalize_ivl a, normalize_ivl b) of
(Ivl l1 u1, Ivl l2 u2) ⇒ normalize_ivl (Ivl (l1 + l2) (u1 + u2)))"
by (cases a; cases b) simp
lemma minus_ivl_norm:
"a - b = (case (normalize_ivl a, normalize_ivl b) of
(Ivl l1 u1, Ivl l2 u2) ⇒ normalize_ivl (Ivl (l1 - u2) (u1 - l2)))"
by (cases a; cases b) simp
lemma ivl_plus_mono:
assumes "a1 ≤ a2" "b1 ≤ b2" shows "a1 + b1 ≤ a2 + (b2::ivl)"
proof -
obtain pa qa where na1: "normalize_ivl a1 = Ivl pa qa" by (rule ivl_exhaustE)
obtain ra sa where na2: "normalize_ivl a2 = Ivl ra sa" by (rule ivl_exhaustE)
obtain pb qb where nb1: "normalize_ivl b1 = Ivl pb qb" by (rule ivl_exhaustE)
obtain rb sb where nb2: "normalize_ivl b2 = Ivl rb sb" by (rule ivl_exhaustE)
from normalize_ivl_mono[OF assms(1)] na1 na2 have A: "Ivl pa qa ≤ Ivl ra sa" by simp
from normalize_ivl_mono[OF assms(2)] nb1 nb2 have B: "Ivl pb qb ≤ Ivl rb sb" by simp
have "Ivl (pa + pb) (qa + qb) ≤ Ivl (ra + rb) (sa + sb)"
using A B by (auto simp: less_eq_ivl_def eint_plus_mono)
moreover have "a1 + b1 = normalize_ivl (Ivl (pa + pb) (qa + qb))"
by (simp add: plus_ivl_norm na1 nb1)
moreover have "a2 + b2 = normalize_ivl (Ivl (ra + rb) (sa + sb))"
by (simp add: plus_ivl_norm na2 nb2)
ultimately show ?thesis by (metis normalize_ivl_mono)
qed
lemma ivl_minus_mono:
assumes "a1 ≤ a2" "b1 ≤ b2" shows "a1 - b1 ≤ a2 - (b2::ivl)"
proof -
obtain pa qa where na1: "normalize_ivl a1 = Ivl pa qa" by (rule ivl_exhaustE)
obtain ra sa where na2: "normalize_ivl a2 = Ivl ra sa" by (rule ivl_exhaustE)
obtain pb qb where nb1: "normalize_ivl b1 = Ivl pb qb" by (rule ivl_exhaustE)
obtain rb sb where nb2: "normalize_ivl b2 = Ivl rb sb" by (rule ivl_exhaustE)
from normalize_ivl_mono[OF assms(1)] na1 na2 have A: "Ivl pa qa ≤ Ivl ra sa" by simp
from normalize_ivl_mono[OF assms(2)] nb1 nb2 have B: "Ivl pb qb ≤ Ivl rb sb" by simp
have "Ivl (pa - qb) (qa - pb) ≤ Ivl (ra - sb) (sa - rb)"
using A B by (auto simp: less_eq_ivl_def eint_minus_mono)
moreover have "a1 - b1 = normalize_ivl (Ivl (pa - qb) (qa - pb))"
by (simp add: minus_ivl_norm na1 nb1)
moreover have "a2 - b2 = normalize_ivl (Ivl (ra - sb) (sa - rb))"
by (simp add: minus_ivl_norm na2 nb2)
ultimately show ?thesis by (metis normalize_ivl_mono)
qed
text ‹
Monotonicity of the precise corner product. Over a larger box the corner
minimum can only drop and the corner maximum can only rise (each old corner is
a point of the new box), so widening either operand widens the product.
›
lemma corner_min_mono:
fixes a1 b1 c1 d1 a2 b2 c2 d2 :: int
assumes "a2 ≤ a1" "b1 ≤ b2" "c2 ≤ c1" "d1 ≤ d2" "a1 ≤ b1" "c1 ≤ d1"
shows "min (a2*c2) (min (a2*d2) (min (b2*c2) (b2*d2)))
≤ min (a1*c1) (min (a1*d1) (min (b1*c1) (b1*d1)))"
proof -
let ?M = "min (a2*c2) (min (a2*d2) (min (b2*c2) (b2*d2)))"
have d1: "a1 ≤ b2" "c1 ≤ d2" "a2 ≤ b1" "c2 ≤ d1" using assms by auto
have "?M ≤ a1*c1" by (rule int_mult_in_corners_lo[OF assms(1) d1(1) assms(3) d1(2)])
moreover have "?M ≤ a1*d1" by (rule int_mult_in_corners_lo[OF assms(1) d1(1) d1(4) assms(4)])
moreover have "?M ≤ b1*c1" by (rule int_mult_in_corners_lo[OF d1(3) assms(2) assms(3) d1(2)])
moreover have "?M ≤ b1*d1" by (rule int_mult_in_corners_lo[OF d1(3) assms(2) d1(4) assms(4)])
ultimately show ?thesis by simp
qed
lemma corner_max_mono:
fixes a1 b1 c1 d1 a2 b2 c2 d2 :: int
assumes "a2 ≤ a1" "b1 ≤ b2" "c2 ≤ c1" "d1 ≤ d2" "a1 ≤ b1" "c1 ≤ d1"
shows "max (a1*c1) (max (a1*d1) (max (b1*c1) (b1*d1)))
≤ max (a2*c2) (max (a2*d2) (max (b2*c2) (b2*d2)))"
proof -
let ?M = "max (a2*c2) (max (a2*d2) (max (b2*c2) (b2*d2)))"
have d1: "a1 ≤ b2" "c1 ≤ d2" "a2 ≤ b1" "c2 ≤ d1" using assms by auto
have "a1*c1 ≤ ?M" by (rule int_mult_in_corners_hi[OF assms(1) d1(1) assms(3) d1(2)])
moreover have "a1*d1 ≤ ?M" by (rule int_mult_in_corners_hi[OF assms(1) d1(1) d1(4) assms(4)])
moreover have "b1*c1 ≤ ?M" by (rule int_mult_in_corners_hi[OF d1(3) assms(2) assms(3) d1(2)])
moreover have "b1*d1 ≤ ?M" by (rule int_mult_in_corners_hi[OF d1(3) assms(2) d1(4) assms(4)])
ultimately show ?thesis by simp
qed
lemma ivl_times_core_top:
"¬ (∃la ua lb ub. a = Ivl (Fin la) (Fin ua) ∧ b = Ivl (Fin lb) (Fin ub))
⟹ ivl_times_core a b = ivl_top"
by (cases "(a,b)" rule: ivl_times_core.cases) auto
lemma ivl_nonempty_le_fin:
assumes "ivl_nonempty a" "a ≤ Ivl (Fin l2) (Fin u2)"
shows "∃l1 u1. a = Ivl (Fin l1) (Fin u1)"
proof (cases a)
case (Ivl l u)
with assms show ?thesis by (cases l; cases u; auto simp: less_eq_ivl_def)
qed
lemma ivl_times_core_mono:
assumes ne: "ivl_nonempty a1" "ivl_nonempty b1"
and le: "a1 ≤ a2" "b1 ≤ b2"
shows "ivl_times_core a1 b1 ≤ ivl_times_core a2 b2"
proof (cases "∃la ua lb ub. a2 = Ivl (Fin la) (Fin ua) ∧ b2 = Ivl (Fin lb) (Fin ub)")
case False
then have "ivl_times_core a2 b2 = ivl_top" by (rule ivl_times_core_top)
thus ?thesis by (simp add: ivl_le_top)
next
case True
then obtain la2 ua2 lb2 ub2 where
a2: "a2 = Ivl (Fin la2) (Fin ua2)" and b2: "b2 = Ivl (Fin lb2) (Fin ub2)" by blast
from ivl_nonempty_le_fin[OF ne(1)] le(1) a2 obtain la1 ua1 where
a1: "a1 = Ivl (Fin la1) (Fin ua1)" by metis
from ivl_nonempty_le_fin[OF ne(2)] le(2) b2 obtain lb1 ub1 where
b1: "b1 = Ivl (Fin lb1) (Fin ub1)" by metis
from le a1 a2 b1 b2 have ord:
"la2 ≤ la1" "ua1 ≤ ua2" "lb2 ≤ lb1" "ub1 ≤ ub2"
by (auto simp: less_eq_ivl_def)
from ne a1 b1 have nemp: "la1 ≤ ua1" "lb1 ≤ ub1" by auto
show ?thesis
unfolding a1 a2 b1 b2 ivl_times_core.simps less_eq_ivl_def
using corner_min_mono[OF ord(1) ord(2) ord(3) ord(4) nemp(1) nemp(2)]
corner_max_mono[OF ord(1) ord(2) ord(3) ord(4) nemp(1) nemp(2)]
by simp
qed
lemma ivl_times_mono:
assumes "a1 ≤ a2" "b1 ≤ b2"
shows "a1 * b1 ≤ a2 * (b2::ivl)"
proof (cases "ivl_nonempty a1 ∧ ivl_nonempty b1")
case False
then have "a1 * b1 = bot" by (auto simp add: times_ivl_def)
thus ?thesis by simp
next
case True
hence ne2: "ivl_nonempty a2" "ivl_nonempty b2"
using assms ivl_nonempty_mono by blast+
have "a1 * b1 = ivl_times_core a1 b1"
using True by (simp add: times_ivl_def)
moreover have "a2 * b2 = ivl_times_core a2 b2"
using ne2 by (simp add: times_ivl_def)
ultimately show ?thesis
using ivl_times_core_mono[OF _ _ assms] True by simp
qed
subsection ‹Truncating division›
text ‹
C division truncates toward zero, so ‹c_div› and ‹c_mod› differ from HOL's
flooring ‹div›/‹mod› on negative operands. These lemmas bound ‹c_mod› by both
operands and show ‹c_div› monotone in the dividend, the facts interval division
rests on.
›
lemma c_mod_modulus_bound:
assumes "b ≠ 0"
shows "abs (c_mod a b) < abs b"
proof -
have "0 ≤ abs a mod abs b" "abs a mod abs b < abs b"
using assms by (auto intro: pos_mod_sign pos_mod_bound)
then show ?thesis unfolding c_mod_abs
by (auto simp: sgn_if abs_mult)
qed
lemma c_mod_dividend_bound:
"abs (c_mod a b) ≤ abs a"
unfolding c_mod_abs
using zmod_le_nonneg_dividend[of "abs a" "abs b"]
by (cases "b = 0") (auto simp: sgn_if abs_mult intro: pos_mod_sign)
lemma c_div_nonneg_divisor:
assumes "0 < b"
shows "c_div a b = (if a < 0 then - ((-a) div b) else a div b)"
using assms by (auto simp: c_div_def sgn_if)
lemma c_div_mono_dividend:
assumes "a1 ≤ a2" "0 < b"
shows "c_div a1 b ≤ c_div a2 b"
proof (cases "a1 < 0 ∧ 0 ≤ a2")
case True
have "c_div a1 b ≤ 0" "0 ≤ c_div a2 b"
using True assms c_div_nonpos(1) c_div_nonneg(1) by auto
then show ?thesis by linarith
next
case False
show ?thesis unfolding c_div_nonneg_divisor[OF assms(2)]
using False assms
by (auto split: if_splits intro: zdiv_mono1)
qed
lemma c_div_antimono_divisor:
assumes "0 ≤ a" "0 < b1" "b1 ≤ b2"
shows "c_div a b2 ≤ c_div a b1"
using assms
by (simp add: c_div_nonneg_divisor zdiv_mono2)
lemma c_div_positive_divisor_bounds:
assumes "0 < l" "l ≤ b" "b ≤ u"
shows "min (c_div a l) (c_div a u) ≤ c_div a b ∧
c_div a b ≤ max (c_div a l) (c_div a u)"
proof (cases "0 ≤ a")
case True
have "c_div a u ≤ c_div a b" "c_div a b ≤ c_div a l"
using assms True by (auto intro: c_div_antimono_divisor)
then show ?thesis by auto
next
case False
have "c_div (-a) u ≤ c_div (-a) b" "c_div (-a) b ≤ c_div (-a) l"
using assms False by (auto intro: c_div_antimono_divisor)
then show ?thesis
by (simp_all add: c_div_neg_dividend min_le_iff_disj le_max_iff_disj)
qed
lemma c_div_positive_corners:
assumes "l ≤ i" "i ≤ u" "0 < c" "c ≤ j" "j ≤ d"
shows "min (c_div l c) (c_div l d) ≤ c_div i j"
and "c_div i j ≤ max (c_div u c) (c_div u d)"
proof -
have "min (c_div l c) (c_div l d) ≤ c_div l j"
using c_div_positive_divisor_bounds[OF assms(3-5), THEN conjunct1] .
also have "c_div l j ≤ c_div i j"
using assms by (auto intro: c_div_mono_dividend)
finally show "min (c_div l c) (c_div l d) ≤ c_div i j" .
have "c_div i j ≤ c_div u j"
using assms by (auto intro: c_div_mono_dividend)
also have "c_div u j ≤ max (c_div u c) (c_div u d)"
using c_div_positive_divisor_bounds[OF assms(3-5), THEN conjunct2] .
finally show "c_div i j ≤ max (c_div u c) (c_div u d)" .
qed
fun ivl_div_positive_core :: "ivl ⇒ ivl ⇒ ivl" where
"ivl_div_positive_core (Ivl (Fin l) (Fin u)) (Ivl (Fin c) (Fin d)) =
(if 0 < c then
Ivl (Fin (min (c_div l c) (c_div l d))) (Fin (max (c_div u c) (c_div u d)))
else ivl_top)"
| "ivl_div_positive_core a b = (if b = Ivl (Fin 1) (Fin 1) then a else ivl_top)"
definition ivl_div_positive :: "ivl ⇒ ivl ⇒ ivl" where
"ivl_div_positive a b =
(if ivl_nonempty a ∧ ivl_nonempty b
then normalize_ivl (ivl_div_positive_core a b) else bot)"
lemma ivl_div_positive_core_sound:
assumes "i ∈ gamma_ivl a" "j ∈ gamma_ivl b"
shows "c_div i j ∈ gamma_ivl (ivl_div_positive_core a b)"
using assms
by (cases "(a, b)" rule: ivl_div_positive_core.cases)
(auto simp: gamma_ivl_top intro: c_div_positive_corners)
lemma ivl_div_positive_sound:
"i ∈ gamma_ivl a ⟹ j ∈ gamma_ivl b ⟹
c_div i j ∈ gamma_ivl (ivl_div_positive a b)"
unfolding ivl_div_positive_def
by (auto simp: normalize_ivl_gamma
intro: ivl_div_positive_core_sound dest: gamma_ivl_nonempty)
lemma ivl_div_positive_core_one:
"ivl_div_positive_core a (Ivl (Fin 1) (Fin 1)) = a"
by (cases "(a, Ivl (Fin 1) (Fin 1))" rule: ivl_div_positive_core.cases) auto
lemma ivl_div_positive_core_top:
"¬ (∃l u c d. a = Ivl (Fin l) (Fin u) ∧ b = Ivl (Fin c) (Fin d) ∧ 0 < c)
⟹ b ≠ Ivl (Fin 1) (Fin 1) ⟹ ivl_div_positive_core a b = ivl_top"
by (cases "(a, b)" rule: ivl_div_positive_core.cases) auto
lemma ivl_div_positive_core_mono:
assumes ne: "ivl_nonempty a1" "ivl_nonempty b1"
and le: "a1 ≤ a2" "b1 ≤ b2"
shows "ivl_div_positive_core a1 b1 ≤ ivl_div_positive_core a2 b2"
proof (cases "b2 = Ivl (Fin 1) (Fin 1)")
case True
have b1: "b1 = Ivl (Fin 1) (Fin 1)"
using ivl_nonempty_le_fin[OF ne(2) le(2)[unfolded True]] le(2) ne(2)
by (auto simp: True less_eq_ivl_def)
show ?thesis using le(1) by (simp add: True b1 ivl_div_positive_core_one)
next
case not_one: False
show ?thesis
proof (cases "∃l u c d. a2 = Ivl (Fin l) (Fin u) ∧ b2 = Ivl (Fin c) (Fin d) ∧ 0 < c")
case False
then show ?thesis using not_one by (simp add: ivl_div_positive_core_top ivl_le_top)
next
case True
then obtain l2 u2 c2 d2 where
a2: "a2 = Ivl (Fin l2) (Fin u2)" and b2: "b2 = Ivl (Fin c2) (Fin d2)"
and pos: "0 < c2" by blast
obtain l1 u1 where a1: "a1 = Ivl (Fin l1) (Fin u1)"
using ivl_nonempty_le_fin[OF ne(1) le(1)[unfolded a2]] by blast
obtain c1 d1 where b1: "b1 = Ivl (Fin c1) (Fin d1)"
using ivl_nonempty_le_fin[OF ne(2) le(2)[unfolded b2]] by blast
have bounds: "l2 ≤ l1" "u1 ≤ u2" "c2 ≤ c1" "d1 ≤ d2" "l1 ≤ u1" "c1 ≤ d1"
using le ne unfolding a1 a2 b1 b2 by (auto simp: less_eq_ivl_def)
have lo:
"min (c_div l2 c2) (c_div l2 d2) ≤ c_div l1 c1"
"min (c_div l2 c2) (c_div l2 d2) ≤ c_div l1 d1"
using bounds pos by (auto intro: c_div_positive_corners(1))
have hi:
"c_div u1 c1 ≤ max (c_div u2 c2) (c_div u2 d2)"
"c_div u1 d1 ≤ max (c_div u2 c2) (c_div u2 d2)"
using bounds pos by (auto intro: c_div_positive_corners(2))
show ?thesis unfolding a1 a2 b1 b2
using lo hi pos bounds by (simp add: less_eq_ivl_def)
qed
qed
lemma ivl_div_positive_mono:
assumes "a1 ≤ a2" "b1 ≤ b2"
shows "ivl_div_positive a1 b1 ≤ ivl_div_positive a2 b2"
using assms
unfolding ivl_div_positive_def
by (auto intro: normalize_ivl_mono ivl_div_positive_core_mono
dest: ivl_nonempty_mono)
definition ivl_positive_part :: "ivl ⇒ ivl" where
"ivl_positive_part a = intersect_ivl a (Ivl (Fin 1) PlusInf)"
definition ivl_zero_part :: "ivl ⇒ ivl" where
"ivl_zero_part a = intersect_ivl a (Ivl (Fin 0) (Fin 0))"
definition ivl_div :: "ivl ⇒ ivl ⇒ ivl" where
"ivl_div a b =
(if ivl_nonempty a ∧ ivl_nonempty b then
ivl_div_positive a (ivl_positive_part b) ⊔
ivl_div_positive (Ivl (Fin 0) (Fin 0) - a)
(ivl_positive_part (Ivl (Fin 0) (Fin 0) - b)) ⊔ ivl_zero_part b
else bot)"
lemma ivl_div_join_left:
"x ∈ gamma_ivl a ⟹ x ∈ gamma_ivl (a ⊔ b)"
using gamma_ivl_mono[OF le_supI1] by blast
lemma ivl_div_join_right:
"x ∈ gamma_ivl b ⟹ x ∈ gamma_ivl (a ⊔ b)"
using gamma_ivl_mono[OF le_supI2] by blast
lemma ivl_div_sound:
assumes "i ∈ gamma_ivl a" "j ∈ gamma_ivl b"
shows "c_div i j ∈ gamma_ivl (ivl_div a b)"
proof -
have ne: "ivl_nonempty a" "ivl_nonempty b"
using assms by (auto dest: gamma_ivl_nonempty)
show ?thesis
proof (cases "j = 0")
case True
then have "c_div i j ∈ gamma_ivl (ivl_zero_part b)"
unfolding ivl_zero_part_def using assms True
by (intro intersect_ivl_gamma) auto
then show ?thesis unfolding ivl_div_def using ne
by (auto intro: ivl_div_join_right)
next
case nz: False
show ?thesis
proof (cases "0 < j")
case True
have "j ∈ gamma_ivl (ivl_positive_part b)"
unfolding ivl_positive_part_def using assms True
by (intro intersect_ivl_gamma) auto
then have "c_div i j ∈ gamma_ivl (ivl_div_positive a (ivl_positive_part b))"
using assms(1) by (auto intro: ivl_div_positive_sound)
then show ?thesis unfolding ivl_div_def using ne
by (auto intro: ivl_div_join_left)
next
case False
have ni: "-i ∈ gamma_ivl (Ivl (Fin 0) (Fin 0) - a)"
using ivl_minus_sound[of 0 _ i a] assms(1) by simp
have nj: "-j ∈ gamma_ivl (ivl_positive_part (Ivl (Fin 0) (Fin 0) - b))"
unfolding ivl_positive_part_def
apply (rule intersect_ivl_gamma)
apply (use ivl_minus_sound[of 0 "Ivl (Fin 0) (Fin 0)" j b] assms(2) in simp)
using False nz by auto
have "c_div i j ∈ gamma_ivl
(ivl_div_positive (Ivl (Fin 0) (Fin 0) - a)
(ivl_positive_part (Ivl (Fin 0) (Fin 0) - b)))"
using ivl_div_positive_sound[OF ni nj]
by (simp add: c_div_neg_dividend c_div_neg_divisor)
then show ?thesis unfolding ivl_div_def using ne
by (auto intro: ivl_div_join_left
ivl_div_join_right)
qed
qed
qed
lemma ivl_div_mono:
assumes "a1 ≤ a2" "b1 ≤ b2"
shows "ivl_div a1 b1 ≤ ivl_div a2 b2"
proof -
have pos: "ivl_positive_part b1 ≤ ivl_positive_part b2"
unfolding ivl_positive_part_def
by (intro intersect_ivl_mono assms order_refl)
have neg_a: "Ivl (Fin 0) (Fin 0) - a1 ≤ Ivl (Fin 0) (Fin 0) - a2"
by (intro ivl_minus_mono assms order_refl)
have neg_b: "ivl_positive_part (Ivl (Fin 0) (Fin 0) - b1) ≤
ivl_positive_part (Ivl (Fin 0) (Fin 0) - b2)"
unfolding ivl_positive_part_def
by (intro intersect_ivl_mono ivl_minus_mono assms order_refl)
have zero: "ivl_zero_part b1 ≤ ivl_zero_part b2"
unfolding ivl_zero_part_def
by (intro intersect_ivl_mono assms order_refl)
have joined:
"ivl_div_positive a1 (ivl_positive_part b1) ⊔
ivl_div_positive (Ivl (Fin 0) (Fin 0) - a1)
(ivl_positive_part (Ivl (Fin 0) (Fin 0) - b1)) ⊔ ivl_zero_part b1 ≤
ivl_div_positive a2 (ivl_positive_part b2) ⊔
ivl_div_positive (Ivl (Fin 0) (Fin 0) - a2)
(ivl_positive_part (Ivl (Fin 0) (Fin 0) - b2)) ⊔ ivl_zero_part b2"
by (intro sup_mono ivl_div_positive_mono assms pos neg_a neg_b zero)
show ?thesis unfolding ivl_div_def
using joined ivl_nonempty_mono assms by auto
qed
subsection ‹Remainder bounds›
text ‹
‹ivl_mod_bound› bounds a remainder by the divisor alone. When the divisor
interval excludes zero, ‹abs (c_mod i j) < abs j› gives a symmetric interval one
inside the largest divisor magnitude; otherwise the bound is ‹ivl_top›.
›
fun ivl_mod_bound :: "ivl ⇒ ivl" where
"ivl_mod_bound (Ivl (Fin l) (Fin u)) =
(if 0 < l ∨ u < 0 then
Ivl (Fin (1 - max (abs l) (abs u))) (Fin (max (abs l) (abs u) - 1))
else ivl_top)"
| "ivl_mod_bound _ = ivl_top"
lemma abs_between_bounds:
fixes l i u :: int
assumes "l ≤ i" "i ≤ u"
shows "abs i ≤ max (abs l) (abs u)"
using assms by (auto simp: abs_if)
lemma ivl_mod_bound_sound:
assumes "j ∈ gamma_ivl b"
shows "c_mod i j ∈ gamma_ivl (ivl_mod_bound b)"
proof (cases b rule: ivl_mod_bound.cases)
case (1 l u)
with assms have bounds: "l ≤ j" "j ≤ u" by simp_all
have magnitude: "abs j ≤ max (abs l) (abs u)"
by (rule abs_between_bounds[OF bounds])
show ?thesis unfolding 1 ivl_mod_bound.simps
using bounds magnitude c_mod_modulus_bound[of j i]
by (auto simp: gamma_ivl_top; linarith)
qed (simp_all add: gamma_ivl_top)
lemma ivl_mod_bound_mono:
assumes "ivl_nonempty b1" "b1 ≤ b2"
shows "ivl_mod_bound b1 ≤ ivl_mod_bound b2"
proof (cases b2 rule: ivl_mod_bound.cases)
case (1 l2 u2)
obtain l1 u1 where b1: "b1 = Ivl (Fin l1) (Fin u1)"
using ivl_nonempty_le_fin[OF assms(1) assms(2)[unfolded 1]] by blast
have bounds: "l2 ≤ l1" "u1 ≤ u2" "l1 ≤ u1"
using assms unfolding 1 b1 by (auto simp: less_eq_ivl_def)
have "max (abs l1) (abs u1) ≤ max (abs l2) (abs u2)"
using abs_between_bounds[of l2 l1 u2] abs_between_bounds[of l2 u1 u2] bounds by auto
then show ?thesis unfolding 1 b1 ivl_mod_bound.simps
using bounds by (auto simp: less_eq_ivl_def ivl_top_def)
qed (simp_all add: ivl_le_top)
lemma ivl_mod_dividend_sound:
assumes "i ∈ gamma_ivl a"
shows "c_mod i j ∈ gamma_ivl (a ⊔ Ivl (Fin 0) (Fin 0))"
proof -
have range: "min 0 i ≤ c_mod i j ∧ c_mod i j ≤ max 0 i"
using c_mod_dividend_bound[of i j] c_mod_nonneg[of i j] c_mod_nonpos[of i j]
by (auto simp: abs_if min_def max_def split: if_splits; linarith)
obtain l u where a: "a = Ivl l u" by (cases a)
show ?thesis using assms range
unfolding a sup_ivl_def
by (cases l; cases u; auto simp: min_def max_def split: if_splits)
qed
definition ivl_mod :: "ivl ⇒ ivl ⇒ ivl" where
"ivl_mod a b =
(if ivl_nonempty a ∧ ivl_nonempty b then
intersect_ivl (a - ivl_div a b * b)
(intersect_ivl (a ⊔ Ivl (Fin 0) (Fin 0)) (ivl_mod_bound b))
else bot)"
lemma ivl_mod_sound:
assumes "i ∈ gamma_ivl a" "j ∈ gamma_ivl b"
shows "c_mod i j ∈ gamma_ivl (ivl_mod a b)"
proof -
have ne: "ivl_nonempty a" "ivl_nonempty b"
using assms by (auto dest: gamma_ivl_nonempty)
have residual: "i - c_div i j * j ∈ gamma_ivl (a - ivl_div a b * b)"
by (intro ivl_minus_sound ivl_times_sound ivl_div_sound assms)
have "c_mod i j ∈ gamma_ivl (a - ivl_div a b * b)"
using residual by (cases "j = 0") (simp_all add: c_mod_def)
then show ?thesis unfolding ivl_mod_def
using assms(1,2) intersect_ivl_gamma ivl_mod_bound_sound ivl_mod_dividend_sound ne(1,2)
by presburger
qed
lemma ivl_mod_mono:
assumes "a1 ≤ a2" "b1 ≤ b2"
shows "ivl_mod a1 b1 ≤ ivl_mod a2 b2"
proof (cases "ivl_nonempty a1 ∧ ivl_nonempty b1")
case False
then show ?thesis unfolding ivl_mod_def by (simp only: False if_False bot_least)
next
case True
have ne: "ivl_nonempty a2" "ivl_nonempty b2"
using True assms ivl_nonempty_mono by blast+
show ?thesis unfolding ivl_mod_def
apply (simp only: True ne simp_thms if_True)
by (intro intersect_ivl_mono ivl_minus_mono ivl_times_mono ivl_div_mono
sup_mono ivl_mod_bound_mono assms order_refl) (use True in auto)
qed
subsection ‹Results are canonical›
text ‹
Every public interval operation returns a canonical representative: a non-empty
interval, or \<^const>‹bot› itself. No operation manufactures a fresh inverted
bound pair such as \<^term>‹Ivl (Fin 2) (Fin 1)›, so structural equality on
results distinguishes exactly the abstract values they denote.
›
lemma normalize_ivl_plus [simp]: "normalize_ivl (a + b) = a + b"
by (simp add: plus_ivl_norm split: ivl.splits prod.splits)
lemma normalize_ivl_minus [simp]: "normalize_ivl (a - b) = a - b"
by (simp add: minus_ivl_norm split: ivl.splits prod.splits)
lemma normalize_ivl_ivl_min [simp]: "normalize_ivl (ivl_min a b) = ivl_min a b"
by (cases a; cases b) simp
lemma normalize_ivl_ivl_max [simp]: "normalize_ivl (ivl_max a b) = ivl_max a b"
by (cases a; cases b) simp
lemma is_bottom_ivl_times_core [simp]:
"¬ is_bottom_ivl (ivl_times_core x y)"
proof (induction x y rule: ivl_times_core.induct)
case (1 l1 u1 l2 u2)
show ?case
by (auto simp: is_bottom_ivl_def min_le_iff_disj le_max_iff_disj)
qed (simp_all add: is_bottom_ivl_def ivl_top_def)
lemma normalize_ivl_times [simp]: "normalize_ivl (a * b) = a * b"
by (cases "ivl_nonempty a ∧ ivl_nonempty b") (auto simp: times_ivl_def)
end