Theory Sorting_Guarded_Partition
theory Sorting_Guarded_Partition
imports Sorting_Quicksort_Scheme
begin
lemma slice_update_outside[simp]: "i∉{l..<h} ⟹ slice l h (xs[i:=x]) = slice l h xs"
  unfolding Misc.slice_def
  apply auto
  by (metis drop_take leI take_update_cancel)
lemma slice_eq_mset_upd_outside: "slice_eq_mset l h xs xs' ⟹ i∉{l..<h} ⟹ i<length xs' ⟹ slice_eq_mset l h (xs[i:=x]) (xs'[i:=x])"
  unfolding slice_eq_mset_def
  apply (auto simp: drop_update_swap not_le)
  by (metis drop_update_cancel drop_update_swap leI)
  
hide_const (open) Transcendental.pi 
lemma strict_itrans: "a < c ⟹ a < b ∨ b < c" for a b c :: "_::linorder"
  by auto
  
  
  
context weak_ordering begin  
  
subsection ‹Hoare Partitioning Scheme›  
definition "ungrd_qsg_next_l_spec si xs p li ≡ 
  doN {
    ASSERT (li ≤ si ∧ si<length xs ∧ xs!si ❙≥ p);
    SPEC (λli'. li≤li' ∧ li' ≤ si ∧ (∀i∈{li..<li'}. xs!i❙<p) ∧ xs!li'❙≥p)
  }"
definition "ungrd_qsg_next_h_spec si xs p hi ≡ 
  doN {
    ASSERT (si < hi ∧ hi≤length xs ∧ xs!si ❙≤ p);
    SPEC (λhi'. si≤hi' ∧ hi'<hi ∧ (∀i∈{hi'<..<hi}. xs!i❙>p) ∧ xs!hi'❙≤p)
  }"
  
  
definition qsg_next_l :: "nat ⇒ 'a list ⇒ 'a ⇒ nat ⇒ nat nres" where            
  "qsg_next_l si xs p li ≡ doN {
    monadic_WHILEIT (λli'. li≤li' ∧ (∀i∈{li..<li'}. xs!i❙<p) ∧ li'≤ si) 
      (λli. doN { mop_cmp_idx_v xs li p}) (λli. do { ASSERT (li < si); RETURN (li + 1) }) li
  }"  
  
lemma qsg_next_l_refine: "(qsg_next_l,PR_CONST ungrd_qsg_next_l_spec)∈Id→Id→Id→Id→⟨Id⟩nres_rel"
  unfolding qsg_next_l_def ungrd_qsg_next_l_spec_def PR_CONST_def
  apply (intro fun_relI; clarsimp)
  subgoal for si xs p li
    apply (refine_vcg monadic_WHILEIT_rule[where R="measure (λli. si - li)"] split_ifI)
    apply clarsimp_all
    subgoal by (metis le_eq_less_or_eq wo_leD)
    subgoal by (metis atLeastLessThan_iff less_Suc_eq)
    subgoal by (metis diff_less_mono2 lessI)
    subgoal using wo_not_le_imp_less by blast
    done
  done
definition qsg_next_h :: "nat ⇒ 'a list ⇒ 'a ⇒ nat ⇒ nat nres" where
  "qsg_next_h si xs p hi ≡ doN {
    ASSERT (hi>0);
    let hi = hi - 1;
    monadic_WHILEIT (λhi'. si≤hi' ∧ hi'≤hi ∧ (∀i∈{hi'<..hi}. xs!i❙>p))
      (λhi. doN { mop_cmp_v_idx xs p hi}) (λhi. doN { ASSERT(hi>0); RETURN (hi - 1)}) hi
  }"  
  
