Capucine Example: Course and StudentsA simple example which shows some of the main features of Capucine. |
To compile, go into the capucine directory and type:
make examples/course.gwhy
type array ('a) logic empty_array (): array ('a) logic store (a: array ('a), i: int, v: 'a): array ('a) logic select (a: array ('a), i: int): 'a axiom select_eq: forall a: array ('a). forall i: int. forall v: 'a. select(store(a, i, v), i) = v axiom select_neq: forall a: array ('a). forall i: int. forall j: int. forall v: 'a. i <> j ==> select(store(a, i, v), j) = select(a, j) (*****************************************************************************) class Student { mark: int; } (* sum between i and j-1 *) (* If marksum is always from 0, provers actually have much more difficulty proving stuff! (I checked that they would not prove false...) *) logic mark_sum [Rs: Student] (a: array ([Rs]), i: int, j: int): int axiom MSempty: forall region Rs: Student. forall a: array ([?]). forall i: int. mark_sum [Rs] (a, i, i) = 0 axiom MSsingleton: forall region Rs: Student. forall a: array ([?]). forall i: int. mark_sum [Rs] (a, i, i+1) = get(Rs, select(a, i), mark) axiom MSsplit: forall region Rs: Student. forall a: array ([?]). forall i: int. forall j: int. forall k: int. i <= k && k <= j ==> mark_sum [Rs] (a, i, j) = mark_sum [Rs] (a, i, k) + mark_sum [Rs] (a, k, j) axiom MSfootprint: forall region Rold: Student. forall region Rnew: Student. forall Aold: array ([?]). forall Anew: array ([?]). forall left: int. forall right: int. (forall i: int. left <= i && i < right ==> get (Rold, select(Aold, i), mark) = get (Rnew, select(Anew, i), mark)) ==> mark_sum [Rold] (Aold, left, right) = mark_sum [Rnew] (Anew, left, right) class Course { group Rstudents: Student; students: array ([Rstudents]); sum: int; count: int; invariant sum = mark_sum [Rstudents] (students, 0, count) && count >= 0 && (forall i: int. forall j: int. 0 <= i && i < count ==> 0 <= j && j < count ==> i <> j ==> select(students, i) <> select(students, j)) && (forall p: [?]. p in Rstudents ==> exists i: int. 0 <= i && i < count && p = select(students, i)) } fun create_course [R: Course] (): [R] consumes R^e produces R^c post get(R, result, count) = 0 { let c = new [R]; c.students <- #empty_array (); c.sum <- 0; c.count <- 0; return c } fun add_student [Rc: Course] (c: [Rc], m: int): [c.Rstudents] consumes Rc^c produces Rc^c post get(get(Rc, c, Rstudents), result, mark) = m && get(Rc, c, count) = get(Rc, c, count)@pre + 1 && get(Rc, c, sum) = get(Rc, c, sum)@pre + m && forall s: [?]. s in get(Rc, c, Rstudents)@pre ==> get(get(Rc, c, Rstudents), s, mark) = get(get(Rc, c, Rstudents), s, mark)@pre { use invariant c; let region Rs: Student; let s = new [Rs]; s.mark <- m; use forall i: int. i >= 0 && i < get(Rc, c, count) ==> select(get(Rc, c, students), i) <> s; adopt Rs as c.Rstudents; c.students <- #store (c.students, c.count, s); c.sum <- c.sum + m; c.count <- c.count + 1; return s } fun change_mark [Rc: Course] (c: [Rc], s: [c.Rstudents], m: int): unit consumes Rc^c produces Rc^c post get(get(Rc, c, Rstudents), s, mark) = m && get(Rc, c, sum) = get(Rc, c, sum)@pre + m - get(get(Rc, c, Rstudents), s, mark)@pre && get(Rc, c, count) = get(Rc, c, count)@pre && forall i: int. 0 <= i && i < get(Rc, c, count) ==> s <> select(get(Rc, c, students), i) ==> get(get(Rc, c, Rstudents), select(get(Rc, c, students), i), mark) = get(get(Rc, c, Rstudents), select(get(Rc, c, students), i), mark)@pre { use invariant c; use s in get(Rc, c, Rstudents); label before; let region F: Student; focus s as F; c.sum <- c.sum - s.mark + m; s.mark <- m; unfocus F as c.Rstudents; (* Helps Z3 mono to prove the invariant of c *) assert exists i: int. 0 <= i && i < get(Rc, c, count) && select(get(Rc, c, students), i) = s && mark_sum [get(Rc, c, Rstudents)] (get(Rc, c, students), 0, i) = mark_sum [get(Rc, c, Rstudents)] (get(Rc, c, students), 0, i)@before && mark_sum [get(Rc, c, Rstudents)] (get(Rc, c, students), i+1, get(Rc, c, count)) = mark_sum [get(Rc, c, Rstudents)] (get(Rc, c, students), i+1, get(Rc, c, count))@before; } fun main (x: unit): unit { let region Ra: Course; let algebra = create_course [Ra] (); let region Rb: Course; let physics = create_course [Rb] (); use invariant algebra; assert get(Ra, algebra, count) = 0; assert get(Ra, algebra, sum) = 0; let anna = add_student [Ra] (algebra, 10); (* pour utiliser l'invariant de algebra *) use anna in get(Ra, algebra, Rstudents); assert get(get(Ra, algebra, Rstudents), anna, mark) = 10; use invariant algebra; assert get(Ra, algebra, count) = 1; assert get(Ra, algebra, sum) = 10; let elisa = add_student [Ra] (algebra, 20); assert get(get(Ra, algebra, Rstudents), anna, mark) = 10; use invariant algebra; assert get(Ra, algebra, count) = 2; assert get(Ra, algebra, sum) = 30; use invariant physics; assert get(Rb, physics, count) = 0; assert get(Rb, physics, sum) = 0; let carolina = add_student [Rb] (physics, 7); use invariant physics; assert get(Ra, algebra, count) = 2; assert get(Ra, algebra, sum) = 30; assert get(Rb, physics, count) = 1; assert get(Rb, physics, sum) = 7; assert get(get(Ra, algebra, Rstudents), elisa, mark) = 20; let x = change_mark [Ra] (algebra, anna, 18); assert get(get(Ra, algebra, Rstudents), anna, mark) = 18; assert get(get(Ra, algebra, Rstudents), elisa, mark) = 20; use invariant algebra; assert get(Ra, algebra, count) = 2; assert get(Ra, algebra, sum) = 38; assert get(Rb, physics, count) = 1; assert get(Rb, physics, sum) = 7; }