/* This file contains a Cryptol implementation of the arithmetic bitvector abstract domain operations from module What4.Utils.Domain in what4. In addition to the algorithms themselves, this file also contains specifications of correctness for each of the operations. All of the correctness properties can be formally proven (each at some specific bit width) by loading this file in cryptol and entering ":prove". */ module arithdomain where //////////////////////////////////////////////////////////// // Library bit : {i, n} (fin n, n > i) => [n] bit = 1 # (0 : [i]) mask : {i, n} (fin n, n >= i) => [n] mask = 0 # (~ 0 : [i]) /** Checked unsigned addition, asserted not to overflow. */ infixl 80 .+. (.+.) : {n} (fin n) => [n] -> [n] -> [n] x .+. y = if carry x y then error "overflow" else x + y /** Checked unsigned subtraction, asserted not to underflow. */ infixl 80 .-. (.-.) : {n} (fin n) => [n] -> [n] -> [n] x .-. y = if x < y then error "underflow" else x - y /** Minimum of two signed values. */ smin : {a} (SignedCmp a) => a -> a -> a smin x y = if x <$ y then x else y /** Maximum of two signed values. */ smax : {a} (SignedCmp a) => a -> a -> a smax x y = if x >$ y then x else y //////////////////////////////////////////////////////////// type Dom n = { lo : [n], sz : [n] } interval : {n} (fin n) => [n] -> [n] -> Dom n interval l s = { lo = l, sz = s } range : {n} (fin n) => [n] -> [n] -> Dom n range lo hi = interval lo (hi - lo) /** Membership predicate that defines the set of concrete values represented by an abstract domain element. */ mem : {n} (fin n) => Dom n -> [n] -> Bit mem a x = x - a.lo <= a.sz umem : {n} (fin n) => ([n], [n]) -> [n] -> Bit umem (lo, hi) x = lo <= x /\ x <= hi smem : {n} (fin n, n >= 1) => ([n], [n]) -> [n] -> Bit smem (lo, hi) x = lo <=$ x /\ x <=$ hi top : {n} (fin n) => Dom n top = interval 0 (~ 0) singleton : {n} (fin n) => [n] -> Dom n singleton x = interval x 0 isSingleton : {n} (fin n) => Dom n -> Bit isSingleton a = a.sz == 0 ubounds : {n} (fin n) => Dom n -> ([n], [n]) ubounds a = if carry a.lo a.sz then (0, ~0) else (a.lo, a.lo + a.sz) sbounds : {n} (fin n, n >= 1) => Dom n -> ([n], [n]) sbounds a = (lo - delta, hi - delta) where delta = reverse 1 (lo, hi) = ubounds (interval (a.lo + delta) a.sz) /** Nonzero signed values in a domain with the least and greatest reciprocals. Note that this coincides with the greatest and least nonzero values using the unsigned ordering. */ rbounds : {n} (fin n, n >= 1) => Dom n -> ([n], [n]) rbounds a = if a.lo == 0 then (a_hi, 1) else if a_hi == 0 then (-1, a.lo) else if a_hi < a.lo then (-1, 1) else (a_hi, a.lo) where a_hi = a.lo + a.sz overlap : {n} (fin n) => Dom n -> Dom n -> Bit overlap a b = diff <= b.sz \/ carry diff a.sz where diff = a.lo - b.lo // To compute the union of two intervals, we choose representatives of // the endpoints modulo 2^n such that their midpoints are no more than // 2^(n-1) apart. In the code below, am and bm are equal to twice the // midpoints of intervals a and b, respectively. union : {n} (fin n) => Dom n -> Dom n -> Dom n union a b = if cw >= size then top else interval (drop`{2} cl) (drop`{2} cw) where size : [n+2] size = bit`{n} am = 2 * zext a.lo .+. zext a.sz bm = 2 * zext b.lo .+. zext b.sz al' = if am .+. size < bm then zext a.lo .+. size else zext a.lo bl' = if bm .+. size < am then zext b.lo .+. size else zext b.lo ah' = al' .+. zext a.sz bh' = bl' .+. zext b.sz cl = min al' bl' ch = max ah' bh' cw = ch .-. cl //////////////////////////////////////////////////////////// zero_ext : {m, n} (fin m, m >= n) => Dom n -> Dom m zero_ext a = interval (zext lo) (zext (hi .-. lo)) where (lo, hi) = ubounds a sign_ext : {m, n} (fin m, m >= n, n >= 1) => Dom n -> Dom m sign_ext a = interval (sext lo) (zext (hi - lo)) where (lo, hi) = sbounds a concat : {m, n} (fin m, fin n) => Dom m -> Dom n -> Dom (m + n) concat a b = interval (a.lo # lo) (a.sz # sz) where (lo, hi) = ubounds b sz = hi .-. lo shrink : {m, n} (fin m, fin n) => Dom (m + n) -> Dom m shrink a = if b_sz >= size then top else interval (tail b_lo) (tail b_sz) where size : [1 + m] size = bit`{m} b_lo, b_hi, b_sz : [1 + m] b_lo = take`{back=n} (zext a.lo) b_hi = take`{back=n} (zext a.lo .+. zext a.sz) b_sz = b_hi .-. b_lo trunc : {m, n} (fin m, fin n) => Dom (m + n) -> Dom n trunc a = if a.sz > mask`{n} then top else interval (drop`{m} a.lo) (drop`{m} a.sz) //////////////////////////////////////////////////////////// // Arithmetic operations add : {n} (fin n) => Dom n -> Dom n -> Dom n add a b = if carry a.sz b.sz then top else interval (a.lo + b.lo) (a.sz .+. b.sz) neg : {n} (fin n) => Dom n -> Dom n neg a = interval (- (a.lo + a.sz)) a.sz // Turns out, bitwise complement is easy to specify // in this domain also bnot : {n} (fin n) => Dom n -> Dom n bnot a = interval (~ ah) a.sz where ah = a.lo + a.sz mul : {n} (fin n) => Dom n -> Dom n -> Dom n mul a b = if sz >= bit`{n} then top else interval (drop lo) (drop sz) where (lo, hi) = mulRange (zbounds a) (zbounds b) sz = hi - lo zbounds : {n} (fin n) => Dom n -> ([1 + n], [1 + n]) zbounds a = (lo', lo' + zext a.sz) where size : [2 + n] size = bit`{n} lo' = if 2 * zext a.lo .+. zext a.sz >= size then 0b1 # a.lo else 0b0 # a.lo mulRange : {m, n} (fin m, fin n, m >= 1, n >= 1) => ([m], [m]) -> ([n], [n]) -> ([m+n], [m+n]) mulRange (xl, xh) (yl, yh) = (zl, zh) where (xlyl, xlyh) = scaleRange xl (yl, yh) (xhyl, xhyh) = scaleRange xh (yl, yh) zl = smin xlyl xhyl zh = smax xlyh xhyh scaleRange : {m, n} (fin m, fin n, m >= 1, n >= 1) => [m] -> ([n], [n]) -> ([m+n], [m+n]) scaleRange k (lo, hi) = if k <$ 0 then (hi', lo') else (lo', hi') where lo' = sext k * sext lo hi' = sext k * sext hi udiv : {n} (fin n, n >= 1) => Dom n -> Dom n -> Dom n udiv a b = range cl ch where (al, ah) = ubounds a (bl, bh) = ubounds b bl' = max 1 bl // assume that division by 0 does not happen bh' = max 1 bh // assume that division by 0 does not happen cl = al / bh' ch = ah / bl' urem : {n} (fin n, n >= 1) => Dom n -> Dom n -> Dom n urem a b = if ql == qh then range rl rh else interval 0 (bh - 1) where (al, ah) = ubounds a (bl, bh) = ubounds b bl' = max 1 bl // assume that division by 0 does not happen bh' = max 1 bh (ql, rl) = (al / bh', al % bh') (qh, rh) = (ah / bl', ah % bl') // The first argument is an ordinary signed interval, but the second // argument is a reciaprocal interval: The arguments should satisfy 'al // <=$ ah' (signed) and '1/bl <= 1/bh' (signed), or equivalently, 'bh // <= bl' (unsigned). sdivRange : {n} (fin n, n >= 1) => ([n], [n]) -> ([n], [n]) -> ([1+n], [1+n]) sdivRange (al, ah) (bl, bh) = (ql, qh) where (ql1, qh1) = shrinkRange (al, ah) bh (ql2, qh2) = shrinkRange (al, ah) bl ql = smin ql1 ql2 qh = smax qh1 qh2 // Extra bit of output is to handle the 'INTMIN / -1' overflow case. shrinkRange : {n} (fin n, n >= 1) => ([n], [n]) -> [n] -> ([1+n], [1+n]) shrinkRange (lo, hi) k = if k >$ 0 then (lo ./. k, hi ./. k) else if k <$ 0 then (hi ./. k, lo ./. k) else (sext lo, sext hi) where x ./. y = sext x /$ sext y sdiv : {n} (fin n, n >= 1) => Dom n -> Dom n -> Dom n sdiv a b = if sz >= bit`{n} then top else interval (drop lo) (drop sz) where (lo, hi) = sdivRange (sbounds a) (rbounds b) sz = hi - lo srem : {n} (fin n, n >= 1) => Dom n -> Dom n -> Dom n srem a b = if ql == qh then (if ql <$ 0 then range (al - drop ql * bl) (ah - drop ql * bh) else range (al - drop ql * bh) (ah - drop ql * bl)) else range rl rh where (al, ah) = sbounds a (bl, bh) = sbounds b (ql, qh) = sdivRange (al, ah) (rbounds b) rl = if al <$ 0 then smin (bl+1) (-bh+1) else 0 rh = if ah >$ 0 then smax (-bl-1) (bh-1) else 0 //////////////////////////////////////////////////////////// // Shifts shl : {n} (fin n) => Dom n -> Dom n -> Dom n shl a b = if sz > mask`{n} then top else interval (drop lo) (drop sz) where al, ah : [n + 1] (al, ah) = zbounds a bl, bh : [n] (bl, bh) = ubounds b // [n + 2] is enough to avoid signed overflow in shift cl, ch : [n + 2] cl = if bl < `n then 1 << bl else bit`{n} ch = if bh < `n then 1 << bh else bit`{n} (lo, hi) = mulRange (al, ah) (cl, ch) sz = hi - lo lshr : {n} (fin n) => Dom n -> Dom n -> Dom n lshr a b = interval cl (ch - cl) where (al, ah) = ubounds a (bl, bh) = ubounds b cl = al >> bh ch = ah >> bl ashr : {n} (fin n, n >= 1) => Dom n -> Dom n -> Dom n ashr a b = interval cl (ch - cl) where (al, ah) = sbounds a (bl, bh) = ubounds b cl = al >>$ (if al <$ 0 then bl else bh) ch = ah >>$ (if ah <$ 0 then bh else bl) //////////////////////////////////////////////////////////// // Comparisons ult : {n} (fin n) => Dom n -> Dom n -> Bit ult a b = (ubounds a).1 < (ubounds b).0 ule : {n} (fin n) => Dom n -> Dom n -> Bit ule a b = (ubounds a).1 <= (ubounds b).0 slt : {n} (fin n, n >= 1) => Dom n -> Dom n -> Bit slt a b = (sbounds a).1 <$ (sbounds b).0 sle : {n} (fin n, n >= 1) => Dom n -> Dom n -> Bit sle a b = (sbounds a).1 <=$ (sbounds b).0 // A bitmask indicating which bits cannot be determined // given the interval information in the given domain unknowns : {n} (fin n, n >= 1) => Dom n -> [n] unknowns a = if carry a.lo a.sz then ~0 else bits where bits = fillright diff diff = a.lo ^ (a.lo + a.sz) fillright : {n} (fin n, n >= 1) => [n] -> [n] fillright x = tail (scanl (||) False x) fillright_alt : {n} (fin n, n >= 1) => [n] -> [n] fillright_alt x = x || ((1 << lg2 x) - 1) property fillright_equiv x = fillright`{16} x == fillright_alt x //////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// // Correctness properties infix 20 =@= /** Equivalence of bitvector domains. */ (=@=) : {n} (fin n) => Dom n -> Dom n -> Bit a =@= b = (a.sz == ~0 /\ b.sz == ~0) \/ (a == b) infix 5 <==> (<==>) : Bit -> Bit -> Bit (<==>) = (==) //////////////////////////////////////////////////////////// // Soundness properties correct_top : {n} (fin n) => [n] -> Bit correct_top x = mem top x correct_ubounds : {n} (fin n) => Dom n -> [n] -> Bit correct_ubounds a x = mem a x ==> umem (ubounds a) x correct_sbounds : {n} (fin n, n >= 1) => Dom n -> [n] -> Bit correct_sbounds a x = mem a x ==> smem (sbounds a) x correct_singleton : {n} (fin n) => [n] -> [n] -> Bit correct_singleton x y = mem (singleton x) y <==> x == y correct_overlap : {n} (fin n) => Dom n -> Dom n -> [n] -> Bit correct_overlap a b x = mem a x ==> mem b x ==> overlap a b correct_overlap_inv : {n} (fin n) => Dom n -> Dom n -> Bit correct_overlap_inv a b = overlap a b ==> (mem a witness /\ mem b witness) where witness = if mem a b.lo then b.lo else a.lo correct_union : {n} (fin n) => Dom n -> Dom n -> [n] -> Bit correct_union a b x = (mem a x \/ mem b x) ==> mem (union a b) x correct_zero_ext : {m, n} (fin m, m >= n) => Dom n -> [n] -> Bit correct_zero_ext a x = mem a x ==> mem (zero_ext`{m} a) (zext`{m} x) correct_sign_ext : {m, n} (fin m, m >= n, n >= 1) => Dom n -> [n] -> Bit correct_sign_ext a x = mem a x ==> mem (sign_ext`{m} a) (sext`{m} x) correct_concat : {m, n} (fin m, fin n) => Dom m -> Dom n -> [m] -> [n] -> Bit correct_concat a b x y = mem a x ==> mem b y ==> mem (concat a b) (x # y) correct_shrink : {m, n} (fin m, fin n) => Dom (m + n) -> [m + n] -> Bit correct_shrink a x = mem a x ==> mem (shrink`{m} a) (take`{m} x) correct_trunc : {m, n} (fin m, fin n) => Dom (m + n) -> [m + n] -> Bit correct_trunc a x = mem a x ==> mem (trunc`{m} a) (drop`{m} x) correct_add : {n} (fin n) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_add a b x y = mem a x ==> mem b y ==> mem (add a b) (x + y) correct_neg : {n} (fin n) => Dom n -> [n] -> Bit correct_neg a x = mem a x <==> mem (neg a) (- x) correct_mul : {n} (fin n) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_mul a b x y = mem a x ==> mem b y ==> mem (mul a b) (x * y) correct_mulRange : {n} (fin n, n >= 1) => ([n], [n]) -> ([n], [n]) -> [n] -> [n] -> Bit correct_mulRange a b x y = smem a x ==> smem b y ==> smem (mulRange a b) (sext x * sext y) correct_udiv : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_udiv a b x y = mem a x ==> mem b y ==> y != 0 ==> mem (udiv a b) (x / y) correct_urem : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_urem a b x y = mem a x ==> mem b y ==> y != 0 ==> mem (urem a b) (x % y) correct_sdivRange : {n} (fin n, n >= 1) => ([n], [n]) -> ([n], [n]) -> [n] -> [n] -> Bit correct_sdivRange a b x y = smem a x ==> umem b y ==> y != 0 ==> smem (sdivRange a (b.1, b.0)) (sext x /$ sext y) correct_shrinkRange : {n} (fin n, n >= 1) => ([n], [n]) -> [n] -> [n] -> Bit correct_shrinkRange a x y = smem a x ==> y != 0 ==> smem (shrinkRange a y) (sext x /$ sext y) correct_sdiv : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_sdiv a b x y = mem a x ==> mem b y ==> y != 0 ==> mem (sdiv a b) (x /$ y) correct_srem : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_srem a b x y = mem a x ==> mem b y ==> y != 0 ==> mem (srem a b) (x %$ y) correct_shl : {n} (fin n) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_shl a b x y = mem a x ==> mem b y ==> mem (shl a b) (x << y) correct_lshr : {n} (fin n) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_lshr a b x y = mem a x ==> mem b y ==> mem (lshr a b) (x >> y) correct_ashr : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_ashr a b x y = mem a x ==> mem b y ==> mem (ashr a b) (x >>$ y) correct_slt : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_slt a b x y = slt a b ==> mem a x ==> mem b y ==> x <$ y correct_sle : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_sle a b x y = sle a b ==> mem a x ==> mem b y ==> x <=$ y correct_ult : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_ult a b x y = ult a b ==> mem a x ==> mem b y ==> x < y correct_ule : {n} (fin n, n >= 1) => Dom n -> Dom n -> [n] -> [n] -> Bit correct_ule a b x y = ule a b ==> mem a x ==> mem b y ==> x <= y correct_bnot : {n} (fin n) => Dom n -> [n] -> Bit correct_bnot a x = mem a x <==> mem (bnot a) (~ x) correct_isSingleton : {n} (fin n) => Dom n -> Bit correct_isSingleton a = isSingleton a ==> a == singleton a.lo correct_unknowns : {n} (fin n, n >= 1) => Dom n -> [n] -> [n] -> Bit correct_unknowns a x y = mem a x ==> mem a y ==> (x || unknowns a) == (y || unknowns a) property p1 = correct_top`{16} property p2 = correct_ubounds`{16} property p3 = correct_sbounds`{16} property p4 = correct_singleton`{16} property p5 = correct_overlap`{16} property p5_inv = correct_overlap_inv`{16} property p6 = correct_union`{8} property p7 = correct_zero_ext`{32, 16} property p8 = correct_sign_ext`{32, 16} property p9 = correct_concat`{16, 16} property p10 = correct_shrink`{8, 8} property p11 = correct_trunc`{8, 8} property p12 = correct_unknowns`{16} property p13 = correct_isSingleton`{16} property a1 = correct_add`{8} property a2 = correct_neg`{16} property a3 = correct_mul`{4} property a4 = correct_udiv`{8} property a5 = correct_urem`{6} property a6 = correct_sdiv`{6} property a7 = correct_srem`{6} property a8 = correct_bnot`{16} property a9 = correct_sdivRange`{6} property s1 = correct_shl`{8} property s2 = correct_lshr`{8} property s3 = correct_ashr`{8} property o1 = correct_slt`{16} property o2 = correct_sle`{16} property o3 = correct_ult`{16} property o4 = correct_ule`{16} //////////////////////////////////////////////////////////// // Operations preserve singletons singleton_overlap : {n} (fin n) => [n] -> [n] -> Bit singleton_overlap x y = overlap (singleton x) (singleton y) == (x == y) singleton_zero_ext : {m, n} (fin m, m >= n) => [n] -> Bit singleton_zero_ext x = zero_ext`{m} (singleton x) == singleton (zext`{m} x) singleton_sign_ext : {m, n} (fin m, m >= n, n >= 1) => [n] -> Bit singleton_sign_ext x = sign_ext`{m} (singleton x) == singleton (sext`{m} x) singleton_concat : {m, n} (fin m, fin n) => [m] -> [n] -> Bit singleton_concat x y = concat (singleton x) (singleton y) == singleton (x # y) singleton_shrink : {m, n} (fin m, fin n) => [m + n] -> Bit singleton_shrink x = shrink`{m} (singleton x) == singleton (take`{m} x) singleton_trunc : {m, n} (fin m, fin n) => [m + n] -> Bit singleton_trunc x = trunc`{m} (singleton x) == singleton (drop`{m} x) singleton_add : {n} (fin n) => [n] -> [n] -> Bit singleton_add x y = add (singleton x) (singleton y) == singleton (x + y) singleton_neg : {n} (fin n) => [n] -> Bit singleton_neg x = neg (singleton x) == singleton (- x) singleton_mul : {n} (fin n) => [n] -> [n] -> Bit singleton_mul x y = mul (singleton x) (singleton y) == singleton (x * y) singleton_mulRange : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_mulRange x y = mulRange (x, x) (y, y) == (sext x * sext y, sext x * sext y) singleton_udiv : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_udiv x y = y != 0 ==> udiv (singleton x) (singleton y) == singleton (x / y) singleton_urem : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_urem x y = y != 0 ==> urem (singleton x) (singleton y) == singleton (x % y) singleton_sdiv : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_sdiv x y = y != 0 ==> sdiv (singleton x) (singleton y) == singleton (x /$ y) singleton_srem : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_srem x y = y != 0 ==> srem (singleton x) (singleton y) == singleton (x %$ y) singleton_shl : {n} (fin n) => [n] -> [n] -> Bit singleton_shl x y = shl (singleton x) (singleton y) == singleton (x << y) singleton_lshr : {n} (fin n) => [n] -> [n] -> Bit singleton_lshr x y = lshr (singleton x) (singleton y) == singleton (x >> y) singleton_ashr : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_ashr x y = ashr (singleton x) (singleton y) == singleton (x >>$ y) singleton_slt : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_slt x y = slt (singleton x) (singleton y) == (x <$ y) singleton_sle : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_sle x y = sle (singleton x) (singleton y) == (x <=$ y) singleton_ult : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_ult x y = ult (singleton x) (singleton y) == (x < y) singleton_ule : {n} (fin n, n >= 1) => [n] -> [n] -> Bit singleton_ule x y = ule (singleton x) (singleton y) == (x <= y) property i01 = singleton_overlap`{16} property i02 = singleton_zero_ext`{32, 16} property i03 = singleton_sign_ext`{32, 16} property i04 = singleton_concat`{16, 16} property i05 = singleton_shrink`{8, 8} property i06 = singleton_trunc`{8, 8} property i07 = singleton_add`{8} property i08 = singleton_neg`{16} property i09 = singleton_mul`{4} property i10 = singleton_udiv`{8} property i11 = singleton_urem`{8} property i12 = singleton_sdiv`{8} property i13 = singleton_srem`{8} property i14 = singleton_shl`{8} property i15 = singleton_lshr`{8} property i16 = singleton_ashr`{8} property i17 = singleton_slt`{16} property i18 = singleton_sle`{16} property i19 = singleton_ult`{16} property i20 = singleton_ule`{16} //////////////////////////////////////////////////////////// // Associativity/commutativity properties comm_overlap : {n} (fin n) => Dom n -> Dom n -> Bit comm_overlap a b = overlap a b <==> overlap b a comm_add : {n} (fin n) => Dom n -> Dom n -> Bit comm_add a b = add a b == add b a assoc_add : {n} (fin n) => Dom n -> Dom n -> Dom n -> Bit assoc_add a b c = add a (add b c) =@= add (add a b) c comm_mul : {n} (fin n) => Dom n -> Dom n -> Bit comm_mul a b = mul a b == mul b a /* mul is not associative! */ assoc_mul : {n} (fin n) => Dom n -> Dom n -> Dom n -> Bit assoc_mul a b c = mul a (mul b c) =@= mul (mul a b) c comm_mulRange : {i, j} (fin i, fin j, i >= 1, j >= 1) => ([i], [i]) -> ([j], [j]) -> Bit comm_mulRange a b = a.0 <=$ a.1 ==> b.0 <=$ b.1 ==> mulRange a b == mulRange b a assoc_mulRange : {i, j, k} (fin i, fin j, fin k, i >= 1, j >= 1, k >= 1) => ([i], [i]) -> ([j], [j]) -> ([k], [k]) -> Bit assoc_mulRange a b c = a.0 <=$ a.1 ==> b.0 <=$ b.1 ==> c.0 <=$ c.1 ==> mulRange a (mulRange b c) == mulRange (mulRange a b) c property c1 = comm_overlap`{16} property c2 = comm_add`{16} property c3 = assoc_add`{16} property c4 = comm_mul`{4} property c5 = comm_mulRange`{4,4} property c6 = assoc_mulRange`{3,3,3} //////////////////////////////////////////////////////////// // Additional properties about union comm_union : {n} (fin n) => Dom n -> Dom n -> Bit comm_union a b = union a b == union b a /* union is actually not associative! */ assoc_union : {n} (fin n) => Dom n -> Dom n -> Dom n -> Bit assoc_union a b c = union a (union b c) == union (union a b) c /* union always has a lower bound equal to one of the input lower bounds */ lo_union : {n} (fin n) => Dom n -> Dom n -> Bit lo_union a b = union a b == top \/ (union a b).lo == a.lo \/ (union a b).lo == b.lo /* union always has an upper bound equal to one of the input upper bounds */ hi_union : {n} (fin n) => Dom n -> Dom n -> Bit hi_union a b = c == top \/ c_hi == a_hi \/ c_hi == b_hi where c = union a b a_hi = a.lo + a.sz b_hi = b.lo + b.sz c_hi = c.lo + c.sz /* union doesn't return top unless necessary */ nontriv_union : {n} (fin n) => Dom n -> Dom n -> [n] -> Bit nontriv_union a b x = union a b =@= top ==> mem a x \/ mem b x /* union of opposite intervals prefers to exclude zero */ nonzero_union : {n} (fin n, n >= 1) => [n] -> [n] -> Bit nonzero_union lo sz = mem (union a b) half /\ (~ mem a 0 ==> ~ mem b 0 ==> ~ mem (union a b) 0) where half : [n] half = reverse 1 a = interval lo sz b = interval (lo + half) sz property u1 = comm_union`{16} property u2 = lo_union`{16} property u3 = hi_union`{16} property u4 = nontriv_union`{8} property u5 = nonzero_union`{16}