Timings for insertionsort.v
- /home/gitlab-runner/builds/v6HyzL39/0/coq/coq/_bench/opam.OLD/ocaml-OLD/.opam-switch/build/coq-bedrock2.dev/./bedrock2/src/bedrock2Examples/insertionsort.v.timing
- /home/gitlab-runner/builds/v6HyzL39/0/coq/coq/_bench/opam.NEW/ocaml-NEW/.opam-switch/build/coq-bedrock2.dev/./bedrock2/src/bedrock2Examples/insertionsort.v.timing
Require Import bedrock2.NotationsCustomEntry.
Import Syntax BinInt String List.ListNotations.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope list_scope.
Definition insertionsort := func! (a, n) {
i = $0;
while (i < n) {
t = load4(a + $4*i);
j = $0;
while (load4(a + $4*j) < t) {
j = j + $1
};
while (j < i+$1) {
u = load4(a + $4*j);
store4(a + $4*j, t);
t = u;
j = j + $1;
coq:(cmd.unset "u")
};
i = i + $1;
coq:(cmd.unset "t");
coq:(cmd.unset "j")
}
}.
Require Import coqutil.Word.Interface coqutil.Word.Properties.
Require Import coqutil.Tactics.Tactics.
Require Import bedrock2.WeakestPrecondition.
Require Import bedrock2.Semantics bedrock2.FE310CSemantics.
Require Import coqutil.Map.Interface bedrock2.Map.Separation bedrock2.Map.SeparationLogic.
Require bedrock2.WeakestPreconditionProperties.
From coqutil.Tactics Require Import letexists eabstract.
Require Import bedrock2.ProgramLogic bedrock2.Scalars bedrock2.Array bedrock2.Loops.
Require Import bedrock2.ZnWords.
Require Import coqutil.Sorting.Permutation.
Context {word: word.word 32} {mem: map.map word Byte.byte}.
Context {word_ok: word.ok word} {mem_ok: map.ok mem}.
Definition nth(l: list word)(n: nat): word := List.nth n l (word.of_Z 0).
Definition Sorted(l: list word): Prop :=
forall i j: nat, (i < j < List.length l)%nat ->
word.unsigned (nth l i) <= word.unsigned (nth l j).
Lemma Sorted_nil: Sorted [].
Lemma Sorted_snoc: forall l a,
Sorted l ->
(forall k, (k < List.length l)%nat -> word.unsigned (nth l k) <= word.unsigned a) ->
Sorted (l ++ [a]).
rewrite List.app_length in *.
assert (j < List.length l \/ j = List.length l)%nat as C by Lia.lia.
rewrite ?List.app_nth1 by Lia.lia.
rewrite ?List.app_nth1 by Lia.lia.
Lemma Sorted_insert: forall left a1 a2 right,
Sorted (left ++ a2 :: right) ->
(forall k, (k < List.length left)%nat -> word.unsigned (nth left k) <= word.unsigned a1) ->
word.unsigned a1 <= word.unsigned a2 ->
Sorted (left ++ [a1; a2] ++ right).
rewrite ?List.app_length in *.
assert (j < List.length left \/ j = List.length left \/ j = List.length left + 1 \/
List.length left + 1 < j < List.length left + 2 + List.length right)%nat as C by Lia.lia.
destruct C as [C | [C | [C | C]]].
rewrite ?List.app_nth1 by Lia.lia.
assert (i < j < Datatypes.length left + S (Datatypes.length right))%nat as A by Lia.lia.
specialize H with (1 := A).
rewrite ?List.app_nth1 in H by Lia.lia.
rewrite ?List.app_nth1 by Lia.lia.
change (left ++ a1 :: a2 :: right) with (left ++ [a1] ++ a2 :: right).
replace (Datatypes.length left + 1)%nat with (List.length (left ++ [a1])).
rewrite <- List.app_assoc.
assert (i < List.length left \/ i = List.length left)%nat as C by Lia.lia.
rewrite List.app_nth1 by Lia.lia.
change (left ++ [a1] ++ a2 :: right) with (left ++ a1 :: a2 :: right).
replace (List.nth (S j) (left ++ a1 :: a2 :: right) (word.of_Z 0))
with (List.nth j (left ++ a2 :: right) (word.of_Z 0)).
change (left ++ a2 :: right) with (left ++ [a2] ++ right).
rewrite List.app_nth2; rewrite List.app_length; cbn.
change (left ++ a1 :: a2 :: right) with (left ++ [a1] ++ a2 :: right).
rewrite List.app_nth2; rewrite List.app_length.
change (Datatypes.length [a1]) with 1%nat.
replace (S j - (Datatypes.length left + 1))%nat with (S (j - (Datatypes.length left + 1))) by Lia.lia.
assert (i < List.length left \/ i = List.length left \/ i = List.length left + 1 \/
List.length left + 1 < i <= j)%nat as D by Lia.lia.
destruct D as [D | [D | [D | D]]].
rewrite (List.app_nth1 left (a1 :: a2 :: right)) by assumption.
assert (i < j < Datatypes.length left + S (Datatypes.length right))%nat as A by Lia.lia.
specialize H with (1 := A).
rewrite (@List.app_nth1 _ left (a2 :: right) _ i) in H by assumption.
assert (i < j < Datatypes.length left + S (Datatypes.length right))%nat as A by Lia.lia.
specialize H with (1 := A).
rewrite List.nth_middle in H.
etransitivity; eassumption.
change (left ++ a1 :: a2 :: right) with (left ++ [a1] ++ a2 :: right).
replace (Datatypes.length left + 1)%nat with (List.length (left ++ [a1])).
replace a2 with (List.nth (List.length left) (left ++ a2 :: right) (word.of_Z 0)) at 1.
change (left ++ a2 :: right) with (left ++ [a2] ++ right).
rewrite List.app_nth2 with (n := j); rewrite List.app_length; cbn.
replace (left ++ a1 :: a2 :: right) with ((left ++ [a1; a2]) ++ right).
rewrite <- List.app_assoc.
rewrite List.app_nth2; rewrite List.app_length; cbn.
replace (List.nth (j - (Datatypes.length left + 1)) right (word.of_Z 0)) with
(List.nth j (left ++ a2 :: right) (word.of_Z 0)).
rewrite List.app_nth2 by Lia.lia.
replace (j - Datatypes.length left)%nat with (S (j - (Datatypes.length left + 1))) by Lia.lia.
replace (List.nth (i - (Datatypes.length left + 2)) right (word.of_Z 0)) with
(List.nth (i - 1) (left ++ a2 :: right) (word.of_Z 0)).
rewrite List.app_nth2 by Lia.lia.
replace (i - 1 - Datatypes.length left)%nat with (S (i - (Datatypes.length left + 2))) by Lia.lia.
Lemma ptsto_nonaliasing: forall addr b1 b2 m (R: mem -> Prop),
(ptsto addr b1 * ptsto addr b2 * R)%sep m ->
False.
unfold ptsto, sep, map.split, map.disjoint in *.
repeat match goal with
| H: exists _, _ |- _ => destruct H
| H: _ /\ _ |- _ => destruct H
end.
specialize (H4 addr b1 b2).
rewrite ?map.get_put_same in H4.
Lemma array_scalar32_max_size: forall addr xs (R: mem -> Prop) m,
(array scalar32 (word.of_Z 4) addr xs * R)%sep m ->
4 * Z.of_nat (Datatypes.length xs) <= 2 ^ 32.
assert (4 * Z.of_nat (Datatypes.length xs) <= 2 ^ 32 \/ 2 ^ 32 < 4 * Z.of_nat (Datatypes.length xs))
as C by Lia.lia.
destruct C as [C | C]; [exact C | exfalso].
pose proof (List.firstn_skipn (Z.to_nat (2 ^ 32 / 4)) xs) as E.
pose proof @List.firstn_length_le _ xs (Z.to_nat (2 ^ 32 / 4)) as A.
assert (Z.to_nat (2 ^ 32 / 4) <= Datatypes.length xs)%nat as B by ZnWords.
destruct (List.firstn (Z.to_nat (2 ^ 32 / 4)) xs) as [|h1 t1] eqn: E1.
destruct (List.skipn (Z.to_nat (2 ^ 32 / 4)) xs) as [|h2 t2] eqn: E2.
pose proof @List.skipn_length _ (Z.to_nat (2 ^ 32 / 4)) xs as B.
SeparationLogic.seprewrite_in @array_append H.
SeparationLogic.seprewrite_in @array_cons H.
SeparationLogic.seprewrite_in @array_cons H.
replace (word.add addr (word.of_Z (word.unsigned (word.of_Z 4) * Z.of_nat (Datatypes.length (h1 :: t1)))))
with addr in H by ZnWords.
unfold scalar32 at 1 3 in H.
unfold truncated_word, truncated_scalar, littleendian, ptsto_bytes.ptsto_bytes in H.
rewrite !HList.tuple.to_list_of_list in H.
eapply (ptsto_nonaliasing addr (List.hd Byte.x00 (LittleEndianList.le_split 4 (word.unsigned h2))) (List.hd Byte.x00 (LittleEndianList.le_split 4 (word.unsigned h1))) m).
unfold LittleEndianList.le_split, List.hd, array in *.
Local Infix "*" := sep : type_scope.
Instance spec_of_insertionsort : spec_of "insertionsort" :=
fnspec! "insertionsort" addr n / xs R,
{ requires t m := n = word.of_Z (Z.of_nat (List.length xs)) /\ (array scalar32 (word.of_Z 4) addr xs * R) m;
ensures t' m' := t = t' /\ exists ys,
Sorted ys /\ Permutation xs ys /\ (array scalar32 (word.of_Z 4) addr ys * R) m' }.
Definition sorted_except(unsortedLen: nat)(addr: word)(xs: list word)(m: mem)(R: mem -> Prop): Prop :=
exists sorted unsorted,
List.length unsorted = unsortedLen /\
(array scalar32 (word.of_Z 4) addr (sorted ++ unsorted) * R) m /\
Sorted sorted /\ Permutation xs (sorted ++ unsorted).
Lemma insertionsort_ok : program_logic_goal_for_function! insertionsort.
assert (4 * Z.of_nat (List.length xs) <= 2 ^ 32).
eapply array_scalar32_max_size.
refine (tailrec HList.polymorphic_list.nil
["a"; "i"; "n"]
(fun unsortedLen t m a i n => PrimitivePair.pair.mk
(Z.of_nat (List.length xs) = word.unsigned n /\
word.unsigned i + Z.of_nat unsortedLen = word.unsigned n /\
a = addr /\
sorted_except unsortedLen addr xs m R)
(fun T M A I N => T = t /\ sorted_except 0 addr xs M R))
lt _ (List.length xs) _ _ _);
cbn [reconstruct map.putmany_of_list HList.tuple.to_list
HList.hlist.foralls HList.tuple.foralls
HList.hlist.existss HList.tuple.existss
HList.hlist.apply HList.tuple.apply
HList.hlist
List.repeat Datatypes.length
HList.polymorphic_list.repeat HList.polymorphic_list.length
PrimitivePair.pair._1 PrimitivePair.pair._2] in *.
(* current state satisfies loop precondition *)
eauto using perm_nil, Sorted_nil.
(* if break, post holds: *)
(* TODO straightline_cleanup should clear these *)
rename x0 into i, x1 into n.
(* COQBUG https://github.com/coq/coq/issues/3051 *)
let x := constr:(word.unsigned_ltu) in rewrite x in *.
destruct_one_match_hyp; ZnWords.
(* if again, execute loop body: *)
rename x0 into i, x1 into n.
match goal with
| H: context[word.ltu] |- _ =>
rewrite word.unsigned_ltu in H;
assert (word.unsigned i < word.unsigned n) by (destruct_one_match_hyp; ZnWords);
clear H
end.
match goal with
| H: sorted_except _ _ _ _ _ |- _ => destruct H as (sorted & unsorted & ? & ? & ? & ?)
end.
destruct unsorted as [|e unsorted].
assert (0 = v)%nat by assumption.
match goal with
| H: (_ * _)%sep m0 |- _ => rename H into HM
end.
change (sorted ++ e :: unsorted) with (sorted ++ [e] ++ unsorted) in HM.
do 2 seprewrite_in @array_append' HM.
match goal with
| H: Permutation _ _ |- _ => pose proof (Permutation_length H) as PL
end.
rewrite List.app_length in PL.
rewrite @List.length_cons in *.
match type of HM with
| context[word.add _ (word.mul _ ?x)] => replace x with i in HM by ZnWords
end.
refine (tailrec (HList.polymorphic_list.cons _ (HList.polymorphic_list.cons _
(HList.polymorphic_list.cons _ HList.polymorphic_list.nil)))
["a"; "i"; "j"; "n"; "t"]
(fun remSortedLen seenSorted remSorted R t m a i0 j n0 e0 => PrimitivePair.pair.mk
(List.length remSorted = remSortedLen /\
i0 = i /\ a = addr /\ n0 = n /\ e0 = e /\ sorted = seenSorted ++ remSorted /\
word.unsigned j + Z.of_nat remSortedLen = word.unsigned i /\
(forall k: nat, (k < List.length seenSorted)%nat -> word.unsigned (nth sorted k) <= word.unsigned e) /\
(array scalar32 (word.of_Z 4) (word.add addr (word.mul (word.of_Z 4) j)) remSorted *
scalar32 (word.add addr (word.mul (word.of_Z 4) i)) e * R) m)
(fun T M A I J N E => T = t /\ I = i /\ N = n /\ e = E /\ a = A /\
word.unsigned J <= word.unsigned i /\
List.length remSorted = remSortedLen /\
sorted = seenSorted ++ remSorted /\
word.unsigned e <= word.unsigned (nth (sorted ++ [e]) (Z.to_nat (word.unsigned J))) /\
(forall k: nat, (k < Z.to_nat (word.unsigned J))%nat -> word.unsigned (nth sorted k) <= word.unsigned e) /\
(array scalar32 (word.of_Z 4)
(word.add addr (word.mul (word.of_Z 4)
(word.of_Z (word.unsigned i - Z.of_nat remSortedLen))))
remSorted *
scalar32 (word.add addr (word.mul (word.of_Z 4) i)) e * R) M))
lt _ (List.length sorted) _ _ _ _ _ _);
cbn [reconstruct map.putmany_of_list HList.tuple.to_list
HList.hlist.foralls HList.tuple.foralls
HList.hlist.existss HList.tuple.existss
HList.hlist.apply HList.tuple.apply
HList.hlist
List.repeat Datatypes.length
HList.polymorphic_list.repeat HList.polymorphic_list.length
PrimitivePair.pair._1 PrimitivePair.pair._2] in *.
(* current state satisfies loop precondition *)
all: reflexivity || ZnWords || idtac.
match goal with
| |- context[array _ _ ?a] => replace a with addr by ZnWords
end.
rename x4 into j, x into seenSorted, x0 into remSorted, v0 into remSortedLen, x1 into R'.
destruct remSorted as [|e' remSorted].
(* exiting loop because element e itself has been reached *)
assert (0 = remSortedLen)%nat by assumption.
assert (j = i) by ZnWords.
rewrite word.unsigned_ltu in C.
rewrite Z.ltb_irrefl in C.
rewrite word.unsigned_of_Z_0 in C.
rewrite word.unsigned_of_Z_1 in C.
all: try reflexivity || ZnWords.
rewrite List.app_nth2 by ZnWordsL.
match goal with
| |- context[List.nth ?N _ _] => replace N with O by ZnWordsL
end.
match goal with
| H: forall _: nat, _ -> _ |- _ => apply H
end.
match goal with
| H: (_ * _) m1 |- _ => rename H into HM1
end.
SeparationLogic.seprewrite @array_nil.
SeparationLogic.seprewrite_in @array_nil HM1.
match goal with
| H: (_ * _) m1 |- _ => rename H into HM1
end.
seprewrite_in @array_cons HM1.
(* exiting loop because e' >= e found, so we know where to insert e *)
rewrite word.unsigned_of_Z_1 in C.
all: try reflexivity || ZnWords.
replace (Z.to_nat (word.unsigned j)) with (List.length seenSorted) by ZnWordsL.
rewrite <- List.app_assoc.
rewrite <- List.app_comm_cons.
match goal with
| H: forall _: nat, _ -> _ |- _ => apply H
end.
SeparationLogic.seprewrite @array_cons.
cancel_seps_at_indices 0%nat 0%nat.
cancel_seps_at_indices 0%nat 0%nat.
(* running loop body (just increment j) *)
rewrite word.unsigned_ltu in C.
rewrite word.unsigned_of_Z_0 in C.
(* at end of first inner loop *)
eexists (seenSorted ++ [e']), remSorted, _, _.
(* precondition of next loop iteration holds *)
all: rewrite <-?List.app_assoc; try reflexivity || ZnWords.
assert (k < List.length seenSorted \/ k = List.length seenSorted)%nat as D by ZnWordsL.
cancel_seps_at_indices 1%nat 0%nat.
(* postcondition of previous loop iteration implies postcondition of current loop iteration *)
all: try reflexivity || assumption || ZnWords.
SeparationLogic.seprewrite @array_cons.
cancel_seps_at_indices 0%nat 1%nat.
cancel_seps_at_indices 0%nat 0%nat.
rename x into a, x1 into j, x3 into e.
match goal with
| H: (_ * _) m1 |- _ => rename H into HM1
end.
match type of HM1 with
| context[array scalar32 _ ?A sorted] => replace A with a in HM1 by ZnWords
end.
remember (List.firstn (Z.to_nat (word.unsigned j)) sorted) as smaller.
remember (List.skipn (Z.to_nat (word.unsigned j)) sorted) as toShift.
assert (sorted = smaller ++ toShift).
assert (word.unsigned j = Z.of_nat (List.length smaller)) as Ej.
(* WHY do I need width:=width even with Local Hint Mode word - : typeclass_instances. ? *)
replace j with (word.of_Z (width := 32) (Z.of_nat (Datatypes.length smaller))) by ZnWords.
clear j Ej Heqsmaller HeqtoShift.
rewrite List.app_nil_l in *.
refine (tailrec (HList.polymorphic_list.cons _ (HList.polymorphic_list.cons _ HList.polymorphic_list.nil))
["a"; "i"; "j"; "n"; "t"]
(fun StoShiftLen toShift R t m a i0 j n0 shelf => PrimitivePair.pair.mk
(i0 = i /\ a = addr /\ n0 = n /\
match StoShiftLen with
| S toShiftLen => List.length toShift = toShiftLen /\
word.unsigned j + Z.of_nat toShiftLen = word.unsigned i /\
(array scalar32 (word.of_Z 4) (word.add addr (word.mul (word.of_Z 4) j))
(toShift ++ [e]) * R) m
| O => (* special precondition for just before exiting the loop: *)
word.unsigned j = word.unsigned i + 1 /\ toShift = [] /\ R m
end)
(fun T M A I J N E => T = t /\ I = i /\ N = n /\ A = a /\
match StoShiftLen with
| S toShiftLen =>
(array scalar32 (word.of_Z 4)
(word.add addr (word.mul (word.of_Z 4) (word.of_Z (word.unsigned i - Z.of_nat toShiftLen))))
(shelf :: toShift) * R) M
| O => R M
end))
lt _ (S (List.length toShift)) _ _ _ _ _);
cbn [reconstruct map.putmany_of_list HList.tuple.to_list
HList.hlist.foralls HList.tuple.foralls
HList.hlist.existss HList.tuple.existss
HList.hlist.apply HList.tuple.apply
HList.hlist
List.repeat Datatypes.length
HList.polymorphic_list.repeat HList.polymorphic_list.length
PrimitivePair.pair._1 PrimitivePair.pair._2] in *.
(* current state satisfies loop precondition *)
all: reflexivity || ZnWordsL || idtac.
SeparationLogic.seprewrite @array_append.
SeparationLogic.seprewrite_in @array_append HM1.
SeparationLogic.seprewrite @array_cons.
SeparationLogic.seprewrite @array_nil.
cancel_seps_at_indices 1%nat 1%nat.
cancel_seps_at_indices 1%nat 0%nat.
rename toShift into toShift_orig, R into R_orig, t into t0.
intros StoShiftLen toShift R t m2 a i0 j n0 shelf.
(* if break, postcondition holds *)
match goal with
| H: word.unsigned br = 0 |- _ => rename H into HC
end.
rewrite word.unsigned_of_Z_1 in HC.
destruct StoShiftLen as [|toShiftLen]; repeat straightline_cleanup.
(* loop body of second inner loop: *)
match goal with
| H: word.unsigned br <> 0 |- _ => rename H into HC
end.
rewrite word.unsigned_of_Z_0 in HC.
destruct StoShiftLen as [|toShiftLen]; repeat straightline_cleanup.
assert (exists y ys, toShift ++ [e] = y :: ys) as Ey.
exists r, (toShift ++ [e]).
destruct Ey as (y & ys & Ey).
match goal with
| H: (_ * _) m2 |- _ => rename H into HM2
end.
seprewrite_in @array_cons HM2.
(* <- steps through all of second inner loop body *)
destruct toShift as [|e' toShift]; cycle 1; cbn [List.length] in *.
rewrite <- List.app_comm_cons in Ey.
eexists _, _, (S (Datatypes.length toShift)).
(* precondition of next loop iteration holds *)
all: try reflexivity || ZnWords.
cancel_seps_at_indices 1%nat 0%nat.
(* postcondition of previous loop iteration implies postcondition of current loop iteration *)
SeparationLogic.seprewrite @array_cons.
cancel_seps_at_indices 0%nat 1%nat.
cancel_seps_at_indices 0%nat 0%nat.
match goal with
| H: (_ * _) m3 |- _ => rename H into HM3
end.
(* precondition of next loop iteration holds *)
all: try reflexivity || ZnWords.
(* postcondition of previous loop iteration implies postcondition of current loop iteration *)
SeparationLogic.seprewrite @array_cons.
SeparationLogic.seprewrite @array_nil.
cancel_seps_at_indices 0%nat 0%nat.
(* <-- increment i *)
(* at end of outer loop *)
exists (List.length unsorted).
(* precondition of next loop iteration holds *)
exists (smaller ++ [e] ++ toShift), unsorted.
repeat SeparationLogic.seprewrite @array_append.
match goal with
| H: (_ * _) m2 |- _ => rename H into HM2
end.
SeparationLogic.seprewrite_in @array_cons HM2.
SeparationLogic.seprewrite @array_cons.
SeparationLogic.seprewrite @array_nil.
cancel_seps_at_indices 0%nat 0%nat.
cancel_seps_at_indices 0%nat 0%nat.
cancel_seps_at_indices 0%nat 0%nat.
match goal with
| H: word.unsigned _ <= word.unsigned _ |- _ => rename H into HLt; move HLt at bottom
end.
rewrite Znat.Nat2Z.id in HLt.
rewrite <- List.app_assoc in HLt.
destruct toShift as [|e' toShift]; cycle 1.
rewrite <- List.app_comm_cons in HLt.
rewrite List.nth_middle in HLt.
eapply Sorted_insert; try eassumption.
rewrite Znat.Nat2Z.id in *.
match goal with
| H: forall _: nat, _ -> _ |- _ => specialize H with (1 := Hk); rename H into B; move B at bottom
end.
rewrite List.app_nth1 in B by assumption.
rewrite List.app_nil_r in *.
rewrite Znat.Nat2Z.id in *.
match goal with
| H: Permutation _ _ |- _ => rename H into HP; move HP at bottom
end.
change (e :: unsorted) with ([e] ++ unsorted) in HP.
rewrite List.app_assoc in HP.
2: apply Permutation_refl.
rewrite <- List.app_assoc.
1: apply Permutation_refl.
apply Permutation_app_comm.
(* postcondition of previous loop iteration implies postcondition of current loop iteration *)
match goal with
| H: sorted_except _ _ _ _ _ |- _ => destruct H as (sorted & unsorted & EL & HM0 & So & Pe)
end.
rewrite List.app_nil_r in *.
(* 4.208 secs *)
(*
From bedrock2 Require Import ToCString PrintString coqutil.Macros.WithBaseName.
Goal True. print_string (c_module &[,insertionsort]). Abort.
*)
(* append this driver code for a little test:
#include <stdio.h>
void printlist(uint32_t* a, size_t n) {
for (size_t i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
int main() {
uint32_t a[] = {12, 50000, 23, 11167, 1000};
size_t n = 5;
printlist(a, n);
insertionsort(a, n);
printlist(a, n);
}
*)