// Finite bags. // (Originally from Dafny Prelude: Copyright (c) Microsoft) // Bag type type Bag T = [T]int; // Bag invariant function Bag#IsValid(b: Bag T): bool; // ints are non-negative, used after havocing, and for conversion from sequences to multisets. axiom (forall b: Bag T :: { Bag#IsValid(b) } Bag#IsValid(b) <==> (forall bx: T :: { b[bx] } 0 <= b[bx])); // Bag size function Bag#Card(Bag T): int; axiom (forall s: Bag T :: { Bag#Card(s) } 0 <= Bag#Card(s)); axiom (forall s: Bag T, x: T, n: int :: { Bag#Card(s[x := n]) } 0 <= n ==> Bag#Card(s[x := n]) == Bag#Card(s) - s[x] + n); // Empty bag function Bag#Empty(): Bag T; axiom (forall o: T :: { Bag#Empty()[o] } Bag#Empty()[o] == 0); axiom (forall s: Bag T :: { Bag#Card(s) } (Bag#Card(s) == 0 <==> s == Bag#Empty()) && (Bag#Card(s) != 0 ==> (exists x: T :: 0 < s[x]))); axiom (forall f: Field (Bag T) :: { Default(f) } Default(f) == Bag#Empty() : Bag T); // Singleton bag function Bag#Singleton(T): Bag T; axiom (forall r: T, o: T :: { Bag#Singleton(r)[o] } (Bag#Singleton(r)[o] == 1 <==> r == o) && (Bag#Singleton(r)[o] == 0 <==> r != o)); axiom (forall r: T :: { Bag#Singleton(r) } Bag#Equal(Bag#Singleton(r), Bag#Extended(Bag#Empty(), r))); // Bag that contains multiple occurrences of the same element function Bag#Multiple(T, int): Bag T; axiom (forall r: T, n: int, o: T :: { Bag#Multiple(r, n)[o] } (Bag#Multiple(r, n)[o] == n <==> r == o) && (Bag#Multiple(r, n)[o] == 0 <==> r != o)); axiom (forall r: T, n: int :: { Bag#Multiple(r, n) } Bag#Equal(Bag#Multiple(r, n), Bag#ExtendedMultiple(Bag#Empty(), r, n))); // Is x contained in b? function {: inline } Bag#Has(b: Bag T, x: T): bool { b[x] > 0 } // Is b empty? function {: inline } Bag#IsEmpty(b: Bag T): bool { Bag#Equal(b, Bag#Empty()) } // Does b contain each element c times? function Bag#IsConstant(b: Bag T, c: int): bool { (forall o: T :: b[o] != 0 ==> b[o] == c )} // Set of values contained in the bag function Bag#Domain(Bag T): Set T; axiom (forall b: Bag T, o: T :: { Bag#Domain(b)[o] } Bag#Domain(b)[o] <==> b[o] > 0 ); axiom (forall b: Bag T :: { Bag#IsEmpty(b), Bag#Domain(b) }{ Set#IsEmpty(Bag#Domain(b)) } Bag#IsEmpty(b) <==> Set#IsEmpty(Bag#Domain(b)) ); // Do two bags contain the same number of the same elements? function Bag#Equal(Bag T, Bag T): bool; axiom(forall a: Bag T, b: Bag T :: { Bag#Equal(a,b) } Bag#Equal(a,b) <==> (forall o: T :: {a[o]} {b[o]} a[o] == b[o])); // extensionality axiom for multisets axiom(forall a: Bag T, b: Bag T :: { Bag#Equal(a,b) } Bag#Equal(a,b) ==> a == b); // Does a have at most as many of each element as b? function Bag#Subset(Bag T, Bag T): bool; axiom(forall a: Bag T, b: Bag T :: { Bag#Subset(a,b) } Bag#Subset(a,b) <==> (forall o: T :: {a[o]} {b[o]} a[o] <= b[o])); // Do two bags have no elements in common? function Bag#Disjoint(Bag T, Bag T): bool; axiom (forall a: Bag T, b: Bag T :: { Bag#Disjoint(a,b) } Bag#Disjoint(a,b) <==> (forall o: T :: {a[o]} {b[o]} a[o] == 0 || b[o] == 0)); // Bag extended with one occurrence of an element function Bag#Extended(Bag T, T): Bag T; // pure containment axiom axiom (forall a: Bag T, x: T, o: T :: { Bag#Extended(a,x)[o] } 0 < Bag#Extended(a,x)[o] <==> o == x || 0 < a[o]); // union-ing increases count by one axiom (forall a: Bag T, x: T :: { Bag#Extended(a, x) } Bag#Extended(a, x)[x] == a[x] + 1); // non-decreasing axiom (forall a: Bag T, x: T, y: T :: { Bag#Extended(a, x), a[y] } 0 < a[y] ==> 0 < Bag#Extended(a, x)[y]); // other elements unchanged axiom (forall a: Bag T, x: T, y: T :: { Bag#Extended(a, x), a[y] } x != y ==> a[y] == Bag#Extended(a, x)[y]); axiom (forall a: Bag T, x: T :: { Bag#Card(Bag#Extended(a, x)) } Bag#Card(Bag#Extended(a, x)) == Bag#Card(a) + 1); // Bag extended with multiple occurrences of an element function Bag#ExtendedMultiple(Bag T, T, int): Bag T; axiom (forall a: Bag T, x: T :: { Bag#ExtendedMultiple(a, x, 0) } Bag#Equal(Bag#ExtendedMultiple(a, x, 0), a)); // pure containment axiom axiom (forall a: Bag T, x: T, n: int, o: T :: { Bag#ExtendedMultiple(a, x, n)[o] } n > 0 ==> (0 < Bag#ExtendedMultiple(a, x, n)[o] <==> o == x || 0 < a[o])); // union-ing increases count by n axiom (forall a: Bag T, x: T, n: int :: { Bag#ExtendedMultiple(a, x, n) } Bag#ExtendedMultiple(a, x, n)[x] == a[x] + n); // non-decreasing axiom (forall a: Bag T, x: T, n: int, y: T :: { Bag#ExtendedMultiple(a, x, n), a[y] } 0 < a[y] ==> 0 < Bag#ExtendedMultiple(a, x, n)[y]); // other elements unchanged axiom (forall a: Bag T, x: T, n: int, y: T :: { Bag#ExtendedMultiple(a, x, n), a[y] } x != y ==> a[y] == Bag#ExtendedMultiple(a, x, n)[y]); axiom (forall a: Bag T, x: T, n: int :: { Bag#Card(Bag#ExtendedMultiple(a, x, n)) } Bag#Card(Bag#ExtendedMultiple(a, x, n)) == Bag#Card(a) + n); // Bag with one occurrence of an element removed function Bag#Removed(Bag T, T): Bag T; axiom (forall a: Bag T, x: T :: { Bag#Removed(a, x)[x] } a[x] > 0 ==> Bag#Removed(a, x)[x] == a[x] - 1); axiom (forall a: Bag T, x: T :: { Bag#Removed(a, x)[x] } a[x] == 0 ==> Bag#Removed(a, x)[x] == 0); axiom (forall a: Bag T, x: T, y: T :: { Bag#Removed(a, x)[y] } x != y ==> a[y] == Bag#Removed(a, x)[y]); axiom (forall a: Bag T, x: T :: { Bag#Card(Bag#Removed(a, x)) } a[x] > 0 ==> Bag#Card(Bag#Removed(a, x)) == Bag#Card(a) - 1); axiom (forall a: Bag T, x: T :: { Bag#Card(Bag#Removed(a, x)) } a[x] == 0 ==> Bag#Card(Bag#Removed(a, x)) == Bag#Card(a)); // Bag with multiple occurrences of an element removed function Bag#RemovedMultiple(Bag T, T, int): Bag T; axiom (forall a: Bag T, x: T, n: int :: { Bag#RemovedMultiple(a, x, n)[x] } a[x] >= n ==> Bag#RemovedMultiple(a, x, n)[x] == a[x] - n); axiom (forall a: Bag T, x: T, n: int :: { Bag#RemovedMultiple(a, x, n)[x] } a[x] < n ==> Bag#RemovedMultiple(a, x, n)[x] == 0); axiom (forall a: Bag T, x: T, n: int, y: T :: { Bag#RemovedMultiple(a, x, n), a[y] } x != y ==> a[y] == Bag#RemovedMultiple(a, x, n)[y]); axiom (forall a: Bag T, x: T, n: int :: { Bag#Card(Bag#RemovedMultiple(a, x, n)) } a[x] >= n ==> Bag#Card(Bag#RemovedMultiple(a, x, n)) == Bag#Card(a) - n); axiom (forall a: Bag T, x: T, n: int :: { Bag#Card(Bag#RemovedMultiple(a, x, n)) } a[x] < 0 ==> Bag#Card(Bag#Removed(a, x)) == Bag#Card(a) - a[x]); // special cases axiom (forall a: Bag T, x: T :: { Bag#RemovedMultiple(a, x, 0) } Bag#Equal(Bag#RemovedMultiple(a, x, 0), a)); // Bag with all occurrences of an element removed. function Bag#RemovedAll(Bag T, T): Bag T; axiom (forall a: Bag T, x: T :: { Bag#RemovedAll(a, x)[x] } Bag#RemovedAll(a, x)[x] == 0); axiom (forall a: Bag T, x: T, y: T :: { Bag#RemovedAll(a, x), a[y] } x != y ==> a[y] == Bag#RemovedAll(a, x)[y]); axiom (forall a: Bag T, x: T :: {Bag#Card(Bag#RemovedAll(a, x))} Bag#Card(Bag#RemovedAll(a, x)) == Bag#Card(a) - a[x]); // Bag that consists only of those elements of a that are in s. function Bag#Restricted(Bag T, Set T): Bag T; axiom (forall a: Bag T, s: Set T, x: T :: { Bag#Restricted(a, s)[x] } Bag#Restricted(a, s)[x] == if s[x] then a[x] else 0); // Union of two bags function Bag#Union(Bag T, Bag T): Bag T; // union-ing is the sum of the contents axiom (forall a: Bag T, b: Bag T, o: T :: { Bag#Union(a,b)[o] } Bag#Union(a,b)[o] == a[o] + b[o]); axiom (forall a: Bag T, b: Bag T :: { Bag#Card(Bag#Union(a,b)) } Bag#Card(Bag#Union(a,b)) == Bag#Card(a) + Bag#Card(b)); // Intersection of two bags function Bag#Intersection(Bag T, Bag T): Bag T; axiom (forall a: Bag T, b: Bag T, o: T :: { Bag#Intersection(a,b)[o] } Bag#Intersection(a,b)[o] == min(a[o], b[o])); // left and right pseudo-idempotence axiom (forall a, b: Bag T :: { Bag#Intersection(Bag#Intersection(a, b), b) } Bag#Intersection(Bag#Intersection(a, b), b) == Bag#Intersection(a, b)); axiom (forall a, b: Bag T :: { Bag#Intersection(a, Bag#Intersection(a, b)) } Bag#Intersection(a, Bag#Intersection(a, b)) == Bag#Intersection(a, b)); // Difference of two multisets function Bag#Difference(Bag T, Bag T): Bag T; axiom (forall a: Bag T, b: Bag T, o: T :: { Bag#Difference(a,b)[o] } Bag#Difference(a,b)[o] == Math#clip(a[o] - b[o])); axiom (forall a, b: Bag T, y: T :: { Bag#Difference(a, b), b[y], a[y] } a[y] <= b[y] ==> Bag#Difference(a, b)[y] == 0 ); axiom (forall a, b: Bag T :: { Bag#Card(Bag#Difference(a, b)) } Bag#Card(Bag#Difference(a, b)) + Bag#Card(Bag#Difference(b, a)) + 2 * Bag#Card(Bag#Intersection(a, b)) == Bag#Card(Bag#Union(a, b)) && Bag#Card(Bag#Difference(a, b)) == Bag#Card(a) - Bag#Card(Bag#Intersection(a, b))); // Conversion from set function Bag#FromSet(Set T): Bag T; axiom (forall s: Set T, a: T :: { Bag#FromSet(s)[a] } (Bag#FromSet(s)[a] == 0 <==> !s[a]) && (Bag#FromSet(s)[a] == 1 <==> s[a])); axiom (forall s: Set T :: { Bag#Card(Bag#FromSet(s)) } Bag#Card(Bag#FromSet(s)) == Set#Card(s)); // Auxiliary functions function Math#clip(a: int): int; axiom (forall a: int :: { Math#clip(a) } 0 <= a ==> Math#clip(a) == a); axiom (forall a: int :: { Math#clip(a) } a < 0 ==> Math#clip(a) == 0); // Type property function {: inline } Bag#DomainType(heap: HeapType, b: Bag ref, t: Type): bool { (forall o: ref :: { Bag#Domain(b)[o] }{ b[o] } Bag#Domain(b)[o] ==> detachable(heap, o, t)) }