lemma qsg_next_h_refine: "(qsg_next_h,PR_CONST (ungrd_qsg_next_h_spec)) ∈ Id → Id → Id → Id → ⟨Id⟩nres_rel"
  unfolding qsg_next_h_def ungrd_qsg_next_h_spec_def PR_CONST_def
  apply (refine_vcg monadic_WHILEIT_rule[where R="measure id"] split_ifI)
  apply (all ‹(determ ‹elim conjE exE›)?›)
  apply simp_all
  subgoal by (metis bot_nat_0.extremum_uniqueI gr0I wo_leD)
  subgoal by (metis One_nat_def le_step_down_nat wo_leD)
  subgoal by (metis Suc_le_eq Suc_pred greaterThanAtMost_iff less_Suc_eq less_Suc_eq_le)
  subgoal by (simp add: greaterThanAtMost_upt)
  subgoal using unfold_le_to_lt by presburger
  done
  
definition "ungrd_qsg_next_lh_spec si0 siN xs p li hi ≡ doN {
  li ← ungrd_qsg_next_l_spec siN xs p li;
  hi ← ungrd_qsg_next_h_spec si0 xs p hi;
  RETURN (li,hi)
}"  
definition "qsg_part_assn1 li⇩0 hi⇩0 p xs⇩0 li hi xs ≡
    0 < li⇩0 ∧ li⇩0≤hi⇩0 ∧ hi⇩0<length xs⇩0 ∧ xs⇩0!(li⇩0-1) ❙≤ p ∧ p ❙≤ xs⇩0!hi⇩0
  ∧ slice_eq_mset li⇩0 hi⇩0 xs xs⇩0
  ∧ li⇩0≤li ∧ li≤hi ∧ hi≤hi⇩0
  ∧ (∀i∈{li⇩0..<li}. xs!i ❙≤ p) 
  ∧ (∀i∈{hi..<hi⇩0}. p ❙≤ xs!i) 
"
definition "qsg_part_assn2 li⇩0 hi⇩0 p xs⇩0 li hi xs ≡
    0 < li⇩0 ∧ li⇩0≤hi⇩0 ∧ hi⇩0<length xs⇩0 ∧ xs⇩0!(li⇩0-1) ❙≤ p ∧ p ❙≤ xs⇩0!hi⇩0
  ∧ slice_eq_mset li⇩0 hi⇩0 xs xs⇩0
  ∧ li⇩0≤li ∧ li≤hi+1 ∧ hi<hi⇩0 ∧ li≤hi⇩0
  ∧ (∀i∈{li⇩0..<li}. xs!i ❙≤ p) 
  ∧ (∀i∈{hi<..<hi⇩0}. p ❙≤ xs!i) 
  ∧ xs!li ❙≥ p
  ∧ xs!hi ❙≤ p  
"
lemma qsg_part_12_aux:
  assumes SENTINELS: "xs ! (li⇩0 - Suc 0) ❙≤ p" "p ❙≤ xs ! hi⇩0"
  assumes LI_LE_HI: "li ≤ hi"
  assumes LI'_BOUND: "li ≤ li'" "li' ≤ hi⇩0"
  assumes HI'_BOUND: "li⇩0 - Suc 0 ≤ hi'" "hi' < hi" 
  assumes 
  UPTO_LI: "∀i∈{li⇩0..<li}. xs ! i ❙≤ p" and
  LI_TO_LI': "∀i∈{li..<li'}. xs ! i ❙< p" and
  DOWNTO_HI: "∀i∈{hi..<hi⇩0}. p ❙≤ xs ! i" and
  HI'_TO_HI: "∀i∈{hi'<..<hi}. p ❙< xs ! i"
  shows "li' ≤ Suc hi'" "(∀i∈{li⇩0..<li'}. xs ! i ❙≤ p)" "(∀i∈{hi'<..<hi⇩0}. p ❙≤ xs ! i)"
