kururuさんがたらいまわしの証明をしていたのを見て

http://d.hatena.ne.jp/kururu_goedel/20100702

これを見て、とりあえず綺麗に書き直そうとしてみたら一日かかった。
しかし、これで一般たらいまわしへの道も見えてきたかも?

ここに置いた。http://github.com/kik/sandbox/blob/master/coq/tarai.v

多少、解説

Fixpoint ntarai3 (n : nat) (a b c : option Z) :=
match a with 
| None => None
| Some av =>
  match b with 
  | None => None
  | Some bv => 
      if Z_le_dec av bv then b else
      match n with 
      | 0%nat => None
      | S np => 
          match c with 
          | None => None
          | Some cv => 
              (ntarai3 np 
                (ntarai3 np (Some (av-1)) b c)
                (ntarai3 np (Some (bv-1)) c a)
                (ntarai3 np (Some (cv-1)) a b))
          end 
      end
  end
end.

くるるさんのからこぴってきたところ。call-by-nameなので必要になるまで option Z の中身はみない。
この定義のまま、証明を始めるとfixやらunfoldやらすさまじい項管理能力が必要で、まったく本質でないところで苦労する。というかした。

こういうときは、Inductive を使って、ntarai3 関数を特徴付けてやればよい

Inductive Tarai_value (n: nat) (a b c: option Z) (v: Z) : Prop :=
| Tarai_a_le_b (av bv: Z)
     (Hle: av <= bv) (Hv: bv = v) (Ha: a = Some av) (Hb: b = Some bv): Tarai_value n a b c v
| Tarai_a_gt_b_1 (av bv cv: Z) (n': nat) (Hgt: av > bv)
     (Ha: a = Some av) (Hb: b = Some bv) (Hc: c = Some cv)
     (Hn: n = S n')
     (v1: Z)
     (Hle2: v1 <= v)
     (Hrec1: Tarai_value n' (Some (av-1)) b c v1)
     (Hrec2: Tarai_value n' (Some (bv-1)) c a v)
| Tarai_a_gt_b_2 (av bv cv: Z) (n': nat) (Hgt: av > bv)
     (Ha: a = Some av) (Hb: b = Some bv) (Hc: c = Some cv)
     (Hn: n = S n')
     (v1 v2 v3: Z)
     (Hle2: ~v1 <= v2)
     (Hrec1: Tarai_value n' (Some (av-1)) b c v1)
     (Hrec2: Tarai_value n' (Some (bv-1)) c a v2)
     (Hrec3: Tarai_value n' (Some (cv-1)) a b v3)
     (Hrec4: Tarai_value n' (Some v1)
                                   (Some v2)
                                   (Some v3) v): Tarai_value n a b c v.

Tarai_value n a b c v という述語は、ntarai3 n a b c が停止して、結果が v になるという意味。
それは、結局3通りしかなくて、a <= b のときには停止して、b = v である(Tarai_a_le_b)。
a > b かつ n > 0 かつ Tarai_value (n-1) (a-1) b c v1 かつ Tarai_value (n-1) c a v かつ、v1 <= v のとき(Tarai_a_gt_b_1)。a > b かつ n > 0 かつ v1 > v2 たらい回しの結果が v になる場合(Tarai_a_gt_b_2)。

あと、こうやって名前をつけとくとdestructしたときにこの名前を使ってくれて読みやすい。

最初に、Tarai_value が正しく定義されてることを証明します。

Lemma lem_saficient:
  forall n a b c v, Tarai_value n a b c v -> ntarai3 n a b c = Some v.
Lemma lem_invert:
  forall n a b c v, ntarai3 n a b c = Some v -> Tarai_value n a b c v.

この二つを証明すれば、もう fix のことは忘れて、Tarai_value に専念できます。この証明は大変でした。特に下のやつ。

ここまで終われば、相当簡単になります(Coqに関しては、証明自体は複雑だけど)。

どれくらい簡単になるかというと

bv : Z
cv : Z
H : bv <= cv
______________________________________(1/2)
Tarai_value 1 (Some (bv + 1)) (Some bv) (Some cv) cv

を証明したいと思ったら、bv+1 > bv なので、Tarai_a_gt_b_1 か Tarai_a_gt_b_2 のどちらかで停止することになります。とりあえず、Tarai_a_gt_b_1 を apply してみると

apply Tarai_a_gt_b_1 with (bv + 1) bv cv 0%nat bv; auto; try omega.

これで、証明しないといけないことが2つになります。

bv : Z
cv : Z
H : bv <= cv
______________________________________(1/3)
Tarai_value 0 (Some (bv + 1 - 1)) (Some bv) (Some cv) bv


______________________________________(2/3)
Tarai_value 0 (Some (bv - 1)) (Some cv) (Some (bv + 1)) cv

上のほうは、bv + 1 - 1 <= bv なので、Tarai_a_le_b により停止します。下のほうも bv - 1 <= cv なので同様です。

apply Tarai_a_le_b with (bv + 1 - 1) bv; auto; omega.
apply Tarai_a_le_b with (bv - 1) cv; auto; omega.

これで、できあがりです。

結構こつをつかんだので、一般たらいまわしもいけるかなあ…