Zulip Chat Archive

Stream: Program verification

Topic: How to approach proving termination here?


Lessness (Nov 28 2023 at 12:36):

I hope this is good place to ask this.

Here is the what I have:

def repeatedDiv (n f highest: Nat): Prod Nat Nat :=
    if H: 1 <= n /\ 2 <= f /\ n % f = 0
    then repeatedDiv (n / f) f (max f highest)
    else (n, highest)
termination_by repeatedDiv a b c => a
decreasing_by
    simp_wf
    cases H with | intro H H0 =>
    cases H0 with | intro H0 H1 =>
    apply Nat.div_lt_self H H0

def loop (n f highest: Nat): Prod Nat Nat :=
    if H: f * f <= n
    then match repeatedDiv n f highest with
         | (n1, highest1) => match repeatedDiv n1 (f + 2) highest1 with
                             | (n2, highest2) => loop n2 (f + 6) highest2
    else (n, highest)
termination_by loop a b c => a - b

Tactic termination_by seems to ignore/lose the link between n2 and n, because the proof state I see is such:

n f highest: Nat
H: f * f  n
n2 highest2: Nat
 n2 - (f + 6) < n - f

Am I doing this wrong way? Big thanks for any hints!

Shreyas Srinivas (Nov 28 2023 at 14:30):

try naming the match condition (similar to what you have done for if). You should get the equations that apply in each match arm as the hypothesis.

Lessness (Nov 28 2023 at 14:30):

Oh, thank you. Will try it immediately.

Lessness (Nov 28 2023 at 14:32):

Thank you, it worked! :partying_face:

Joachim Breitner (Nov 28 2023 at 14:32):

This is a good answer, and if it works, great!
Another option, especially if repeatedDivisn’t used anywhere else, is to make its type carry the fact that the result is smaller or equal,