proof -
  show G1: "(∀i∈{li⇩0..<li'}. xs ! i ❙≤ p)" using UPTO_LI LI_TO_LI' by (auto simp: unfold_lt_to_le)
  
  show G2: "(∀i∈{hi'<..<hi⇩0}. p ❙≤ xs ! i)" using HI'_TO_HI DOWNTO_HI by (auto simp: unfold_lt_to_le)
  
  consider "li<li'" | "hi' < hi-1" | "li'=li" "hi'=hi-1"
    using ‹hi' < hi› ‹li ≤ li'› by linarith
  then show "li' ≤ Suc hi'" proof cases
    case 1
    hence "xs!(li'-1) ❙< p" using LI_TO_LI' by simp
    moreover have "∀i∈{hi'<..hi⇩0}. p ❙≤ xs!i" using ‹p ❙≤ xs ! hi⇩0›
      by (metis G2 greaterThanAtMost_iff greaterThanLessThan_iff le_eq_less_or_eq)
    ultimately show ?thesis
      using ‹li' ≤ hi⇩0›
      apply clarsimp
      by (metis Suc_leD Suc_lessE ‹xs ! (li' - 1) ❙< p› diff_Suc_1 greaterThanAtMost_iff le_def unfold_lt_to_le)
  next
    case 2
    hence "p ❙< xs!(hi'+1)" using HI'_TO_HI by simp
    moreover have "(∀i∈{li⇩0-1..<li'}. xs ! i ❙≤ p)" using G1 ‹xs ! (li⇩0 - Suc 0) ❙≤ p› 
      apply clarsimp
      by (metis atLeastLessThan_iff le_antisym le_refl nat_le_Suc_less_imp nat_less_le nat_neq_iff)
    ultimately show ?thesis
      using ‹li⇩0 - Suc 0 ≤ hi'›
      apply clarsimp
      by (meson atLeastLessThan_iff le_SucI le_def unfold_lt_to_le)
  next
    case 3
    then show ?thesis using ‹li≤hi› by linarith
  qed
qed  
lemma qsg_part_12: "qsg_part_assn1 li⇩0 hi⇩0 p xs⇩0 li hi xs 
  ⟹ ungrd_qsg_next_lh_spec (li⇩0-1) hi⇩0 xs p li hi ≤ SPEC (λ(li',hi').
    qsg_part_assn2 li⇩0 hi⇩0 p xs⇩0 li' hi' xs ∧ hi'<hi
     )"
  unfolding ungrd_qsg_next_lh_spec_def ungrd_qsg_next_l_spec_def ungrd_qsg_next_h_spec_def
  apply refine_vcg
  unfolding qsg_part_assn1_def
  apply (clarsimp_all simp: slice_eq_mset_eq_length)
  subgoal by (metis hd_drop_conv_nth slice_eq_mset_def)
  subgoal by linarith
  subgoal for i
    apply (subst slice_eq_mset_nth_outside, assumption)
    apply auto
    by (metis diff_diff_cancel less_imp_diff_less slice_eq_mset_eq_length)
  subgoal for hi' li'  
    unfolding qsg_part_assn2_def
    apply (clarsimp intro!: qsg_part_12_aux)
    apply (blast dest: qsg_part_12_aux)
    done
  done  
  
  
definition "qsg_partition_aux li⇩0 hi⇩0 p xs⇩0 ≡ doN {
  
  ASSERT (li⇩0>0);
  (li,hi) ← ungrd_qsg_next_lh_spec (li⇩0-1) hi⇩0 xs⇩0 p li⇩0 hi⇩0;
  
  (xs,li,hi) ← WHILEIT 
    (λ(xs,li,hi). qsg_part_assn2 li⇩0 hi⇩0 p xs⇩0 li hi xs)
    (λ(xs,li,hi). li<hi) 
    (λ(xs,li,hi). doN {
      ASSERT(li<hi ∧ li<length xs ∧ hi<length xs ∧ li≠hi);
      xs ← mop_list_swap xs li hi;
      let li = li + 1;
      
      (li,hi) ← ungrd_qsg_next_lh_spec (li⇩0-1) hi⇩0 xs p li hi;
      RETURN (xs,li,hi)
    }) 
    (xs⇩0,li,hi);
  
  RETURN (xs,li)
}"  
definition "gpartition_spec li hi p xs xs' m ≡ 
    slice_eq_mset li hi xs' xs 
  ∧ m∈{li..hi}
  ∧ (∀i∈{li..<m}. xs'!i ❙≤ p)
  ∧ (∀i∈{m..<hi}. p ❙≤ xs'!i)"
definition "gpartition_SPEC li hi p xs ≡ do {
  ASSERT (li≤hi ∧ hi≤length xs);
  SPEC (λ(xs',m). gpartition_spec li hi p xs xs' m)
}"
lemma qsg_part1_init: "⟦0 < li; hi < length xs; xs ! (li - Suc 0) ❙≤ p; p ❙≤ xs ! hi; li ≤ hi⟧ ⟹ qsg_part_assn1 li hi p xs li hi xs"
  unfolding qsg_part_assn1_def
  by simp
lemma qsg_part2_in_bounds: 
  assumes "qsg_part_assn2 li⇩0 hi⇩0 p xs⇩0 li hi xs" 
  shows "li<length xs" "hi<length xs"
  using assms unfolding qsg_part_assn2_def
  by (auto dest: slice_eq_mset_eq_length)
  
  
lemma qsg_part_21: "⟦qsg_part_assn2 li⇩0 hi⇩0 p xs⇩0 li hi xs; li < hi⟧ ⟹ qsg_part_assn1 li⇩0 hi⇩0 p xs⇩0 (Suc li) hi (swap xs li hi)"
  unfolding qsg_part_assn2_def qsg_part_assn1_def
  apply (intro conjI)
  apply clarsimp_all
  subgoal by (metis atLeastLessThan_iff diff_diff_cancel less_Suc_eq_le less_imp_diff_less nat_less_le slice_eq_mset_eq_length swap_indep swap_nth1)
  subgoal by (metis Suc_diff_Suc diff_is_0_eq greaterThanLessThan_iff nat.simps(3) not_less_iff_gr_or_eq slice_eq_mset_eq_length strict_itrans swap_indep swap_nth2)
  done
  
lemma qsg_part_2fin: "⟦qsg_part_assn2 li⇩0 hi⇩0 p xs⇩0 li hi xs; ¬ li < hi⟧ ⟹ gpartition_spec li⇩0 hi⇩0 p xs⇩0 xs li"  
  unfolding qsg_part_assn2_def gpartition_spec_def
  apply clarsimp
  by (metis atLeastLessThan_iff atLeastSucLessThan_greaterThanLessThan le_antisym linorder_le_less_linear nat_Suc_less_le_imp)
  
lemma qsg_partition_aux_correct:
  "⟦0<li; hi<length xs; xs!(li-1) ❙≤ p; p ❙≤ xs!hi⟧ ⟹ qsg_partition_aux li hi p xs ≤ gpartition_SPEC li hi p xs"  
  unfolding qsg_partition_aux_def gpartition_SPEC_def
  apply (refine_vcg WHILEIT_rule[where R="measure (λ(_,_,hi). hi)"] qsg_part_12[where xs⇩0=xs])  
  apply clarsimp_all
  subgoal by (simp add: qsg_part1_init)
  subgoal by (simp add: qsg_part2_in_bounds)
  subgoal by (simp add: qsg_part2_in_bounds)
  subgoal by (simp add: qsg_part_21)
  subgoal by (simp add: qsg_part_2fin)
  done    
definition "qsg_partition li⇩0 hi⇩0 p xs⇩0 ≡ do {
  ASSERT (li⇩0+1<hi⇩0 ∧ hi⇩0≤length xs⇩0);
  (e0,xs) ← mop_idx_v_swap xs⇩0 li⇩0 (COPY p);
  let li=li⇩0+1;
  
  let hi=hi⇩0-1;
  (eN,xs) ← mop_idx_v_swap xs hi (COPY p);
  (xs,m) ← qsg_partition_aux li hi p xs;
  
  let li = li-1;
  let e0ok = e0 ❙≤ p;
  let eNok = p ❙≤ eN;
  (_,xs) ← mop_idx_v_swap xs li e0;
  (_,xs) ← mop_idx_v_swap xs hi eN;
  
  ASSERT (slice_eq_mset li (hi+1) xs xs⇩0);
  
  if e0ok ∧ eNok then
    RETURN (xs,m)
  else if e0ok ∧ ¬eNok then do {
    xs ← mop_list_swap xs m hi;
    ASSERT (m<hi⇩0);
    let m=m+1;
    RETURN (xs,m)
  } else if eNok then do {
    ASSERT (1≤m);
    let m=m-1;
    xs ← mop_list_swap xs li m;
    RETURN (xs,m)
  } else do {
    xs ← mop_list_swap xs li hi;
    RETURN (xs,m)
  }
  
}"
term mop_cmp_idx_v
definition "qsg_partition_wrapper li hi p xs ≡ do {
  if li<hi then (
    if li+1<hi then qsg_partition li hi p xs
    else doN {
      if⇩N mop_cmp_idx_v xs li p then RETURN (xs,hi)
      else RETURN (xs,li)
    }
  ) else RETURN (xs,li)
}"
lemma qsg_partition_correct: "li+1<hi ⟹ qsg_partition li hi p xs ≤ gpartition_SPEC li hi p xs"
  unfolding qsg_partition_def gpartition_SPEC_def
  apply (refine_vcg qsg_partition_aux_correct[THEN order_trans])
  apply simp_all
  apply clarsimp_all
  apply simp_all
  unfolding gpartition_SPEC_def
  apply refine_vcg
  apply clarsimp_all
  apply simp_all
  unfolding gpartition_spec_def
  apply clarsimp_all
  apply (simp_all add: slice_eq_mset_eq_length)
  subgoal for xs' m
    apply (drule slice_eq_mset_upd_outside[where i="li" and x="xs ! li"]; simp?)
    apply (drule slice_eq_mset_upd_outside[where i="hi - Suc 0" and x="xs ! (hi - Suc 0)"]; simp?)
    apply (simp add: list_update_swap)
    apply (erule slice_eq_mset_subslice)
    apply auto
    done
  subgoal for xs' m
    apply (intro conjI)
    subgoal by simp
    subgoal by (auto simp: nth_list_update' slice_eq_mset_eq_length)
    subgoal by (clarsimp simp: nth_list_update' slice_eq_mset_eq_length)
    done
  subgoal for xs' m
    apply (intro conjI)
    subgoal
      apply (simp (no_asm_simp) add: slice_eq_mset_eq_length swap_nth nth_list_update) 
      using connex
      by blast
    subgoal by (fastforce simp: slice_eq_mset_eq_length swap_nth nth_list_update)
    done
  subgoal for xs' m
    apply (intro conjI)
    subgoal by (metis Suc_le_D Suc_to_right atLeastLessThan_iff le_Suc_eq le_eq_less_or_eq nz_le_conv_less slice_eq_mset_swap(1) zero_less_Suc)
    subgoal by simp 
    subgoal by simp 
    subgoal by (fastforce simp add: slice_eq_mset_eq_length swap_nth nth_list_update) 
    subgoal
      apply (simp (no_asm_simp) add: slice_eq_mset_eq_length swap_nth nth_list_update)
      apply safe
      subgoal using connex apply blast done
      subgoal by (metis atLeastLessThan_iff le_def le_eq_less_or_eq nat_le_Suc_less_imp)
      subgoal using connex by blast
      subgoal using connex by blast
      subgoal by (metis Suc_le_D Suc_le_lessD Suc_to_right atLeastLessThan_iff nat_in_between_eq(2))
      by (meson atLeastLessThan_iff le_antisym linorder_not_le nat_le_Suc_less_imp)
    done  
  subgoal for xs' m
    apply (intro conjI)
    subgoal by simp 
    subgoal
      apply (simp (no_asm_simp) add: slice_eq_mset_eq_length swap_nth nth_list_update) 
      using connex
      by blast
    subgoal
      apply (simp (no_asm_simp) add: slice_eq_mset_eq_length swap_nth nth_list_update)
      by (meson atLeastLessThan_iff connex diff_is_0_eq' diffs0_imp_equal le_def nat_le_Suc_less_imp)
    done
  done
  
  sepref_register gpartition_SPEC
  
  lemma qsg_partition_wrapper_refine: "(qsg_partition_wrapper, PR_CONST gpartition_SPEC) ∈ Id → Id → Id → Id → ⟨Id⟩nres_rel"
    unfolding qsg_partition_wrapper_def
    apply (clarsimp split!: if_split intro!: nres_relI)
    subgoal by (simp add: qsg_partition_correct)
    unfolding gpartition_SPEC_def
    apply (all refine_vcg)
    apply simp_all
    unfolding gpartition_spec_def
    apply auto
    subgoal by (metis connex le_less_Suc_eq strict_itrans wo_leD)
    subgoal by (metis le_less_Suc_eq strict_itrans wo_leI)
    done
  
  
end  
  
context sort_impl_context begin
  
sepref_register ungrd_qsg_next_l_spec ungrd_qsg_next_h_spec 
sepref_definition qsg_next_l_impl [llvm_inline] is "uncurry3 (qsg_next_l)" :: "size_assn⇧k *⇩a (arr_assn)⇧k *⇩a elem_assn⇧k *⇩a size_assn⇧k →⇩a size_assn"
  unfolding qsg_next_l_def PR_CONST_def
  apply (annot_snat_const "TYPE(size_t)")
  by sepref
lemmas [sepref_fr_rules] = qsg_next_l_impl.refine[FCOMP qsg_next_l_refine]  
  
term qsg_next_h
sepref_definition qsg_next_h_impl [llvm_inline] is "uncurry3 (qsg_next_h)" :: "size_assn⇧k *⇩a (arr_assn)⇧k *⇩a elem_assn⇧k *⇩a size_assn⇧k →⇩a size_assn"
  unfolding qsg_next_h_def PR_CONST_def
  apply (annot_snat_const "TYPE(size_t)")
  by sepref
  
lemmas [sepref_fr_rules] = qsg_next_h_impl.refine[FCOMP qsg_next_h_refine]  
  
sepref_register qsg_partition_aux  
sepref_def qsg_partition_aux_impl  is "uncurry3 (PR_CONST qsg_partition_aux)" 
  :: "[λ_. True]⇩c size_assn⇧k *⇩a size_assn⇧k *⇩a elem_assn⇧k *⇩a (arr_assn)⇧d 
    → arr_assn ×⇩a size_assn [λ(((_,_),_),ai) (r,_). r=ai]⇩c"
  unfolding qsg_partition_aux_def PR_CONST_def ungrd_qsg_next_lh_spec_def
  apply (simp only: nres_monad_laws split)
  apply (annot_snat_const "TYPE(size_t)")
  by sepref
                  
lemma unfold_let_le: "(let x = a❙≤b in f x) = (let x = ¬ b❙<a in f x)"  
  by (simp add: unfold_le_to_lt)
        
                  
  
definition "list_guarded_swap xs i j ≡ if i≠j then mop_list_swap xs i j else RETURN xs "  
  
lemma list_guarded_swap_refine[refine]: 
  "⟦ (xs,xs')∈⟨Id⟩list_rel; (i,i')∈Id; (j,j')∈Id ⟧ ⟹ list_guarded_swap xs i j ≤⇓Id (mop_list_swap xs' i' j')"
  unfolding list_guarded_swap_def
  apply simp
  apply refine_vcg
  by simp
sepref_register list_guarded_swap  
  
sepref_def list_guarded_swap_impl [llvm_inline] is "uncurry2 list_guarded_swap" 
  :: "[λ_. True]⇩c arr_assn⇧d *⇩a size_assn⇧k *⇩a size_assn⇧k → arr_assn [λ((p,_),_) r. r=p]⇩c"
  unfolding list_guarded_swap_def
  by sepref
end
context sort_impl_copy_context begin
  
  
definition "qsg_partition_swap_back hi⇩0 li hi m xs e0ok eNok ≡ do {
  if e0ok ∧ eNok then
    RETURN (xs,m)
  else if e0ok ∧ ¬eNok then do {
    xs ← list_guarded_swap xs m hi;
    ASSERT (m<hi⇩0);
    let m=m+1;
    RETURN (xs,m)
  } else if eNok then do {
    ASSERT (1≤m);
    let m=m-1;
    xs ← list_guarded_swap xs li m;
    RETURN (xs,m)
  } else do {
    xs ← list_guarded_swap xs li hi;
    RETURN (xs,m)
  }
}"
  
definition "qsg_partition2 li hi⇩0 p xs⇩0 ≡ do {
  (e0,xs) ← mop_idx_v_swap xs⇩0 li (COPY p);
  ASSERT (li<hi⇩0);
  let li=li+1;
  
  ASSERT (hi⇩0>0);
  let hi=hi⇩0-1;
  (eN,xs) ← mop_idx_v_swap xs hi (COPY p);
  (xs,m) ← qsg_partition_aux li hi p xs;
  
  ASSERT (li>0);
  let li = li-1;
  let e0ok = e0 ❙≤ p;
  let eNok = p ❙≤ eN;
  (_,xs) ← mop_idx_v_swap xs li e0;
  (_,xs) ← mop_idx_v_swap xs hi eN;
  qsg_partition_swap_back hi⇩0 li hi m xs e0ok eNok
}"
lemma qsg_partition2_refine: "(PR_CONST  qsg_partition2, PR_CONST qsg_partition) ∈ Id → Id → Id → Id → ⟨Id⟩nres_rel"
  unfolding qsg_partition2_def qsg_partition_def qsg_partition_swap_back_def PR_CONST_def
  apply refine_rcg
  apply refine_dref_type
  apply simp_all
  done
sepref_register qsg_partition_swap_back
sepref_def qsg_partition_swap_back_impl is "uncurry6 (qsg_partition_swap_back)" 
  :: "[λ_. True]⇩c size_assn⇧k *⇩a size_assn⇧k *⇩a size_assn⇧k *⇩a size_assn⇧k *⇩a (arr_assn)⇧d *⇩a bool1_assn⇧k *⇩a bool1_assn⇧k
    → arr_assn ×⇩a size_assn [λ((((((_,_),_),_),ai),_),_) (r,_). r=ai]⇩c"
  unfolding qsg_partition_swap_back_def PR_CONST_def
  apply (annot_snat_const "TYPE(size_t)")
  by sepref_dbg_keep
  
  
sepref_register qsg_partition
sepref_definition qsg_partition_impl [llvm_code]  is "uncurry3 (PR_CONST qsg_partition2)" 
  :: "[λ_. True]⇩c size_assn⇧k *⇩a size_assn⇧k *⇩a elem_assn⇧k *⇩a (arr_assn)⇧d 
    → arr_assn ×⇩a size_assn [λ(((_,_),_),ai) (r,_). r=ai]⇩c"
  unfolding qsg_partition2_def PR_CONST_def unfold_let_le
  supply [[goals_limit = 1]]
  apply (annot_snat_const "TYPE(size_t)")
  apply sepref_dbg_keep
  done
lemmas qsg_partition_impl'_hnr[sepref_fr_rules] = qsg_partition_impl.refine[FCOMP qsg_partition2_refine]
sepref_register qsg_partition_wrapper
sepref_definition qsg_partition_wrapper_impl [llvm_code] is "uncurry3 (qsg_partition_wrapper)"
  :: "[λ_. True]⇩c size_assn⇧k *⇩a size_assn⇧k *⇩a elem_assn⇧k *⇩a (arr_assn)⇧d 
    → arr_assn ×⇩a size_assn [λ(((_,_),_),ai) (r,_). r=ai]⇩c"
  unfolding qsg_partition_wrapper_def PR_CONST_def
  apply (annot_snat_const "TYPE(size_t)")
  by sepref
lemmas qsg_partition_wrapper_impl'_hnr[sepref_fr_rules] = qsg_partition_wrapper_impl.refine[FCOMP qsg_partition_wrapper_refine]  
  
end
end