Theory Alternating_Permutations

section ‹Alternating permutations›
theory Alternating_Permutations
  imports "HOL-Combinatorics.Combinatorics" Boustrophedon_Transform_Library
begin

text ‹
  Given a strict linear order $<$ on some finite set $A = \{a_1, \ldots, a_n\}$ with
  $a_1 < \ldots < a_n$ we call a permutation $\pi$ ‹alternating› if
  $f(a_1) > f(a_2) < f(a_3) > f_(a_4) \ldots$.

  Since it is somewhat awkward to specify this for a function, we instead define what an
  alternating permutation is using the view that a permutation on $A$ is simple the tuple
  $(f_(a_1), \ldots, f(a_n))$.
›

subsection ‹Alternating lists›

text ‹
  Given a relation $R$, we say that a list $[x_1, \ldots, x_n]$ is ‹$R$-alternating› if
  we have $(x_i, x_{i+1}) \in R$ for any even $i$ and $(x_{i+1}, x_i)\in R$ for any odd $i$.

  In other words: if we view $R$ as an order then the list alternates between ``rises`` and
  ``falls``, starting with a ``fall''.
›

fun alternating_list :: "('a × 'a) set  'a list  bool" where
  "alternating_list R []  True"
| "alternating_list R [x]  True"
| "alternating_list R (x # y # xs)  (y,x)  R  alternating_list (R¯) (y # xs)"

lemma alternating_list_Cons_iff:
  "alternating_list R (x # xs)  xs = []  ((hd xs, x)  R  alternating_list (converse R) xs)"
  by (cases xs) auto

lemma alternating_list_append_iff:
  "alternating_list R (xs @ ys)  (let R' = if even (length xs) then R else converse R in
     alternating_list R xs  alternating_list R' ys  (xs = []  ys = []  (last xs, hd ys)  R'))"
  by (induction R xs rule: alternating_list.induct)
     (auto simp: Let_def alternating_list_Cons_iff)


text ‹
  A reverse-alternating list is the same as an alternating list except that it starts with a
  ``rise'' instead of a ``fall''. Equivalently, a reverse-alternating list is an alternating list
  with respect to the converse relation.
›
abbreviation rev_alternating_list :: "('a × 'a) set  'a list  bool" where
  "rev_alternating_list R  alternating_list (R¯)"

lemma alternating_list_rev:
  "alternating_list R (rev xs)  alternating_list (if odd (length xs) then R else converse R) xs"
  by (induction xs arbitrary: R)
     (auto simp: alternating_list_append_iff last_rev alternating_list_Cons_iff)

lemma alternating_list_map:
  assumes "alternating_list R xs"
  assumes "monotone_on (set xs) (λx y. (x, y)  R) (λx y. (x, y)  R') f"
  shows   "alternating_list R' (map f xs)"