repeatedDiv (n f highest: Nat) : ({m : Nat // m  n} × Nat) :=

Shreyas Srinivas (Nov 28 2023 at 18:45):

One question from my side: what is the parameter highest doing/denoting?

Lessness (Nov 28 2023 at 19:24):

It will be the highest divisor of n (if n isn't a prime).

Lessness (Nov 28 2023 at 19:25):

It's the 3rd exercise from Project Euler.

Lessness (Nov 29 2023 at 20:39):

How to 'force' this if .. then .. else .. construction in the proof of the repeatedDivLe to simplify to else branch?

import Mathlib

def repeatedDiv (n f highest: Nat): Prod Nat Nat :=
    if H: 1 <= n /\ 2 <= f /\ n % f = 0
    then repeatedDiv (n / f) f (max f highest)
    else (n, highest)
termination_by repeatedDiv a b c => a
decreasing_by
    simp_wf
    cases H with | intro H H0 =>
    cases H0 with | intro H0 H1 =>
    apply Nat.div_lt_self H H0

theorem repeatedDivLe (n f highest: Nat): 1 <= n -> 2 <= f -> (n % f = 0 -> False) -> (repeatedDiv n f highest).fst = n := by
    intros H H0 H1
    unfold repeatedDiv
    have H2: (1 <= n /\ 2 <= f /\ n % f = 0) -> False := by
      intros H2
      cases H2 with | intro H2 H3 =>
      cases H3 with | intro H3 H4 =>
      exact (H1 H4)
    simp
    sorry

Thank you!

Joachim Breitner (Nov 29 2023 at 20:46):

I’d start like this:

import Mathlib

def repeatedDiv (n f highest: Nat): Prod Nat Nat :=
    if H: 1 <= n /\ 2 <= f /\ n % f = 0
    then repeatedDiv (n / f) f (max f highest)
    else (n, highest)
termination_by repeatedDiv a b c => a
decreasing_by
    simp_wf
    cases H with | intro H H0 =>
    cases H0 with | intro H0 H1 =>
    apply Nat.div_lt_self H H0

theorem repeatedDivLe (n f highest: Nat): 1 <= n -> 2 <= f -> (n % f = 0 -> False) -> (repeatedDiv n f highest).fst = n := by
    intros H H0 H1
    unfold repeatedDiv
    split
    · have IH := repeatedDivLe (n / f) f (max f highest)
      sorry
    · rfl
termination_by _ a b c => a
decreasing_by
    simp_wf
    cases H with | intro H H0 =>
    cases H0 with | intro H0 H1 =>
    apply Nat.div_lt_self H H0

Lessness (Nov 29 2023 at 20:47):

Found it by some googling and luck.

theorem repeatedDivLe (n f highest: Nat): 1 <= n -> 2 <= f -> (n % f = 0 -> False) -> (repeatedDiv n f highest).fst = n := by
    intros H H0 H1
    unfold repeatedDiv
    have H2: (1 <= n /\ 2 <= f /\ n % f = 0) -> False := by
      intros H2
      cases H2 with | intro H2 H3 =>
      cases H3 with | intro H3 H4 =>
      exact (H1 H4)
    simp
    rewrite [if_neg]
    . simp
    . exact H2

Lessness (Nov 29 2023 at 20:47):

Now looking at your answer, too. Thank you!

Joachim Breitner (Nov 29 2023 at 20:55):

Ah, nevermind, your proof isn’t actually inductive, and only goes into the second case of the function definition. Then if_neg works well.

Joachim Breitner (Nov 29 2023 at 20:57):

You don’t need the first three assumptions, btw:

theorem repeatedDivLe (n f highest: Nat): ¬ n % f = 0 -> (repeatedDiv n f highest).fst = n := by
    intros  H1
    unfold repeatedDiv
    aesop

Lessness (Nov 30 2023 at 00:55):

Joachim Breitner said:

I’d start like this:

import Mathlib

def repeatedDiv (n f highest: Nat): Prod Nat Nat :=
    if H: 1 <= n /\ 2 <= f /\ n % f = 0
    then repeatedDiv (n / f) f (max f highest)
    else (n, highest)
termination_by repeatedDiv a b c => a
decreasing_by
    simp_wf
    cases H with | intro H H0 =>
    cases H0 with | intro H0 H1 =>
    apply Nat.div_lt_self H H0

theorem repeatedDivLe (n f highest: Nat): 1 <= n -> 2 <= f -> (n % f = 0 -> False) -> (repeatedDiv n f highest).fst = n := by
    intros H H0 H1
    unfold repeatedDiv
    split
    · have IH := repeatedDivLe (n / f) f (max f highest)
      sorry
    · rfl
termination_by _ a b c => a
decreasing_by
    simp_wf
    cases H with | intro H H0 =>
    cases H0 with | intro H0 H1 =>
    apply Nat.div_lt_self H H0

I tried to run your answer in live.lean-lang.org and it said at termination_by _ a b c => a:

type mismatch
  a
has type
  1  n : Prop
but is expected to have type
  ?β : Sort ?u.7785

Lessness (Nov 30 2023 at 00:57):

Why is it so? It seems to me that a denotes the first parameter, not the fourth or 1 <= n ... :O

Joachim Breitner (Nov 30 2023 at 07:57):

Ah, yes, my bad. Try

termination_by _ a b c _ _ _ => a
decreasing_by
    simp_wf
    apply Nat.div_lt_self H H0

Frédéric Dupuis (Nov 30 2023 at 15:16):

Another way to deal with these termination proofs is to just prove that the relevant number is decreasing at a strategic location in the function:

theorem repeatedDivLe (n f highest: Nat): 1 <= n -> 2 <= f -> (n % f = 0 -> False) -> (repeatedDiv n f highest).fst = n := by
    intros H H0 H1
    unfold repeatedDiv
    split
    case inl h =>
      have : n / f < n := False.elim (H1 h.2.2)
      have IH := repeatedDivLe (n / f) f (max f highest)
      sorry
    case inr h => rfl

Last updated: Dec 20 2023 at 11:08 UTC