Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I split a list in half in coq?

Tags:

rocq-prover

It looks definitely simple task until I actually try to work on it. My method is to use twin pointers to avoid asking the length of the list ahead of time, but the difficulties come from the implication that I know for sure one list is "no emptier" than another. Specifically, in pseudo-coq:

Definition twin_ptr (heads, tail, rem : list nat) :=
  match tail, rem with
  | _, [] => (rev heads, tail)
  | _, [_] => (rev heads, tail)
  | t :: tl, _ :: _ :: rm => twin_ptr (t :: heads) tl rm
  end.

Definition split (l : list nat) := twin_ptr [] l l

But definitely it's not going to compile because the match cases are incomplete. However, the missing case by construction doesn't exist.

What's your way of implementing it?

like image 467
Jason Hu Avatar asked Sep 02 '25 10:09

Jason Hu


2 Answers

I you are not afraid of dependent types, you can add a proof that rem is shorter than tail as an argument of twin_ptr. Using Program to help manage these dependent types, this could give the following.

Require Import List. Import ListNotations.
Require Import Program.
Require Import Arith.
Require Import Omega.

Program Fixpoint twin_ptr
  (heads tail rem : list nat)
  (H:List.length rem <= List.length tail) :=
  match tail, rem with
  | a1, [] => (rev heads, tail)
  | a2, [a3] => (rev heads, tail)
  | t :: tl, _ :: _ :: rm => twin_ptr (t :: heads) tl rm _
  | [], _::_::_ => !
  end.
Next Obligation.
  simpl in H. omega.
Qed.
Next Obligation.
  simpl in H. omega.
Qed.

Definition split (l : list nat) := twin_ptr [] l l (le_n _).

The exclamation mark means that a branch is unreachable.

You can then prove lemmas about twin_ptr and deduce the properties of split from them. For example,

Lemma twin_ptr_correct : forall head tail rem H h t,
  twin_ptr head tail rem H = (h, t) ->
  h ++ t = rev head ++ tail.
Proof.
Admitted.

Lemma split_correct : forall l h t,
  split l = (h, t) ->
  h ++ t = l.
Proof.
  intros. apply twin_ptr_correct in H. assumption.
Qed.

Personally, I dislike to use dependent types in functions, as resulting objects are more difficult to manipulate. Instead, I prefer defining total functions and give them the right hypotheses in the lemmas.

like image 88
eponier Avatar answered Sep 05 '25 17:09

eponier


You do not need to maintain the invariant that the second list is bigger than the third. Here is a possible solution:

Require Import Coq.Arith.PeanoNat.
Require Import Coq.Arith.Div2.
Require Import Coq.Lists.List.
Import ListNotations.

Section Split.

Variable A : Type.

Fixpoint split_aux (hs ts l : list A) {struct l} : list A * list A :=
  match l with
  | []  => (rev hs, ts)
  | [_] => (rev hs, ts)
  | _ :: _ :: l' =>
    match ts with
    | []      => (rev hs, [])
    | h :: ts => split_aux (h :: hs) ts l'
    end
  end.

Lemma split_aux_spec hs ts l n :
  n = div2 (length l) ->
  split_aux hs ts l = (rev (rev (firstn n ts) ++ hs), skipn n ts).
Proof.
revert hs ts l.
induction n as [|n IH].
- intros hs ts [|x [|y l]]; easy.
- intros hs ts [|x [|y l]]; simpl; try easy.
  intros Hn.
  destruct ts as [|h ts]; try easy.
  rewrite IH; try congruence.
  now simpl; rewrite <- app_assoc.
Qed.

Definition split l := split_aux [] l l.

Lemma split_spec l :
  split l = (firstn (div2 (length l)) l, skipn (div2 (length l)) l).
Proof.
unfold split.
rewrite (split_aux_spec [] l l (div2 (length l))); trivial.
now rewrite app_nil_r, rev_involutive.
Qed.

End Split.
like image 31
Arthur Azevedo De Amorim Avatar answered Sep 05 '25 16:09

Arthur Azevedo De Amorim