proof -
  define A where "A = set xs"
  have "(f x, f y)  R'" if "(x, y)  R" "x  A" "y  A" for x y
    using assms(2) that by (auto simp: monotone_on_def A_def)
  moreover have "set xs  A"
    by (simp add: A_def)
  ultimately show ?thesis using assms(1)
    by (induction R xs arbitrary: R' rule: alternating_list.induct) auto
qed

lemma alternating_list_map_iff:
  assumes "monotone_on (set xs) (λx y. (x, y)  R) (λx y. (x, y)  R') f"
  assumes "strict_linear_order_on (set xs) R" "strict_linear_order_on (f ` set xs) R'"
  shows   "alternating_list R' (map f xs)  alternating_list R xs"
proof
  assume "alternating_list R xs"
  thus "alternating_list R' (map f xs)"
    by (intro alternating_list_map) (use assms in simp_all)
next
  assume "alternating_list R' (map f xs)"
  hence "alternating_list R (map (inv_into (set xs) f) (map f xs))"
  proof (rule alternating_list_map)
    have "monotone_on (f ` set xs) (λx y. (x, y)  R') (λx y. (x, y)  R) (inv_into (set xs) f)"
      by (rule monotone_on_inv_into) (use assms in simp_all)
    thus "monotone_on (set (map f xs)) (λx y. (x, y)  R') (λx y. (x, y)  R) (inv_into (set xs) f)"
      by simp
  qed
  also have "map (inv_into (set xs) f) (map f xs) = map (λx. x) xs"
    unfolding map_map o_def
    by (intro map_cong inv_into_f_f monotone_on_imp_inj_on[OF assms(1)])
       (use assms in simp_all)
  finally show "alternating_list R xs"
    by simp
qed


subsection ‹The set of alternating permutations on a set›

definition alternating_permutations_of_set :: "('a × 'a) set  'a set  'a list set" where
  "alternating_permutations_of_set R A = {yspermutations_of_set A. alternating_list R ys}"

lemma finite_alternating_permutations_of_set [intro]: "finite (alternating_permutations_of_set R A)"
  unfolding alternating_permutations_of_set_def by simp

lemma alternating_permutations_of_set_code [code]:
  "alternating_permutations_of_set R A = Set.filter (alternating_list R) (permutations_of_set A)"
  by (simp add: alternating_permutations_of_set_def Set.filter_def)

abbreviation rev_alternating_permutations_of_set :: "('a × 'a) set  'a set  'a list set" where
  "rev_alternating_permutations_of_set R A  alternating_permutations_of_set (converse R) A"

definition alt_permutes ("_ alt'_permutes⇘_ _" [40,0,40] 41) where
  "f alt_permutes⇘RA  f permutes A  alternating_list R (map f (sorted_list_of_set_wrt R A))"

abbreviation rev_alt_permutes ("_ rev'_alt'_permutes⇘_ _" [40,0,40] 41) where
  "f rev_alt_permutes⇘RA  f alt_permutes⇘converse RA"

abbreviation alt_permutes_less ("_ alt'_permutes _" [40,40] 41) where
  "f alt_permutes A  f alt_permutes⇘{(x,y). x < y}A"

abbreviation rev_alt_permutes_less ("_ rev'_alt'_permutes _" [40,40] 41) where
  "f rev_alt_permutes A  f rev_alt_permutes⇘{(x,y). x < y}A"



lemma alternating_permutations_of_set_empty [simp]:
  "alternating_permutations_of_set R {} = {[]}"
  by (auto simp: alternating_permutations_of_set_def)

lemma alternating_permutations_of_set_singleton [simp]:
  "alternating_permutations_of_set R {x} = {[x]}"
  by (auto simp: alternating_permutations_of_set_def)

lemma bij_betw_alternating_permutations_of_set:
  assumes "monotone_on A (λx y. (x,y)  R) (λx y. (x,y)  R') f"
  assumes "strict_linear_order_on A R" "strict_linear_order_on (f ` A) R'" "B = f  ` A"
  shows   "bij_betw (map f) (alternating_permutations_of_set R A) (alternating_permutations_of_set R' B)"
proof -
  have "inj_on f A"
    by (rule monotone_on_imp_inj_on[OF assms(1)]) (use assms(2,3) in simp_all)
  have inj: "inj_on (map f) (alternating_permutations_of_set R A)"
    by (rule inj_on_mapI[OF inj_on_subset[OF inj_on f A]])
       (auto simp: alternating_permutations_of_set_def permutations_of_set_def)

  have "map f ` alternating_permutations_of_set R A = alternating_permutations_of_set R' (f ` A)"
    (is "_ ` ?lhs = ?rhs")
  proof safe
    fix xs assume "xs  ?lhs"
    thus "map f xs  ?rhs" using assms
      by (auto simp: alternating_permutations_of_set_def permutations_of_set_def distinct_map alternating_list_map
                     inj_on_subset[OF inj_on f A])
  next
    fix xs assume xs: "xs  ?rhs"
    hence set_xs: "set xs = f ` A"
      by (auto simp: alternating_permutations_of_set_def permutations_of_set_def)
    define ys where "ys = map (inv_into A f) xs"
    have mono: "monotone_on (f ` A) (λx y. (x,y)  R') (λx y. (x,y)  R) (inv_into A f)"
      by (intro monotone_on_inv_into) (use assms in simp_all)
    hence inj': "inj_on (inv_into A f) (f ` A)"
      by (rule monotone_on_imp_inj_on) (use assms inj_on f A in simp_all)
    have "ys  ?lhs" using xs mono inj_on f A inj' assms(2,3)
      by (auto simp: ys_def alternating_permutations_of_set_def permutations_of_set_def distinct_map
               intro!: inj_on_subset[OF inj_on f A] alternating_list_map)
    moreover have "map f ys = map (λx. x) xs"
      unfolding ys_def map_map o_def
      by (intro map_cong inv_into_f_f) (use inj_on f A set_xs in auto)
    ultimately show "xs  map f ` ?lhs"
      by auto
  qed
  with inj show ?thesis using B = f ` A
    unfolding bij_betw_def by blast
qed
    
lemma alternating_permutations_of_set_glue:
  assumes A: "finite A" 
  assumes X: "X  A" and x: "x  A - X" "y. y  A-{x}  (x,y)  R"
  assumes xs: "xs  alternating_permutations_of_set R X"
  assumes ys: "ys  alternating_permutations_of_set R (A - X - {x})"
  defines "R'  (if odd (card X) then R else R¯)"
  shows   "rev xs @ [x] @ ys  alternating_permutations_of_set R' A"
proof -
  have "set (xs @ ys)  A - {x}"
    using xs ys X x unfolding alternating_permutations_of_set_def permutations_of_set_def
    by auto
  hence *: "y  A - {x}" if "y  set (xs @ ys)" for y
    using that by blast
  have length_xs: "length xs = card X"
    using xs distinct_card[of xs]
    unfolding alternating_permutations_of_set_def permutations_of_set_def by simp

  have "xs = []  (hd xs, x)  R¯"
    using x(2)[OF *, of "hd xs"] by (cases "xs = []") auto
  moreover have "ys = []  (hd ys, x)  R¯"
    using x(2)[OF *, of "hd ys"] by (cases "ys = []") auto
  ultimately have "alternating_list R' (rev xs @ [x] @ ys)"
    using xs ys unfolding alternating_list_append_iff R'_def alternating_permutations_of_set_def
    by (simp add: length_xs alternating_list_rev last_rev)
  moreover have "rev xs @ [x] @ ys  permutations_of_set A"
    using xs ys X x unfolding alternating_permutations_of_set_def permutations_of_set_def
    by auto
  ultimately show ?thesis
    unfolding alternating_permutations_of_set_def by blast
qed

lemma alternating_permutations_of_set_split:
  assumes A: "finite A" 
  assumes z: "z  A"
  assumes zs: "zs  alternating_permutations_of_set R A"
  assumes k: "k < length zs" "zs ! k = z"
  defines "R'  (if odd k then R else converse R)"
  obtains xs ys where
    "zs = rev xs @ [z] @ ys" "alternating_list R' xs" "alternating_list R' ys" 
    "distinct xs" "distinct ys" "length xs = k"
proof -
  have "set zs = A" "distinct zs"
    using zs unfolding alternating_permutations_of_set_def permutations_of_set_def by blast+
  with z(1) have "z  set zs"
    by blast
  then obtain xs ys where zs_eq: "zs = xs @ z # ys"
    by (metis in_set_conv_decomp)

  have "zs ! length xs = z" "length xs < length zs"
    using k by (simp_all add: zs_eq)
  with distinct zs and k have k_eq: "k = length xs"
    using distinct_conv_nth by blast

  have "alternating_list R (xs @ z # ys)"
    using zs by (simp add: alternating_permutations_of_set_def zs_eq)
  hence "alternating_list R' (rev xs)" "alternating_list R' ys"
    by (auto simp: alternating_list_append_iff alternating_list_Cons_iff
                   Let_def k_eq R'_def alternating_list_rev)
  thus ?thesis
    using distinct zs k_eq by (intro that[of "rev xs" ys]) (simp_all add: zs_eq)
qed

lemma inj_on_glue_alternating_permutations_of_set:
  fixes A :: "'a set"
  assumes x: "x  A" "y. y  A - {x}  (x, y)  R"
  defines "P  (λX::'a set. alternating_permutations_of_set R X)"
  shows   "inj_on (λ(xs, ys). rev xs @ [x] @ ys) ((XPow (A-{x}). P X × P (A - X - {x})))"
proof (rule inj_onI, clarify, goal_cases)
  case (1 xs1 ys1 xs2 ys2)
  from 1 have "rev xs1 @ x # ys1 = rev xs2 @ x # ys2"
    by simp
  moreover have "x  set xs1" "x  set xs2" "x  set ys1" "x  set ys2" 
    using 1 unfolding P_def alternating_permutations_of_set_def permutations_of_set_def
    by auto
  ultimately show "xs1 = xs2  ys1 = ys2"
    by (subst (asm) append_Cons_eq_iff) auto
qed


subsection ‹Zigzag numbers›

text ‹
  The zigzag numbers $E_n$ count the number of alternating permutations on a linearly ordered set
  with $n$ elements. Note that varying conventions exist; e.g.\ these are also sometimes
  also called ``Euler numbers'' or ``Euler zigzag numbers''.~\oeiscite{A000111}

  In our formalisation, ``Euler numbers'' are something closely related but different,
  following the conventions of ProofWiki and Mathematica.

  It is easy to see that we can w.l.o.g.\ assume that the set in question is the integers
  from $1$ to $n$ and the order in question is the natural order $<$.
›
definition zigzag_number :: "nat  nat" where
  "zigzag_number n = card (alternating_permutations_of_set {(x,y). x < y} {1..n})"

lemma zigzag_number_0 [simp]: "zigzag_number 0 = 1"
  and zigzag_number_1 [simp]: "zigzag_number (Suc 0) = 1"
  by (simp_all add: zigzag_number_def)

lemma card_alternating_permutations_of_set:
  assumes "strict_linear_order_on A R" "finite A"
  shows   "card (alternating_permutations_of_set R A) = zigzag_number (card A)"
proof -
  obtain f :: "nat  'a" where f:
    "bij_betw f {1..card A} A" "monotone_on {1..card A} (<) (λx y. (x,y)  R) f"
    using strict_linear_orderE_bij_betw'[OF assms] .
  define P1 where "P1 = alternating_permutations_of_set {(x, y). x < y} {1..card A}"
  define P2 where "P2 = alternating_permutations_of_set R A"

  have "zigzag_number (card A) = card P1"
    by (simp add: zigzag_number_def P1_def)
  also have "bij_betw (map f) P1 P2"
    unfolding P1_def P2_def
  proof (rule bij_betw_alternating_permutations_of_set)
    show "strict_linear_order_on (f ` {1..card A}) R" and "A = f ` {1..card A}"
      using assms f(1) by (simp_all add: bij_betw_def)
  qed (use f(2) in auto)
  hence "card P1 = card P2"
    by (rule bij_betw_same_card)
  finally show ?thesis
    by (simp add: P2_def)
qed

text ‹
  The zigzag numbers satisfy the Catalan-like recurrence
  \[ 2 E_{n+1} = \sum_{k=0}^n {n \choose k} E_k E_{n-k}\ . \]

  The idea behind the proof is to look at a linearly ordered set $A$ of size $n+1$ (with $n > 0$)
  and its largest element $x$. We now do the following:

     Pick a number $0\leq k\leq n$.

     Pick a subset $X\subseteq A\setminus\{x\}$ of elements to occur to the left of $A$
      in our permutation. We have ${n\choose k}$ choices for this.

     Pick an alternating permutation xs› of $X$ and a reverse-alternating permutation of
      ys› of $A\setminus(X\cup\{x\})$. We have $E_k$ and $E_{n-k}$ choices for this, respectively.

     Return the permutation rev xs @ [x] @ ys›

  This process constructs exactly all alternating and reverse-alternating permutations on $A$.
  Moreover, the alternating and reverse-alternating permutations of $A$ are disjoint and have
  the same cardinality since $|A| \geq 2$.

  Thus if we sum the number of possibilities we counted above over all $k$,
  we obtain exactly $2 E_{n+1}$.
›
theorem zigzag_number_Suc:
  assumes "n > 0"
  shows   "2 * zigzag_number (Suc n) =
             (kn. (n choose k) * (zigzag_number k * zigzag_number (n - k)))"
proof -
  define P where "P = (λX::nat set. alternating_permutations_of_set {(x,y). x < y} X)"
  define P' where "P' = (λX::nat set. alternating_permutations_of_set {(x,y). x > y} X)"
  define glue :: "nat list × nat list  nat list" where "glue = (λ(xs, ys). rev xs @ [1] @ ys)"
  define A where "A = {1..n+1}"
  have [intro]: "finite (P X)" "finite (P' X)" for X
    unfolding P_def P'_def by auto
  let ?less = "{(x,y). x < (y::nat)}"
  let ?greater = "{(x,y). x > (y::nat)}"
  have [simp]: "converse ?less = ?greater" "converse ?greater = ?less"
    by (auto simp: converse_def)
  define R where "R = (λk. if odd (k::nat) then ?less else ?greater)"

  have disjoint: "P A  P' A = {}"
  proof -
    have False if "zs  P A" "zs  P' A" for zs
    proof -
      have zs: "set zs = A" "distinct zs" "alternating_list ?less zs" "alternating_list ?greater zs"
        using that
        unfolding P_def P'_def alternating_permutations_of_set_def permutations_of_set_def
        by simp_all
      have "length zs  2"
        using distinct_card[of zs] zs n > 0 by (simp add: A_def)
      then obtain x y zs' where zs_eq: "zs = x # y # zs'"
        by (auto simp: Suc_le_length_iff numeral_2_eq_2)
      show False
        using zs by (simp add: zs_eq)
    qed
    thus ?thesis
      by blast
  qed

  have "card (glue ` (XPow (A-{1}). P X × P (A - X - {1}))) = 
        card (XPow (A-{1}). P X × P (A - X - {1}))"
    unfolding glue_def P_def
    by (rule card_image, rule inj_on_glue_alternating_permutations_of_set)
       (auto simp: A_def)

  also have "glue ` (XPow (A-{1}). P X × P (A - X - {1})) = P A  P' A"
  proof (rule antisym)
    have "glue (xs, ys)  P A  P' A" 
      if X: "X  Pow (A - {1})" and xs: "xs  P X" and ys: "ys  P (A - X - {1})" for X xs ys
    proof -
      have "rev xs @ [1] @ ys  alternating_permutations_of_set
              (if odd (card X) then ?less else ?less¯) A"
        by (rule alternating_permutations_of_set_glue[of A X 1 ?less xs ys])
           (use X xs ys in auto simp: A_def P_def)
      hence "glue (xs, ys)  (if odd (card X) then P A else P' A)"
        by (auto simp: glue_def P_def P'_def)
      also have "  P A  P' A"
        by auto
      finally show "glue (xs, ys)  P A  P' A" .
    qed
    thus "glue ` (XPow (A-{1}). P X × P (A - X - {1}))  P A  P' A"
      by blast
  next
    have "zs  glue ` (XPow (A-{1}). P X × P (A - X - {1}))" if zs: "zs  P A  P' A" for zs
    proof -
      from zs have set_zs: "set zs = A" and "distinct zs"
        by (auto simp: P_def P'_def alternating_permutations_of_set_def permutations_of_set_def)
      have "length zs = Suc n"
        using set_zs distinct zs distinct_card[of zs] by (simp add: A_def)
      from set_zs have "1  set zs"
        by (auto simp: A_def)
      then obtain k where k: "k < length zs" "zs ! k = 1"
        by (meson in_set_conv_nth)
      define R' where "R' = (if zs  P A then ?less else ?greater)"
      obtain xs ys where xs_ys:
        "zs = rev xs @ [1] @ ys" "alternating_list (if odd k then R' else R'¯) xs"
        "alternating_list (if odd k then R' else R'¯) ys" "distinct xs" "distinct ys" "length xs = k"
        by (rule alternating_permutations_of_set_split[of A 1 zs R' k])
           (use k zs in auto simp: A_def R'_def P_def P'_def)
      have set_xs: "set xs  A - {1}"
        using distinct zs unfolding set_zs [symmetric] xs_ys(1) by (auto simp: xs_ys(1))
      have set_ys: "set ys = A - set xs - {1}"
        using distinct zs unfolding set_zs [symmetric] xs_ys(1) by (auto simp: xs_ys(1))
      have "odd k  zs  P A"
      proof -
        have 1: "xs  []  ys  []"
          using xs_ys(1) n > 0 length zs = Suc n by (auto simp: A_def)
        have 2: "x  A - {1}" if "x  set (xs @ ys)" for x
        proof -
          have "x  set (xs @ ys)"
            using that by simp
          also have "  set zs - {1}"
            using distinct zs by (auto simp add: xs_ys(1))
          finally show ?thesis
            by (simp add: set_zs)
        qed
        have 3: "xs = []  1 < hd xs" 
          using 2[of "hd xs"] by (cases "xs = []") (auto simp: hd_in_set A_def)
        have 4: "ys = []  1 < hd ys" 
          using 2[of "hd ys"] by (cases "ys = []") (auto simp: hd_in_set A_def)
        have "alternating_list R' zs"
          using zs by (auto simp: R'_def P_def P'_def alternating_permutations_of_set_def)
        thus ?thesis
          using 1 3 4 xs_ys(2,3) length xs = k zs
          by (auto simp: xs_ys(1) alternating_list_append_iff alternating_list_Cons_iff
                         alternating_list_rev Let_def R'_def last_rev split: if_splits)
      qed
      hence "(if odd k then R' else R'¯) = ?less"
        by (auto simp: R'_def)
      with xs_ys and set_ys have "zs = glue (xs, ys)" "xs  P (set xs)" "ys  P (A - set xs - {1})"
        by (simp_all add: glue_def P_def alternating_permutations_of_set_def permutations_of_set_def)
      thus "zs  glue ` (XPow (A-{1}). P X × P (A - X - {1}))"
        using set_xs by blast
    qed
    thus "P A  P' A  glue ` (XPow (A-{1}). P X × P (A - X - {1}))"
      by blast
  qed

  also have "card (P A  P' A) = card (P A) + card (P' A)"
    by (subst card_Un_disjoint) (use disjoint in auto)
  also have "card (P A) = zigzag_number (Suc n)"
    unfolding P_def by (subst card_alternating_permutations_of_set) (auto simp: A_def)
  also have "card (P' A) = zigzag_number (Suc n)"
    unfolding P'_def by (subst card_alternating_permutations_of_set) (auto simp: A_def)

  also have "card (XPow (A-{1}). P X × P (A - X - {1})) = 
               (XPow (A - {1}). card (P X × P (A - X - {1})))"
  proof (intro card_UN_disjoint ballI impI)
    fix X Y assume "X  Pow (A - {1})" "Y  Pow (A - {1})" "X  Y"
    show "P X × P (A - X - {1})  P Y × P (A - Y - {1}) = {}"
      using X  Y unfolding P_def alternating_permutations_of_set_def permutations_of_set_def
      by blast
  qed (auto simp: A_def)
  also have " = (XPow (A - {1}). zigzag_number (card X) * zigzag_number (n - card X))"
  proof (rule sum.cong)
    fix X assume X: "X  Pow (A - {1})"
    have [simp]: "finite X"
      by (rule finite_subset[of _ A]) (use X in auto simp: A_def)
    have "card (P X × P (A - X - {1})) = card (P X) * card (P (A - X - {1}))"
      by (rule card_cartesian_product)
    also have "card (P X) = zigzag_number (card X)"
      unfolding P_def by (rule card_alternating_permutations_of_set) (use X in auto)
    also have "card (P (A - X - {1})) = zigzag_number (card (A - X - {1}))"
      unfolding P_def by (rule card_alternating_permutations_of_set) (use X in auto simp: A_def)
    also have "card (A - X - {1}) = card (A - X) - 1"
      using X by (subst card_Diff_subset) (auto simp: A_def)
    also have "card (A - X) = card A - card X"
      using X finite_subset[of X A] by (subst card_Diff_subset) (auto simp: A_def)
    also have "card A = n + 1"
      by (simp add: A_def)
    finally show "card (P X × P (A - X - {1})) = 
                    zigzag_number (card X) * zigzag_number (n - card X)"
      by simp
  qed auto

  also have "Pow (A - {1}) = (kn. {XPow (A-{1}). card X = k})"
    by (subst Pow_conv_subsets_of_size) (simp_all add: A_def)
  also have "(X. zigzag_number (card X) * zigzag_number (n - card X)) =
               (kn. card {X. X  A-{1}  card X = k} * (zigzag_number k * zigzag_number (n - k)))"
    by (subst sum.UNION_disjoint) (auto simp: A_def)
  also have " = (kn. (n choose k) * (zigzag_number k * zigzag_number (n - k)))"
    using n_subsets[of "A - {1}"] by (simp add: A_def)
  finally show ?thesis
    by simp
qed

text ‹
  The exponential generating function of the zigzag numbers is:
    \[f(x) = \sum_{n\geq 0} \frac{E_n}{n!} x^n = \sec x + \tan x\]
  This follows from the fact that by the above recurrence for $E_n$, both $f$ and $\sin + \tan$
  satisfy the ordinary differential equation $2 f'(x) = 1 + f(x)^2$
›
corollary exponential_generating_function_zigzag_number:
  "Abs_fps (λn. of_nat (zigzag_number n) / fact n :: 'a :: field_char_0) = fps_sec 1 + fps_tan 1"
proof -
  define F where "F  Abs_fps (λn. of_nat (zigzag_number n) / fact n :: 'a)"
  define G where "G  (fps_sec 1 + fps_tan 1 :: 'a fps)"
  have [simp]: "fps_nth F 0 = 1" "fps_nth F (Suc 0) = 1"
    by (simp_all add: F_def)
  have F_Suc: "fps_nth F (Suc n) = (kn. fps_nth F k * fps_nth F (n - k)) / (2 * of_nat (n + 1))"
    if "n > 0" for n
  proof -
    have "2 * fps_nth F (Suc n) = of_nat (2 * zigzag_number (Suc n)) / fact (Suc n)"
      by (simp add: F_def)
    also have " = (kn. fps_nth F k * fps_nth F (n - k)) / of_nat (n + 1)"
      by (subst zigzag_number_Suc) (use that in auto simp: F_def mult_ac binomial_fact sum_divide_distrib)
    finally show ?thesis
      unfolding of_nat_mult by (simp add: divide_simps mult_ac del: of_nat_Suc)
  qed
  have "2 * fps_deriv F = 1 + F ^ 2"
    by (rule fps_ext) (auto simp: fps_nth_power_0 F_Suc fps_square_nth divide_simps simp del: of_nat_Suc)
  have "2 * fps_deriv G = 1 + G ^ 2" 
    using fps_sec_square_conv_fps_tan_square[where ?'a = 'a]
    by (simp add: G_def fps_sec_deriv fps_tan_deriv' power2_eq_square algebra_simps)

  have "fps_nth F n = fps_nth G n" for n
  proof (induction rule: less_induct)
    case (less n)
    show ?case
    proof (cases "n = 0")
      case True
      thus ?thesis
        by (auto simp: F_def G_def)
    next
      case n: False
      have "2 * of_nat n * fps_nth F n = fps_nth (2 * fps_deriv F) (n - 1)"
        using n by simp
      also have "2 * fps_deriv F = 1 + F ^ 2"
        by fact
      also have "fps_nth (1 + F ^ 2) (n - 1) = fps_nth 1 (n - 1) + (kn-1. F $ k * F $ (n - Suc k))"
        using n by (simp add: fps_square_nth)
      also have "(kn-1. F $ k * F $ (n - Suc k)) = (kn-1. G $ k * G $ (n - Suc k))"
        by (intro sum.cong arg_cong2[of _ _ _ _ "(*)"] less.IH) (use n in auto)
      also have "fps_nth 1 (n - 1) +  = fps_nth (1 + G ^ 2) (n - 1)"
        using n by (simp add: fps_square_nth)
      also have "(1 + G ^ 2) = 2 * fps_deriv G"
        using 2 * fps_deriv G = 1 + G ^ 2 ..
      also have "fps_nth  (n - 1) = 2 * of_nat n * fps_nth G n"
        using n by simp
      finally show ?thesis
        using n by simp
    qed
  qed
  thus "F = G"
    by (rule fps_ext)
qed

text ‹
  Lastly, we get the following explicit relationships between the zigzag numbers
  and the coefficients appearing in the Maclaurin series of $\sec$ and $\tan$.
›
corollary zigzag_number_conv_fps_sec:
  assumes "even n"
  shows   "real (zigzag_number n) = fps_nth (fps_sec 1) n * fact n"
proof -
  have "real (zigzag_number n) / fact n = 
          fps_nth (Abs_fps (λn. real (zigzag_number n) / fact n)) n"
    by simp
  also have "Abs_fps (λn. real (zigzag_number n) / fact n) = fps_sec 1 + fps_tan 1"
    by (rule exponential_generating_function_zigzag_number)
  also have "fps_nth  n = fps_nth (fps_sec 1) n"
    using assms by (simp add: fps_nth_tan_even)
  finally show ?thesis
    by (simp add: field_simps)
qed

corollary zigzag_number_conv_fps_tan:
  assumes "odd n"
  shows   "real (zigzag_number n) = fps_nth (fps_tan 1) n * fact n"
proof -
  have "real (zigzag_number n) / fact n = 
          fps_nth (Abs_fps (λn. real (zigzag_number n) / fact n)) n"
    by simp
  also have "Abs_fps (λn. real (zigzag_number n) / fact n) = fps_sec 1 + fps_tan 1"
    by (rule exponential_generating_function_zigzag_number)
  also have "fps_nth  n = fps_nth (fps_tan 1) n"
    using assms by (simp add: fps_nth_sec_odd)
  finally show ?thesis
    by (simp add: field_simps)
qed



subsection ‹Alternating permutations with a fixed first element›

text ‹
  In order to study the ‹Entringer numbers›, a generalisation of the zigzag numbers,
  we introduce the set of alternating permutations on a set that start with some fixed
  element x›.
›
definition alternating_permutations_of_set_with_hd :: 
  "('a × 'a) set  'a set  'a  'a list set" where
  "alternating_permutations_of_set_with_hd R A x =
     {xsalternating_permutations_of_set R A. xs  []  hd xs = x}"

lemma alternating_permutations_of_set_with_hd_singleton:
  "alternating_permutations_of_set_with_hd R {y} x = (if x = y then {[x]} else {})"
  by (auto simp: alternating_permutations_of_set_with_hd_def alternating_permutations_of_set_def)

lemma alternating_permutations_of_set_with_hd_outside:
  assumes "x  A"
  shows   "alternating_permutations_of_set_with_hd R A x = {}"
proof -
  {
    fix xs assume "xs  alternating_permutations_of_set_with_hd R A x"
    hence "set xs = A" "xs  []" "hd xs = x"
      by (auto simp: alternating_permutations_of_set_with_hd_def 
                     alternating_permutations_of_set_def permutations_of_set_def)
    moreover from this have "hd xs  set xs"
      by (intro hd_in_set)
    ultimately have "x  A"
      by auto
    hence False
      using assms by simp
  }
  thus ?thesis
    by blast
qed

lemma alternating_permutations_of_set_with_hd_least:
  assumes "strict_linear_order_on A R"
  assumes "y. y  A - {x}  (x, y)  R" "x  A" "A  {x}" "finite A"
  shows   "alternating_permutations_of_set_with_hd R A x = {}"
proof -
  from assms have "A - {x}  {}"
    by auto
  hence "card (A - {x}) > 0"
    using finite A card_gt_0_iff by blast
  hence "card A  2"
    by (subst (asm) card_Diff_subset) (use assms in auto)

  {
    fix xs assume "xs  alternating_permutations_of_set_with_hd R A x"
    hence xs: "set xs = A" "xs  []" "hd xs = x" "alternating_list R xs" "distinct xs"
      by (auto simp: alternating_permutations_of_set_with_hd_def 
                     alternating_permutations_of_set_def permutations_of_set_def)
    have "length xs  2"
      using distinct_card[of xs] xs card A  2 by simp
    then obtain x' y xs' where xs_eq: "xs = x' # y # xs'"
      by (auto simp: Suc_le_length_iff numeral_2_eq_2)
    have [simp]: "x' = x"
      using hd xs = x by (simp add: xs_eq)
    from xs(4) have "(y, x)  R"
      by (simp add: xs_eq)
    moreover from this and assms(1) have "y  A - {x}"
      using set xs = A  by (auto simp: strict_linear_order_on_def irrefl_def xs_eq)
    with assms(2)[of y] and set xs = A have "(x, y)  R"
      by (auto simp: xs_eq)
    ultimately have False
      using strict_linear_order_on_asym_on[OF assms(1)] x  A y  A - {x} 
      by (auto simp: asym_on_def)
  }
  thus ?thesis
    by blast
qed

lemma alternating_permutations_of_set_with_hd_greatest:
  assumes "strict_linear_order_on A R"
  assumes "y. y  A - {x}  (y, x)  R" "x  A"
  shows   "bij_betw (λxs. x # xs)
             (rev_alternating_permutations_of_set R (A - {x}))
             (alternating_permutations_of_set_with_hd R A x)"
proof -
  have [simp]: "A  {}"
    using x  A by auto
  show ?thesis
  proof (rule bij_betwI)
    show "(#) x  rev_alternating_permutations_of_set R (A - {x}) 
                    alternating_permutations_of_set_with_hd R A x"
    proof (safe, goal_cases)
      case (1 xs)
      hence "set xs  A - {x}"
        by (auto simp: alternating_permutations_of_set_def permutations_of_set_def)
      moreover have "hd xs  set xs  xs = []"
        using hd_in_set by blast
      ultimately have "hd xs  A - {x}  xs = []"
        by blast
      hence "(hd xs, x)  R  xs = []"
        using assms(2) by blast
      thus ?case
        using x  A assms(2) 1
        by (auto simp: alternating_permutations_of_set_with_hd_def alternating_permutations_of_set_def
                       permutations_of_set_nonempty alternating_list_Cons_iff)
    qed
  next
    show "tl  alternating_permutations_of_set_with_hd R A x 
                 rev_alternating_permutations_of_set R (A - {x})"
      by (auto simp: alternating_permutations_of_set_with_hd_def
                     alternating_permutations_of_set_def permutations_of_set_nonempty
                     alternating_list_Cons_iff)
  qed (auto simp: alternating_permutations_of_set_with_hd_def)  
qed

lemma UN_alternating_permutations_of_set_with_hd:
  assumes "A  {}"
  shows   "(xA. alternating_permutations_of_set_with_hd R A x) =
             alternating_permutations_of_set R A"
  using assms
  by (force simp: alternating_permutations_of_set_with_hd_def
                  alternating_permutations_of_set_def permutations_of_set_def intro!: hd_in_set)

lemma alternating_permutations_of_set_with_hd_split_first:
  assumes "strict_linear_order_on A R" "x  A" "A  {x}"
  shows   "bij_betw ((#) x)
            (y{yA-{x}. (y,x)R}. alternating_permutations_of_set_with_hd (converse R) (A - {x}) y)
            (alternating_permutations_of_set_with_hd R A x)"
proof -
  have [simp]: "A  {}"
    using assms by auto
  have "A - {x}  {}"
    using assms by blast
 
  show ?thesis
  proof (rule bij_betwI)
    show "(#) x   (alternating_permutations_of_set_with_hd (R¯) (A - {x}) ` {y  A - {x}. (y, x)  R}) 
                   alternating_permutations_of_set_with_hd R A x"
    proof (intro Pi_I; elim UN_E, goal_cases)
      case (1 xs y)
      have xs: "xs  permutations_of_set (A - {x})" "alternating_list (converse R) xs" "hd xs = y"
        using 1 by (auto simp: alternating_permutations_of_set_with_hd_def 
                               alternating_permutations_of_set_def)
      have "x # xs  permutations_of_set A"
        using xs x  A by (auto simp: permutations_of_set_nonempty)
      moreover have "alternating_list R (x # xs)"
        using xs 1 by (auto simp: alternating_list_Cons_iff)
      ultimately show "x # xs  alternating_permutations_of_set_with_hd R A x"
        unfolding alternating_permutations_of_set_with_hd_def
        by (auto simp: alternating_permutations_of_set_def)
    qed
  next
    show "tl  alternating_permutations_of_set_with_hd R A x 
                   (alternating_permutations_of_set_with_hd (R¯) (A - {x}) ` {y  A - {x}. (y, x)  R})"
    proof (safe, goal_cases)
      case (1 xs)
      have xs: "xs  permutations_of_set A" "alternating_list R xs" "hd xs = x"
        using 1 by (auto simp: alternating_permutations_of_set_with_hd_def 
                               alternating_permutations_of_set_def)
      have "xs  []"
        using xs assms by (auto simp: permutations_of_set_def)
      then obtain ys where xs_eq: "xs = x # ys"
        using xs(3) by (cases xs) auto

      have ys: "ys  permutations_of_set (A - {x})"
        using xs by (auto simp: permutations_of_set_nonempty xs_eq)
      hence "set ys = A - {x}"
        by (auto simp: permutations_of_set_def)
      hence "ys  []"
        using A - {x}  {} by (intro notI) auto

      have "hd ys  A"
        using hd_in_set[of ys] set ys = A - {x} ys  [] by auto
      moreover have "rev_alternating_list R ys" "(hd ys, x)  R"
        using xs ys  [] by (auto simp: xs_eq alternating_list_Cons_iff)
      moreover have "(hd ys, hd ys)  R"
        using assms(1) by (auto simp: strict_linear_order_on_def irrefl_def)
      ultimately show ?case
        using ys  [] ys
        by (auto simp: xs_eq alternating_permutations_of_set_with_hd_def
                       alternating_permutations_of_set_def)
    qed
  qed (auto simp: alternating_permutations_of_set_with_hd_def)
qed

lemma bij_betw_alternating_permutations_of_set_with_hd_flip:
  assumes "x  n"
  shows   "bij_betw (map (λk. n - k))
             (alternating_permutations_of_set_with_hd {(x::nat,y). x < y} {0..n} x)
             (alternating_permutations_of_set_with_hd {(x::nat,y). x > y} {0..n} (n - x))"
proof -
  have *: "bij_betw (λk. n - k) {0..n} {0..n}"
    by (rule bij_betwI[of _ _ _ "λk. n - k"]) auto
  have "bij_betw (map ((-) n))
          (alternating_permutations_of_set {(x, y). x < y} {0..n})
          (alternating_permutations_of_set {(x, y). y < x} {0..n})"
    by (rule bij_betw_alternating_permutations_of_set)
       (use * in auto simp: monotone_on_def image_def bij_betw_def)

  thus ?thesis
    unfolding alternating_permutations_of_set_with_hd_def
  proof (rule bij_betw_Collect, goal_cases)
    case (1 xs)
    hence "xs  []" "set xs = {0..n}"
      by (auto simp: alternating_permutations_of_set_def permutations_of_set_def)
    with hd_in_set[of xs] have "hd xs  n"
      by auto
    thus ?case using xs  [] assms
      by (auto simp: hd_map)
  qed
qed



subsection ‹Entringer numbers›

text ‹
  The Entringer number $E_{n,k}$ now counts the number of alternating permutations
  on a set with $n+1$ elements that start with the (unique) element of rank $k$,
  i.e.\ the $k$-th largest element of the set.~\oeiscite{A008282}

  As we will see, it suffices to w.l.o.g.\ only consider sets of integers of the form
  $\{0, \ldots, n\}$.
›

definition entringer_number :: "nat  nat  nat" where
  "entringer_number n k = 
     card (alternating_permutations_of_set_with_hd {(x,y). x < y} {0..n} k)"

lemma entringer_number_0_0 [simp]: "entringer_number 0 0 = 1"
  and entring_number_0_left [simp]: "k  0  entringer_number 0 k = 0"
  by (simp_all add: entringer_number_def alternating_permutations_of_set_with_hd_singleton)

lemma entringer_number_0_right [simp]:
  assumes "n > 0"
  shows   "entringer_number n 0 = 0"
proof -
  have "alternating_permutations_of_set_with_hd {(x,y). x < y} {0..n} 0 = {}"
    by (rule alternating_permutations_of_set_with_hd_least) (use assms in auto)
  thus ?thesis
    using assms by (simp add: entringer_number_def)
qed

lemma entringer_number_greater_eq_0 [simp]:
  assumes "k > n"
  shows   "entringer_number n k = 0"
proof -
  have "alternating_permutations_of_set_with_hd {(x,y). x < y} {0..n} k = {}"
    by (rule alternating_permutations_of_set_with_hd_outside) (use assms in auto)
  thus ?thesis
    using assms by (simp add: entringer_number_def)
qed

theorem card_alternating_permutations_of_set_with_hd:
  assumes "strict_linear_order_on A R" "finite A" "x  A"
  shows   "card (alternating_permutations_of_set_with_hd R A x) =
             entringer_number (card A - 1) (card {yA-{x}. (y,x)  R})"
proof -
  define n where "n = card A - 1"
  have "A  {}"
    using x  A by auto
  with finite A have "card A > 0"
    using card_gt_0_iff by blast
  hence "card A = Suc n"
    by (auto simp: n_def)
  hence *: "{0..n} = {0..<card A}"
    by auto

  obtain f :: "nat  'a" where f:
    "bij_betw f {0..n} A" "monotone_on {0..n} (<) (λx y. (x,y)  R) f"
    using strict_linear_orderE_bij_betw[OF assms(1,2)] unfolding * .
  obtain k where k: "k  n" "f k = x"
    using f(1) x  A by (auto simp: bij_betw_def)
  have R_f_iff: "(f x, f y)  R  x < y" if "x  n" "y  n" for x y
    by (rule monotone_on_strict_linear_orderD[OF f(2)])
       (use assms that f(1) in auto simp: bij_betw_def)
  have f_eq_iff: "f x = f y  x = y" if "x  n" "y  n" for x y
    using f(1) that by (auto simp: bij_betw_def inj_on_def)

  have "bij_betw f {i{0..n}. i < k} {yA. (y, x)  R}"
    using f(1) by (rule bij_betw_Collect) (use f(2) k in auto simp: monotone_on_def R_f_iff)
  hence "card {i{0..n}. i < k} = card {yA. (y, x)  R}"
    by (rule bij_betw_same_card)
  also have "{i{0..n}. i < k} = {..<k}"
    using k by auto
  also have "{yA. (y, x)  R} = {yA-{x}. (y, x)  R}"
    using x  A assms by (auto simp: strict_linear_order_on_def irrefl_def)
  finally have k_eq: "k = card {yA-{x}. (y, x)  R}"
    by simp

  have "bij_betw (map f)
          (alternating_permutations_of_set_with_hd {(x,y). x < y} {0..n} k)
          (alternating_permutations_of_set_with_hd R A x)"
    unfolding alternating_permutations_of_set_with_hd_def
    using bij_betw_alternating_permutations_of_set
  proof (rule bij_betw_Collect)
    show "A = f ` {0..n}" "strict_linear_order_on (f ` {0..n}) R"
      using f(1) assms by (simp_all add: bij_betw_def)
  next
    fix xs assume "xs  alternating_permutations_of_set {(x, y). x < y} {0..n}"
    hence xs: "set xs = {0..n}" "xs  []"
      by (auto simp: alternating_permutations_of_set_def permutations_of_set_def)
    show "(map f xs  []  hd (map f xs) = x)  (xs  []  hd xs = k)"
      using k hd_in_set[of xs] xs by (auto simp: hd_map f_eq_iff)
  qed (use f assms in auto simp: hd_map)
  hence "card (alternating_permutations_of_set_with_hd {(x,y). x < y} {0..n} k) =
         card (alternating_permutations_of_set_with_hd R A x)"
    by (rule bij_betw_same_card)
  also have "card (alternating_permutations_of_set_with_hd {(x,y). x < y} {0..n} k) =
             entringer_number n k"
    unfolding entringer_number_def by simp
  finally show ?thesis
    by (simp add: n_def k_eq)
qed

text ‹
  It is not difficult to show that $E_{n,n} = E_n$, i.e.\ the Entringer numbers really
  are a generalisation of the Euler numbers. The idea is that if we have an alternating
  permutation of $n$ elements $0, 1, \ldots, n$ that starts with largest one (i.e.\ $n$)
  then the list we obtain after dropping the initial element is a reverse-alternating
  permutation of $0, 1, \ldots, n-1$ with no further restrictions, and this map is one-to-one.
›
lemma entringer_number_same [simp]:
  "entringer_number n n = zigzag_number n"
proof (cases "n = 0")
  case False
  have "bij_betw (λxs. n # xs)
               (rev_alternating_permutations_of_set {(x, y). x < y} ({0..n}-{n}))
               (alternating_permutations_of_set_with_hd {(x, y). x < y} {0..n} n)"
    by (rule alternating_permutations_of_set_with_hd_greatest) auto
  hence "card (rev_alternating_permutations_of_set {(x, y). x < y} ({0..n}-{n})) =
         card (alternating_permutations_of_set_with_hd {(x, y). x < y} {0..n} n)"
    by (rule bij_betw_same_card)
  also have " = entringer_number n n"
    using False by (simp add: entringer_number_def)
  also have "converse {(x, y). x < y} = {(x::nat, y). x > y}"
    by auto
  also have "card (alternating_permutations_of_set {(x, y). x > y} ({0..n}-{n})) = zigzag_number n"
    by (subst card_alternating_permutations_of_set) auto
  finally show ?thesis ..
qed auto

lemma card_rev_alternating_permutations_of_set_with_hd:
  assumes x: "x  n"
  shows "card (alternating_permutations_of_set_with_hd {(x::nat,y). x > y} {0..n} x) =
           entringer_number n (n - x)"
proof -
  have "card (alternating_permutations_of_set_with_hd {(x::nat,y). x > y} {0..n} x) =
        entringer_number n (card {y  {0..n} - {x}. x < y})"
    by (subst card_alternating_permutations_of_set_with_hd) (use assms in auto)
  also have "{y  {0..n} - {x}. x < y} = {x<..n}"
    using x by auto
  finally show ?thesis
    by simp
qed

text ‹
  The following summation identity can be visualised as follows: if we have an alternating
  permutation of the elements $0, \ldots, n$ that starts with $k$ then the next element
  after $k$ must be a reverse-alternating permutation starting with one of the elements
  $0, \ldots, k-1$, and this is again a bijection.
›
theorem sum_entringer_numbers:
  assumes k: "k  Suc n"
  shows   "(i<k. entringer_number n (n - i)) = entringer_number (Suc n) k"
proof -
  define A where "A = (λX x. alternating_permutations_of_set_with_hd {(x::nat,y). x < y} X x)"
  define A' where "A' = (λX x. alternating_permutations_of_set_with_hd {(x::nat,y). x > y} X x)"
  have converses: "converse {(x::nat,y). x < y} = {(x::nat,y). x > y}" 
                  "converse {(x::nat,y). x > y} = {(x::nat,y). x < y}"
    by auto

  have "bij_betw ((#) k)
         ( (alternating_permutations_of_set_with_hd ({(x, y). x < y}¯) ({0..Suc n} - {k}) ` {y  {0..Suc n} - {k}. (y, k)  {(x, y). x < y}}))
         (alternating_permutations_of_set_with_hd {(x, y). x < y} {0..Suc n} k)"
    by (intro alternating_permutations_of_set_with_hd_split_first) (use k in auto)
  also have "{y  {0..Suc n} - {k}. (y, k)  {(x, y). x < y}} = {0..<k}"
    using k by auto
  finally have "bij_betw ((#) k) (i<k. A' ({0..Suc n} - {k}) i) (A {0..Suc n} k)"
    using converses by (simp add: A_def A'_def case_prod_unfold atLeast0LessThan)
  hence "card (i<k. A' ({0..Suc n} - {k}) i) = card (A {0..Suc n} k)"
    by (rule bij_betw_same_card)
  also have "card (A {0..Suc n} k) = entringer_number (Suc n) k"
    by (simp add: entringer_number_def A_def)
  also have "card (i<k. A' ({0..Suc n} - {k}) i) = (i<k. card (A' ({0..Suc n} - {k}) i))"
    by (subst card_UN_disjoint)
       (auto simp: A'_def alternating_permutations_of_set_with_hd_def alternating_permutations_of_set_def)
  also have " = (i<k. entringer_number n (n - i))"
  proof (intro sum.cong)
    fix i assume i: "i  {..<k}"
    have "card (A' ({0..Suc n} - {k}) i) =
          entringer_number n (card {j  {0..Suc n} - {k} - {i}. i < j})"
      unfolding A'_def using i k
      by (subst card_alternating_permutations_of_set_with_hd) auto
    also have "{j  {0..Suc n} - {k} - {i}. i < j} = {i<..Suc n} - {k}"
      using i k by auto
    also have "card  = n - i"
      using i k by (subst card_Diff_subset) auto
    finally show "card (A' ({0..Suc n} - {k}) i) = entringer_number n (n - i)" .
  qed auto
  finally show ?thesis .
qed

lemma sum_entringer_numbers':
  assumes k: "k  n"
  shows   "(ik. entringer_number n (n - i)) = entringer_number (Suc n) (Suc k)"
proof -
  have "(i<Suc k. entringer_number n (n - i)) = entringer_number (Suc n) (Suc k)"
    by (rule sum_entringer_numbers) (use k in auto)
  also have "{..<Suc k} = {..k}"
    by auto
  finally show ?thesis .
qed

text ‹
  A consequence of this summation identity is that the sum of all the values in the $n$-th row
  of the Entringer triangle is exactly the $n$-th zigzag number.
›
corollary sum_entringer_numbers_row: "(kn. entringer_number n k) = zigzag_number (Suc n)"
proof -
  have "(kn. entringer_number n (n - k)) = zigzag_number (Suc n)"
    using sum_entringer_numbers'[OF order.refl, of n] by simp
  also have "(kn. entringer_number n (n - k)) = (kn. entringer_number n k)"
    by (rule sum.reindex_bij_witness[of _ "λk. n - k" "λk. n - k"]) auto
  finally show ?thesis
    by simp
qed

text ‹
  By telescoping the summation identity, we also obtain the following simple recurrence
  for the Entringer numbers:
›
corollary entringer_number_rec:
  assumes "k  n"
  shows   "entringer_number (Suc n) (Suc k) =
             entringer_number (Suc n) k + entringer_number n (n - k)"
proof -
  have "entringer_number (Suc n) (Suc k) = (ik. entringer_number n (n - i))"
    by (rule sum_entringer_numbers' [symmetric]) (use assms in auto)
  also have "{..k} = insert k {..<k}"
    by auto
  also have "(i. entringer_number n (n - i)) =
             (i<k. entringer_number n (n - i)) + entringer_number n (n - k)"
    by (subst sum.insert) auto
  also have "(i<k. entringer_number n (n - i)) = entringer_number (Suc n) k"
    by (rule sum_entringer_numbers) (use assms in auto)
  finally show ?thesis .
qed

text ‹
  This recurrence can be used to compute the Entringer numbers (although if one wants
  this to be efficient one has to be a bit smarter about avoiding double computations;
  either by memoisation or by finding a smarter way to traverse the triangle).
›
lemma entringer_number_code [code]:
  "entringer_number n k =
     (if n = 0 then if k = 0 then 1 else 0
      else if k = 0  k > n then 0
      else entringer_number n (k - 1) + entringer_number (n - 1) (n - k))"
  using entringer_number_rec[of "k - 1" "n - 1"] by (cases n; cases k) auto

end