Theory Sorting_Pdq_Insertion_Sort

section Insertion Sorts for PDQ-Sort
theory Sorting_Pdq_Insertion_Sort
imports Sorting_Setup Sorting_Partially_Sorted
begin

  (* Partially overlapping with Sorting_Unguarded_Insertion_Sort, but the details of the 
    required specs are different enough, such that we decided for the redundancy, rarther than
    trying to find a good generalization.
  *)


  subsection Insertion Sort

  subsubsection Abstract Algorithm
    
  context weak_ordering begin
    definition "pdq_sort_one_more_spec GUARDED xs l i  doN {
      ASSERT (sorted_wrt_lt (<) (slice l i xs)  li  i<length xs  (¬GUARDED  0<l  ¬ xs!(l-1) > xs!i ) );
      SPEC (λxs'. slice_eq_mset l (Suc i) xs xs'  sorted_wrt_lt (<) (slice l (Suc i) xs'))
    }"
    
  
    definition "pdq_guarded_sort_spec G xs l h  doN {
      ASSERT (¬G  0<l  (xset (slice l h xs). ¬ x < xs!(l-1)));
      slice_sort_spec (<) xs l h
    }"
    
    
    definition pdq_insort where "pdq_insort GUARDED xs0 l h  doN {
      ASSERT( lh  hlength xs0  (¬GUARDED  0<l  (xset (slice l h xs0). ¬ x < xs0!(l-1))));
      
      if lh then doN {
        ASSERT (l<h);
        (_,xs)WHILEIT 
          (λ(i,xs). l<i  ih  slice_eq_mset l h xs0 xs  sorted_wrt_lt (<) (slice l i xs))
          (λ(i,xs). i<h)
          (λ(i,xs). doN {
            xs  pdq_sort_one_more_spec GUARDED xs l i;
            ASSERT (i<h);
            RETURN (i+1,xs)
          })
          (l+1,xs0);
        RETURN xs
      } else RETURN xs0
    }"
  
    
    lemma insort_correct_aux1: "hlength xs  (xset (slice l h xs). ¬ x < k)  (j{l..<h}. ¬ xs!j < k)"
      by (auto simp: Ball_def set_slice_conv)
    
      
    lemma insort_correct: "pdq_insort G xs l h  pdq_guarded_sort_spec G xs l h"
      unfolding pdq_insort_def slice_sort_spec_sem_alt pdq_guarded_sort_spec_def pdq_sort_one_more_spec_def
      apply (refine_vcg WHILEIT_rule[where R="measure (λ(i,_). h-i)"])
      apply (clarsimp_all simp: slice_eq_mset_eq_length)
      subgoal 
        apply (subst (asm) slice_eq_mset_nth_outside, assumption)
        apply (auto simp: slice_eq_mset_eq_length) [2]
        apply (simp add: slice_eq_mset_set_inside)
        apply (simp add: insort_correct_aux1 slice_eq_mset_eq_length)
        done
      subgoal by (meson Suc_leI dual_order.refl le_SucI less_imp_le slice_eq_mset_subslice slice_eq_mset_trans) 
      subgoal by simp
      done

     
    definition "pdq_is_insert GUARDED xs0 l i0  doN {
      ASSERT ( li0  i0<length xs0  (¬GUARDED  0<l  ¬(xs0!(l-1) > xs0!i0)));
      
      tmp  mop_list_get xs0 i0;
      
      (xs,i)WHILEIT 
        (λ(xs,i). 
            li  ii0
           eq_outside_range xs0 xs l (i0+1)
           (j{l..<i}. xs0!j = xs!j)
           (j{i<..i0}. xs0!(j-1) = xs!j  tmp < xs!j)
           (¬GUARDED  i1)
        )
        (λ(xs,i). (¬GUARDED  l<i)  tmp < xs!(i-1) )
        (λ(xs,i). doN {
          ASSERT (i1);
          x  mop_list_get xs (i-1);
          xs  mop_list_set xs i x;
          RETURN (xs,i-1)
        })
        (xs0,i0);
        
      xs  mop_list_set xs i tmp;  
        
      RETURN xs
    }"
      
    
    lemma is_insert_aux1: 
      assumes BOUNDS: "l  i" "i  i0" "i0<length xs" "length xs0 = length xs"
      assumes PRFX_EQ: "j{l..<i}. xs0 ! j = xs ! j"
      assumes SFX_SHIFT: "j{i<..i0}. xs0 ! (j - Suc 0) = xs ! j  xs0!i0 < xs!j"
      assumes SORTED: "sorted_wrt_lt (<) (slice l i0 xs0)"
      assumes GUARD: "l < i  ¬ xs0 ! i0 < xs ! (i - Suc 0)"
      shows "mset (slice l (Suc i0) xs0) = mset (slice l (Suc i0) (xs[i := xs0 ! i0]))"
        and "sorted_wrt_lt (<) (slice l (Suc i0) (xs[i := xs0 ! i0]))"
    proof -
      define sxs0 where "sxs0 = slice l (Suc i0) xs0"
      define sxs' where "sxs' = slice l (Suc i0) (xs[i := xs0 ! i0])" 
      
      
      have "slice l i0 xs0 = butlast sxs0"
        unfolding sxs0_def using BOUNDS
        by (simp add: slice_Suc_conv)
    
      with SORTED have SORTED': "sorted_wrt_lt (<) (butlast sxs0)" by simp 
        
      have [simp]: "length sxs0 = length sxs'"
        unfolding sxs'_def sxs0_def using BOUNDS 
        by (auto)
        
      have [simp]: "length sxs' = i0+1-l"  
        unfolding sxs'_def using BOUNDS 
        by (auto)
        
      
      from PRFX_EQ have 1: "j{0..<i-l}. sxs0 ! j = sxs' ! j"
        unfolding sxs'_def sxs0_def using BOUNDS 
        by (auto simp: slice_nth)
        
      from SFX_SHIFT have 2: "j{i-l..<i0-l}. sxs0 ! j = sxs' ! (j + 1)"
        unfolding sxs'_def sxs0_def using BOUNDS 
        apply (auto simp: slice_nth)
        by (smt One_nat_def Suc_leI add.commute add_Suc_right diff_add_inverse greaterThanAtMost_iff le_add_diff_inverse le_imp_less_Suc less_diff_conv plus_1_eq_Suc)
        
      have 3: "sxs0!(i0-l) = sxs'!(i-l)" 
        unfolding sxs'_def sxs0_def using BOUNDS 
        by (auto simp: slice_nth)

      have A: "sxs0 = take (i-l) sxs' @ drop (i-l+1) sxs' @ [sxs'!(i-l)]"  
        apply (subst list_eq_iff_nth_eq) using BOUNDS
        apply (auto simp: nth_append 1 2 3)
        by (metis "3" add_diff_cancel_right' leI le_add_diff_inverse2 less_antisym less_diff_conv)
        
      have B: "sxs' = take (i-l) sxs' @ sxs'!(i-l) # drop (i-l+1) sxs'"
        using BOUNDS by (auto simp: id_take_nth_drop)
      
      show "mset sxs0 = mset sxs'"  
        apply (rewrite in "mset  = _" A)
        apply (rewrite in "_ = mset " B)
        by simp

      from SFX_SHIFT have S: "j{i-l..<i0-l}. sxs' ! (i-l) < sxs' ! (j+1)"
        unfolding sxs'_def sxs0_def using BOUNDS 
        by (auto simp: slice_nth nth_list_update less_diff_conv)
        
      from GUARD have GUARD': "i=l  ¬ sxs'!(i-l) < sxs'!(i-Suc 0-l)"
        unfolding sxs'_def sxs0_def using BOUNDS 
        by (auto simp: slice_nth)
        
      show "sorted_wrt_lt (<) sxs'"  
        apply (rewrite B)
        apply (rule sorted_lelI)
        
        using SORTED'[unfolded A]
        apply (auto simp add: sorted_wrt_append butlast_append le_by_lt)
        subgoal
          using GUARD'
          by (simp add: add.commute BOUNDS last_take_nth_conv le_imp_less_Suc less_diff_conv less_imp_le_nat wo_leI)
        subgoal premises prems
          using S prems(1) by (auto simp: in_set_conv_nth unfold_lt_to_le)
        done
        
    qed
    
    lemma pdq_is_insert_correct: "pdq_is_insert G xs0 l i0  pdq_sort_one_more_spec G xs0 l i0"
      unfolding pdq_is_insert_def pdq_sort_one_more_spec_def
      apply (refine_vcg WHILEIT_rule[where R="measure (λ(_,i). i)"])
      apply simp_all
      apply (all (elim conjE)?)
      apply (simp_all add: slice_eq_mset_eq_length eq_outside_rane_lenD eq_outside_range_triv) 
      apply clarsimp_all
      subgoal 
        by (metis One_nat_def eq_outside_range_def last_take_nth_conv le_less_trans less_le nat_le_Suc_less_imp)
      subgoal by (simp add: eq_outside_erange_upd_inside)
      subgoal
        apply (clarsimp simp: nth_list_update)
        by (metis One_nat_def atLeastLessThan_iff eq_outside_range_def last_take_nth_conv le_neq_implies_less less_not_refl2 nat_le_Suc_less_imp nat_minus1_less_if_neZ order_trans)
      subgoal
        by (metis One_nat_def Suc_leI diff_0_eq_0 diff_is_0_eq eq_outside_range_def last_take_nth_conv le_antisym not_less_eq_eq)  
      subgoal for xs i
        apply (thin_tac "¬G  _")+
        apply (clarsimp simp: slice_eq_mset_alt)
        apply (intro conjI)
        apply (auto simp: eq_outside_erange_upd_inside eq_outside_rane_lenD intro!: is_insert_aux1)
        done
      subgoal for xs i
        apply (thin_tac "¬G  _")+
        apply (auto simp: eq_outside_rane_lenD intro!: is_insert_aux1)
        done
      done    


      
    subsubsection Loop unrolling Optimization
    text Avoid move from/to temporary variable when the element is already at correct position.
            
    definition "pdq_is_insert' GUARDED xs l i  doN {
      ASSERT ( li  i<length xs  (¬GUARDED  i1));
      if ((¬GUARDED  l<i)  xs!i < xs!(i-1)) then doN {
        tmp  mop_list_get xs i;
        ASSERT (i1);
        x  mop_list_get xs (i-1);
        xs  mop_list_set xs i x;
        (xs,i)WHILEIT (λ(xs,i). ¬GUARDED  i1)
          (λ(xs,i). (¬GUARDED  l<i)  tmp < xs!(i-1) )
          (λ(xs,i). doN {
            ASSERT (i1);
            x  mop_list_get xs (i-1);
            xs  mop_list_set xs i x;
            RETURN (xs,i-1)
          })
          (xs,i-1);
        
        xs  mop_list_set xs i tmp;
        RETURN xs
      } else RETURN xs
    }"
      
    
    lemma pdq_is_insert'_refine: "pdq_is_insert' G xs l i  pdq_is_insert G xs l i"
      unfolding pdq_is_insert_def
      
      apply (rewrite WHILEIT_unfold')
      unfolding pdq_is_insert'_def
      supply if_split[split del]
      apply (cases "(¬ G  l < i)  xs ! i < xs ! (i - 1)"; simp only:; simp)
      subgoal
        apply (rule refine_IdD)
        apply refine_vcg
        apply refine_dref_type
        apply (clarsimp_all simp only: list_rel_id_simp prod_rel_id_simp IdI)
        apply simp_all
        done
      subgoal
        by (simp add: pw_le_iff refine_pw_simps split!: if_splits)
      done
      
      
      
    subsection Maybe Insertion Sort  
    text Will stop sorting after a few swaps
    abbreviation "partial_insertion_sort_limit  8::nat"

    
    subsubsection Abstract Algorithm
    definition "maybe_sort_spec xs l h  doN {
      ASSERT (lh  hlength xs);
      SPEC (λ(r,xs'). slice_eq_mset l h xs xs'  (r  sorted_wrt_lt (<) (slice l h xs')))
    }"
      
    definition "maybe_sort_one_more_spec xs l i  doN {
      ASSERT (sorted_wrt_lt (<) (slice l i xs)  li  i<length xs );
      SPEC (λ(d,xs'). slice_eq_mset l (Suc i) xs xs'  di-l  sorted_wrt_lt (<) (slice l (Suc i) xs'))
    }"
    
    
    
    definition maybe_insort where "maybe_insort xs0 l h  doN {
      ASSERT( lh  hlength xs0 );
      
      if lh then doN {
        ASSERT (l<h);
        (i,_,xs)WHILEIT 
          (λ(i,nc,xs). 
            l<i  ih 
             slice_eq_mset l h xs0 xs 
             sorted_wrt_lt (<) (slice l i xs))
          (λ(i,nc,xs). i<h  nc>0)
          (λ(i,nc,xs). doN {
            (d,xs)  maybe_sort_one_more_spec xs l i;
            
            let nc = (if d<nc then nc-d else 0);
            ASSERT (i<h);
            RETURN (i+1,nc,xs)
          })
          (l+1,partial_insertion_sort_limit+1,xs0);
        RETURN (i=h,xs)
      } else RETURN (True,xs0)
    }"
      
                                  
    lemma maybe_insort_correct: "maybe_insort xs l h  maybe_sort_spec xs l h"
      unfolding maybe_insort_def maybe_sort_spec_def maybe_sort_one_more_spec_def
      apply (refine_vcg WHILEIT_rule[where R="measure (λ(i,_). h-i)"])
      apply (clarsimp_all simp: slice_eq_mset_eq_length)
      subgoal by (meson Suc_leI dual_order.refl le_SucI less_imp_le slice_eq_mset_subslice slice_eq_mset_trans) 
      subgoal by simp
      done

      
      
    definition "maybe_is_insert xs0 l i0  doN {
      ASSERT ( li0  i0<length xs0);
      
      tmp  mop_list_get xs0 i0;
      
      (xs,i)WHILEIT 
        (λ(xs,i). 
            li  ii0
           eq_outside_range xs0 xs l (i0+1)
           (j{l..<i}. xs0!j = xs!j)
           (j{i<..i0}. xs0!(j-1) = xs!j  tmp < xs!j)
        )
        (λ(xs,i). l<i  tmp < xs!(i-1) )
        (λ(xs,i). doN {
          ASSERT (i1);
          x  mop_list_get xs (i-1);
          xs  mop_list_set xs i x;
          RETURN (xs,i-1)
        })
        (xs0,i0);
        
      xs  mop_list_set xs i tmp;  
        
      ASSERT (ii0);
      RETURN (i0-i,xs)
    }"
      
      
    lemma maybe_is_insert_correct: "maybe_is_insert xs0 l i0  maybe_sort_one_more_spec xs0 l i0"
      unfolding maybe_is_insert_def maybe_sort_one_more_spec_def
      apply (refine_vcg WHILEIT_rule[where R="measure (λ(_,i). i)"])
      apply simp_all
      apply (all (elim conjE)?)
      apply (simp_all add: slice_eq_mset_eq_length eq_outside_rane_lenD eq_outside_range_triv) 
      apply clarsimp_all
      subgoal by (simp add: eq_outside_erange_upd_inside)
      subgoal
        by (auto simp: nth_list_update)
      subgoal for xs i
        apply (clarsimp simp: slice_eq_mset_alt)
        apply (intro conjI)
        apply (auto simp: eq_outside_erange_upd_inside eq_outside_rane_lenD intro: is_insert_aux1)
        done
      subgoal for xs i
        apply (auto simp: eq_outside_rane_lenD intro: is_insert_aux1)
        done
      done    
      

    subsubsection Loop Unrolling Optimization  
            
    definition "maybe_is_insert' xs l i0  doN {
      let i = i0;
      ASSERT ( li  i<length xs);

      if (l<i  xs!i < xs!(i-1)) then doN {
        tmp  mop_list_get xs i;
        
        ASSERT (i1);
        x  mop_list_get xs (i-1);
        xs  mop_list_set xs i x;
        
        (xs,i)WHILET 
          (λ(xs,i). l<i  tmp < xs!(i-1) )
          (λ(xs,i). doN {
            ASSERT (i1);
            x  mop_list_get xs (i-1);
            xs  mop_list_set xs i x;
            RETURN (xs,i-1)
          })
          (xs,i-1);
          
        xs  mop_list_set xs i tmp;  
        ASSERT (ii0);
        RETURN (i0-i,xs)
      } else RETURN (0,xs)
    }"
      
      
    lemma maybe_is_insert'_refine: "maybe_is_insert' xs l i  maybe_is_insert xs l i"
      unfolding maybe_is_insert_def
      
      apply (rewrite WHILEIT_unfold')
      unfolding maybe_is_insert'_def Let_def
      supply if_split[split del]
      apply (cases "l < i  xs ! i < xs ! (i - 1)"; simp only:; simp)
      subgoal
        apply (rule refine_IdD)
        apply refine_vcg
        apply refine_dref_type
        apply (clarsimp_all simp only: list_rel_id_simp prod_rel_id_simp IdI)
        apply simp_all
        done
      subgoal
        by (simp add: pw_le_iff refine_pw_simps split!: if_splits)
      done
      

  subsection Refinements to Explicit Ownership      
      
    definition "pdq_is_insert2' GUARDED xs l i  doN {
      xs  mop_to_eo_conv xs;
      
      ifN (if (¬GUARDED  l<i) then doN { ASSERT (i1); mop_cmpo_idxs xs i (i-1) } else RETURN False) then doN {
      
        (tmp,xs)  mop_eo_extract xs i;

        ASSERT (i1);
        (x,xs)  mop_eo_extract xs (i-1);
        xs  mop_eo_set xs i x;
                
        (xs,i)monadic_WHILEIT 
          (λ_. True)
          (λ(xs,i). if (¬GUARDED  l<i) then doN { ASSERT (i1); mop_cmpo_v_idx xs tmp (i-1) } else RETURN False )
          (λ(xs,i). doN {
            ASSERT (i>0);
            (x,xs)  mop_eo_extract xs (i-1);
            xs  mop_eo_set xs i x;
            RETURN (xs,i-1)
          })
          (xs,i-1);
        xs  mop_eo_set xs i tmp;  
        xs  mop_to_wo_conv xs;
        RETURN xs
      } else mop_to_wo_conv xs
    }"
      
    definition "pdq_ii2_loop_rel  {((xs',i'), (xs,i)). i'=i  length xs' = length xs  i<length xs  (j{0..<length xs}-{i}. xs'!j = Some (xs!j))  xs'!i=None}"
    
    lemma pdq_is_insert2'_refine: "pdq_is_insert2' G xs l i (Idlist_rel) (pdq_is_insert' G xs l i)"
    proof -
    
      have 1: "mop_cmpo_idxs (map Some xs) i (i - 1) = doN { ASSERT (i<length xs); RETURN (xs!i < xs!(i-1)) }"
        by (auto simp: pw_eq_iff refine_pw_simps)
      
      show ?thesis
        unfolding pdq_is_insert2'_def pdq_is_insert'_def
        supply [simp del] = conc_Id
        supply [split del] = if_split
        apply (
          cases "¬ G  l < i"; 
          simp named_ss HOL_ss: split del: if_split;
          cases "xs ! i < xs ! (i - 1)"; 
          simp named_ss HOL_ss: mop_to_eo_conv_def nres_monad_laws 1 split del: if_split)
        subgoal 
          apply (intro refine0; simp)
          apply linarith
          apply (rule refine)
          apply (rule refine)
          apply blast
          apply (rule refine)
          apply (rule monadic_WHILEIT_refine_WHILEIT[where R=pdq_ii2_loop_rel])
          subgoal by (auto simp: pdq_ii2_loop_rel_def nth_list_update split: if_splits)
          subgoal by simp
          subgoal
            apply (thin_tac "G  _")+
            apply (clarsimp split: prod.splits if_splits simp: pdq_ii2_loop_rel_def)
            apply refine_vcg
            apply (auto) 
            done
          subgoal  
            apply (thin_tac "G  _")+
            apply clarsimp
            apply refine_vcg
            unfolding pdq_ii2_loop_rel_def
            apply (auto simp: nth_list_update split: if_splits)
            done
          subgoal
            apply (thin_tac "G  _")+
            apply refine_vcg
            apply (auto simp: pdq_ii2_loop_rel_def nth_list_update in_set_conv_nth split: if_split intro: list_eq_iff_nth_eq[THEN iffD2])  
            done
          done
        subgoal by refine_rcg auto
        subgoal by refine_rcg auto
        subgoal by refine_rcg auto
        done
    qed  
      
    definition "pdq_is_insert2 GUARDED xs0 l i0  doN {
      xs  mop_to_eo_conv xs0;
      
      (tmp,xs)  mop_eo_extract xs i0;
      
      (xs,i)monadic_WHILEIT 
        (λ(xs,i). True
        )
        (λ(xs,i). if (¬GUARDED  l<i) then doN { ASSERT (i>0); mop_cmpo_v_idx xs tmp (i-1) } else RETURN False )
        (λ(xs,i). doN {
          ASSERT (i>0);
          (x,xs)  mop_eo_extract xs (i-1);
          xs  mop_eo_set xs i x;
          RETURN (xs,i-1)
        })
        (xs,i0);
        
      xs  mop_eo_set xs i tmp;  
        
      xs  mop_to_wo_conv xs;
      
      RETURN xs
    }"
      
    lemma pdq_is_insert2_refine: "pdq_is_insert2 G xs l i (Idlist_rel) (pdq_is_insert G xs l i)"
      unfolding pdq_is_insert2_def pdq_is_insert_def
      supply [simp del] = conc_Id
      
      apply simp
      apply (intro refine0; simp)
      apply (rule refine)
      apply (rule monadic_WHILEIT_refine_WHILEIT[where R=pdq_ii2_loop_rel])
      subgoal by (auto simp: pdq_ii2_loop_rel_def)
      subgoal by simp
      subgoal
        apply (clarsimp split: prod.splits simp: pdq_ii2_loop_rel_def)
        apply refine_vcg
        apply (auto)
        done
      subgoal  
        apply clarsimp
        apply refine_vcg
        unfolding pdq_ii2_loop_rel_def
        apply (auto simp: nth_list_update split: if_splits)
        done
      subgoal
        apply refine_vcg
        apply (auto simp: pdq_ii2_loop_rel_def nth_list_update in_set_conv_nth intro: list_eq_iff_nth_eq[THEN iffD2])  
        done
      done
      

      
    definition "maybe_is_insert2 xs0 l i0  doN {
      ASSERT ( li0  i0<length xs0);

      xs  mop_to_eo_conv xs0;
            
      (tmp,xs)  mop_eo_extract xs i0;
      
      (xs,i)monadic_WHILEIT 
        (λ(xs,i). True)
        (λ(xs,i). if l<i then doN { ASSERT (i>0); mop_cmpo_v_idx xs tmp (i-1) } else RETURN False )
        (λ(xs,i). doN {
          ASSERT (i>0);
          (x,xs)  mop_eo_extract xs (i-1);
          xs  mop_eo_set xs i x;
          RETURN (xs,i-1)
        })
        (xs,i0);
        
      xs  mop_eo_set xs i tmp;  
      
      xs  mop_to_wo_conv xs;
        
      ASSERT (ii0);
      RETURN (i0-i,xs)
    }"
      
    lemma maybe_is_insert2_refine: "maybe_is_insert2 xs l i (Id ×r Idlist_rel) (maybe_is_insert xs l i)"
      unfolding maybe_is_insert2_def maybe_is_insert_def
      supply [simp del] = conc_Id
      
      apply simp
      apply (intro refine0; simp)
      apply (rule refine)
      apply (rule monadic_WHILEIT_refine_WHILEIT[where R=pdq_ii2_loop_rel])
      subgoal by (auto simp: pdq_ii2_loop_rel_def)
      subgoal by simp
      subgoal
        apply (clarsimp split: prod.splits simp: pdq_ii2_loop_rel_def)
        apply refine_vcg
        apply (auto)
        done
      subgoal  
        apply clarsimp
        apply refine_vcg
        unfolding pdq_ii2_loop_rel_def
        apply (auto simp: nth_list_update split: if_splits)
        done
      subgoal
        apply refine_vcg
        apply (auto simp: pdq_ii2_loop_rel_def nth_list_update in_set_conv_nth intro: list_eq_iff_nth_eq[THEN iffD2])  
        done
      done

      
    definition "maybe_is_insert2' xs l i0  doN {
      let i=i0;
      ASSERT ( li  i<length xs);
      xs  mop_to_eo_conv xs;
      
      ifN (if l<i then doN { ASSERT (i>0); mop_cmpo_idxs xs i (i-1) } else RETURN False) then doN {
        (tmp,xs)  mop_eo_extract xs i0;
        
        ASSERT (i1);
        (x,xs)  mop_eo_extract xs (i-1);
        xs  mop_eo_set xs i x;
        
        (xs,i)monadic_WHILEIT 
          (λ_. True)
          (λ(xs,i). if l<i then doN { ASSERT (i1); mop_cmpo_v_idx xs tmp (i-1) } else RETURN False )
          (λ(xs,i). doN {
            ASSERT (i1);
            (x,xs)  mop_eo_extract xs (i-1);
            xs  mop_eo_set xs i x;
            RETURN (xs,i-1)
          })
          (xs,i-1);
          
        xs  mop_eo_set xs i tmp;  
        xs  mop_to_wo_conv xs;
          
        ASSERT (ii0);
        RETURN (i0-i,xs)
      } else doN {
        xs  mop_to_wo_conv xs;
        RETURN (0,xs)
      }
    }"
      
    lemma maybe_is_insert2'_refine: "maybe_is_insert2' xs l i (Id ×r Idlist_rel) (maybe_is_insert' xs l i)"
    proof -
      have 1: "mop_cmpo_idxs (map Some xs) i (i - 1) = doN { ASSERT (i<length xs); RETURN (xs!i < xs!(i-1)) }"
        by (auto simp: pw_eq_iff refine_pw_simps)
    
      show ?thesis
        unfolding maybe_is_insert2'_def maybe_is_insert'_def Let_def
        supply [simp del] = conc_Id
        supply [split del] = if_split
        apply (
          cases "l < i"; 
          simp named_ss HOL_ss: split del: if_split;
          cases "xs ! i < xs ! (i - 1)"; 
          simp named_ss HOL_ss: mop_to_eo_conv_def nres_monad_laws 1 split del: if_split)
          
        subgoal 
          apply (intro refine0; simp)
          apply (intro refine0; simp)
          apply (rule refine)
          apply (rule monadic_WHILEIT_refine_WHILET[where R=pdq_ii2_loop_rel])
          subgoal by (auto simp: pdq_ii2_loop_rel_def nth_list_update split: if_splits)
          subgoal
            apply (clarsimp split: prod.splits simp: pdq_ii2_loop_rel_def split: if_split)
            apply refine_vcg
            apply (auto)
            done
          subgoal  
            apply clarsimp
            apply refine_vcg
            unfolding pdq_ii2_loop_rel_def
            apply (auto simp: nth_list_update split: if_splits)
            done
          subgoal
            apply refine_vcg
            apply (auto simp: pdq_ii2_loop_rel_def nth_list_update in_set_conv_nth split: if_splits intro: list_eq_iff_nth_eq[THEN iffD2])  
            done
          done
        subgoal by refine_rcg auto
        subgoal by refine_rcg auto
        subgoal by refine_rcg auto
        done
        
    qed
                        
  end

  
subsection Refinement to Isabelle-LLVM  
  
context sort_impl_context begin
  sepref_register 
    pdq_is_guarded_insert3: "pdq_is_insert2 True"
    pdq_is_unguarded_insert3: "pdq_is_insert2 False"
    maybe_is_insert3: "maybe_is_insert2"
  
    
  term arr_assn  
    
  sepref_def pdq_is_guarded_insert_impl [llvm_inline] is "uncurry2 (PR_CONST (pdq_is_insert2' True))" 
    :: "[λ_.True]c arr_assnd *a size_assnk *a size_assnk  arr_assn [λ((a,_),_) r. r=a]c"
    unfolding pdq_is_insert2'_def PR_CONST_def
    apply (simp named_ss HOL_ss:)
    supply [[goals_limit = 1]]
    apply (annot_snat_const "TYPE(size_t)")
    by sepref

  sepref_def pdq_is_unguarded_insert_impl [llvm_inline] is "uncurry2 (PR_CONST (pdq_is_insert2' False))" 
    :: "[λ_.True]c arr_assnd *a size_assnk *a size_assnk  arr_assn [λ((a,_),_) r. r=a]c"
    unfolding pdq_is_insert2'_def PR_CONST_def
    apply (simp named_ss HOL_ss:)
    supply [[goals_limit = 1]]
    apply (annot_snat_const "TYPE(size_t)")
    by sepref

  sepref_def maybe_is_insert_impl [llvm_inline] is "uncurry2 (PR_CONST maybe_is_insert2')" 
    :: "[λ_.True]c arr_assnd *a size_assnk *a size_assnk  size_assn ×a arr_assn [λ((a,_),_) (_,r). r=a]c"
    unfolding maybe_is_insert2'_def PR_CONST_def
    supply [[goals_limit = 1]]
    apply (annot_snat_const "TYPE(size_t)")
    by sepref
    

  sepref_register 
    pdq_is_guarded_som: "pdq_sort_one_more_spec True"
    pdq_is_unguarded_som: "pdq_sort_one_more_spec False"
    maybe_sort_one_more_spec
    
   lemma pdq_is_insert2_refine': "(PR_CONST (pdq_is_insert2 G), PR_CONST (pdq_sort_one_more_spec G))Id  Id  Id  Idnres_rel"
   proof -
     note pdq_is_insert2_refine also note pdq_is_insert_correct 
     finally show ?thesis by (auto intro!: nres_relI) 
   qed

   lemma pdq_is_insert2'_refine': "(PR_CONST (pdq_is_insert2' G), PR_CONST (pdq_sort_one_more_spec G))Id  Id  Id  Idnres_rel"
   proof -
     note pdq_is_insert2'_refine also note pdq_is_insert'_refine also note pdq_is_insert_correct 
     finally show ?thesis by (auto intro!: nres_relI) 
   qed
   
   lemma maybe_is_insert2_refine': "(PR_CONST (maybe_is_insert2), PR_CONST (maybe_sort_one_more_spec))Id  Id  Id  Idnres_rel"
   proof -
     note maybe_is_insert2_refine also note maybe_is_insert_correct 
     finally show ?thesis by (auto intro!: nres_relI) 
   qed
      
   lemma maybe_is_insert2'_refine': "(PR_CONST (maybe_is_insert2'), PR_CONST (maybe_sort_one_more_spec))Id  Id  Id  Idnres_rel"
   proof -
     note maybe_is_insert2'_refine also note maybe_is_insert'_refine also note maybe_is_insert_correct 
     finally show ?thesis by (auto intro!: nres_relI) 
   qed
   
     
   lemmas [sepref_fr_rules] = 
     pdq_is_unguarded_insert_impl.refine[FCOMP pdq_is_insert2'_refine']  
     pdq_is_guarded_insert_impl.refine[FCOMP pdq_is_insert2'_refine']  
     maybe_is_insert_impl.refine[FCOMP maybe_is_insert2'_refine']     
    

     
  sepref_register 
    pdq_unguarded_insort: "pdq_insort False"
    pdq_guarded_insort: "pdq_insort True"
    maybe_insort
    
  sepref_def pdq_unguarded_insort_impl is "uncurry2 (PR_CONST (pdq_insort False))" 
    :: "[λ_.True]c arr_assnd *a size_assnk *a size_assnk  arr_assn [λ((a,_),_) r. r=a]c"
    unfolding pdq_insort_def PR_CONST_def
    apply (simp named_ss HOL_ss:)
    apply (annot_snat_const "TYPE(size_t)")
    by sepref
    
  sepref_def pdq_guarded_insort_impl is "uncurry2 (PR_CONST (pdq_insort True))" 
    :: "[λ_.True]c arr_assnd *a size_assnk *a size_assnk  arr_assn [λ((a,_),_) r. r=a]c"
    unfolding pdq_insort_def PR_CONST_def
    apply (simp named_ss HOL_ss:)
    apply (annot_snat_const "TYPE(size_t)")
    by sepref
     
  sepref_def maybe_insort_impl is "uncurry2 (PR_CONST maybe_insort)" 
    :: "[λ_.True]c arr_assnd *a size_assnk *a size_assnk  bool1_assn ×a arr_assn [λ((a,_),_) (_,r). r=a]c"
    unfolding maybe_insort_def PR_CONST_def
    apply (simp named_ss HOL_ss:)
    apply (annot_snat_const "TYPE(size_t)")
    by sepref
    
    
  definition "pdq_insort_decG G xs l h  if G then pdq_insort True xs l h else pdq_insort False xs l h"  
  lemma insort_is_decG: "pdq_insort = pdq_insort_decG" unfolding pdq_insort_decG_def by (auto intro!: ext)
    
  sepref_definition pdq_insort_impl [llvm_inline] is "uncurry3 (PR_CONST pdq_insort_decG)" 
    :: "[λ_.True]c bool1_assnk *a arr_assnd *a size_assnk *a size_assnk  arr_assn [λ(((_,a),_),_) r. r=a]c"
    unfolding pdq_insort_decG_def PR_CONST_def by sepref
     
    
  sepref_register 
    pdq_guarded_sort_spec
    maybe_sort_spec
    
  lemma pdq_insort_refine': "(PR_CONST pdq_insort_decG, PR_CONST pdq_guarded_sort_spec)  Id  Id  Id  Id  Idnres_rel"
    using insort_correct by (auto intro!: nres_relI simp: insort_is_decG)
    
  lemma maybe_insort_refine': "(PR_CONST maybe_insort, PR_CONST maybe_sort_spec)  Id  Id  Id  Idnres_rel"  
    using maybe_insort_correct by (auto intro!: nres_relI)
    
  lemmas [sepref_fr_rules] = 
    pdq_insort_impl.refine[FCOMP pdq_insort_refine']  
    maybe_insort_impl.refine[FCOMP maybe_insort_refine']
    
             
end    
  
(* TODO: Parameterized comparisons *)  

end