Zulip Chat Archive

Stream: Is there code for X?

Topic: Nat.choose_eq_one_iff


Antoine Chambert-Loir (Jul 14 2025 at 00:34):

I'm not particularly satisfied with this proof that cries for some golfing…

import Mathlib

theorem Nat.choose_eq_one_iff {n p : } : n.choose p = 1  p = 0  n = p := by
  rcases n.eq_zero_or_pos with rfl | hn
  · rcases p.eq_zero_or_pos with rfl | hp
    · simp
    · simp [Nat.choose_eq_zero_of_lt hp, eq_comm, Nat.ne_zero_of_lt hp]
  constructor
  · intro h
    rcases p.eq_zero_or_pos with rfl | hp
    · simp
    right
    apply le_antisymm
    · rw [ not_lt]
      intro hp'
      apply Nat.not_succ_le_self 1
      conv_rhs => rw [ h]
      rw [Nat.choose_eq_choose_pred_add hn hp]
      simp only [Nat.succ_eq_add_one]
      apply Nat.add_le_add <;>
        rw [Nat.one_le_iff_ne_zero, ne_eq, Nat.choose_eq_zero_iff, not_lt]
      · refine Nat.sub_le_sub_right ?_ 1
        apply le_of_lt hp'
      · exact (Nat.le_sub_one_iff_lt hn).mpr hp'
    · rw [ not_lt]
      intro hp'
      simp [Nat.choose_eq_zero_of_lt hp'] at h
  · rintro (rfl | rfl) <;> simp

Kyle Miller (Jul 14 2025 at 00:44):

This has a non-terminal simp... but it's a lot shorter:

import Mathlib

theorem Nat.choose_eq_one_iff {n p : } : n.choose p = 1  p = 0  n = p := by
  induction n generalizing p
    <;> cases p
    <;> simp [Nat.choose_succ_succ, Nat.add_eq_one_iff, Nat.choose_eq_zero_iff, *]
  omega

Kyle Miller (Jul 14 2025 at 00:46):

Here's a structured one:

import Mathlib

theorem Nat.choose_eq_one_iff {n p : } : n.choose p = 1  p = 0  n = p := by
  induction n generalizing p with
  | zero => cases p <;> simp
  | succ n ih =>
    cases p with
    | zero => simp
    | succ p =>
      simp only [Nat.choose_succ_succ, Nat.choose_eq_zero_iff, Nat.add_eq_one_iff, ih]
      omega

Antoine Chambert-Loir (Jul 14 2025 at 00:47):

great! docs#Nat.add_eq_one_iff is quite helpful!

Jeremy Tan (Jul 14 2025 at 00:49):

theorem Nat.choose_eq_one_iff {n k : } : n.choose k = 1  k = 0  n = k := by
  constructor <;> intro h
  · rcases lt_trichotomy k n with hk | rfl | hk
    · rw [ add_one_le_iff] at hk
      have := choose_mono k hk
      simp_rw [choose_succ_self_right, h] at this; omega
    · tauto
    · simp [choose_eq_zero_of_lt hk] at h
  · rcases h with rfl | rfl <;> simp

Kenny Lau (Jul 14 2025 at 09:54):

import Mathlib.Data.Nat.Choose.Basic

theorem Nat.choose_eq_one_iff {n k : } : n.choose k = 1  k = 0  n = k :=
  fun h  or_iff_not_imp_right.2 fun hnk  (lt_or_gt_of_ne hnk).elim
    (fun hnk  Nat.zero_ne_one (choose_eq_zero_of_lt hnk  h) |>.elim)
    (by simpa [h] using choose_mono k ·),
  fun h  h.elim (·  by simp) (·  by simp)

Ruben Van de Velde (Jul 14 2025 at 09:58):

That's scoring pretty poorly on the readability scale

Eric Wieser (Jul 14 2025 at 10:17):

Less readable?

theorem Nat.choose_pos_iff {n k : } : 0 < n.choose k  k  n :=
  fun h => le_of_not_gt fun h' => (choose_eq_zero_of_lt h').not_gt h, Nat.choose_pos

theorem Nat.choose_eq_one_iff {n k : } : n.choose k = 1  k = 0  n = k where
  mp h := or_iff_not_imp_right.2 fun hnk  by
    simpa [h] using choose_mono k <| lt_of_le_of_ne' (Nat.choose_pos_iff.mp <| by omega) hnk
  mpr | .inr rfl | .inl rfl => by simp

Ruben Van de Velde (Jul 14 2025 at 10:26):

That's a little better, so I made it worse again:

theorem Nat.choose_pos_iff {n k : } : 0 < n.choose k  k  n :=
  (le_of_not_gt <| mt choose_eq_zero_of_lt <| ne_of_gt ·), Nat.choose_pos

Kenny Lau (Jul 14 2025 at 10:58):

Ruben Van de Velde said:

That's scoring pretty poorly on the readability scale

import Mathlib.Data.Nat.Choose.Basic

theorem Nat.choose_eq_one_iff {n k : } : n.choose k = 1  k = 0  n = k := by
  refine fun n_choose_k_eq_1  ?_, fun k_eq_0_or_n_eq_k  ?_⟩
  · rcases lt_trichotomy k n with k_lt_n | rfl | n_lt_k
    · left; simpa [n_choose_k_eq_1] using choose_mono k k_lt_n
    · tauto
    · simp [choose_eq_zero_of_lt n_lt_k] at n_choose_k_eq_1
  · rcases k_eq_0_or_n_eq_k with rfl | rfl <;> simp

I've made it more like Jeremy's original proof

Bhavik Mehta (Jul 14 2025 at 14:47):

Ruben Van de Velde said:

That's a little better, so I made it worse again:

theorem Nat.choose_pos_iff {n k : } : 0 < n.choose k  k  n :=
  (le_of_not_gt <| mt choose_eq_zero_of_lt <| ne_of_gt ·), Nat.choose_pos

theorem Nat.choose_pos_iff {n k : } : 0 < n.choose k  k  n := by
  simpa [pos_iff_ne_zero] using choose_eq_zero_iff.not

Last updated: Dec 20 2025 at 21:32 UTC