1-- Permutations of lists: equality of all counts, its equivalence laws,2-- splitting a list at an occurrence, and invariance of length, support,3-- and filtering4-- Part of Cumulus, the Grace17 mathematics library.5-- Copyright (c) 2026 Grant Passmore.6-- Released under the Apache License 2.0. See LICENSE in the repository root.7-- Authors: Grant Passmore89module Grace.Cumulus.List.Permutation10import Count1112-- Two lists are permutations of each other when every element occurs13-- equally often in both.14predicate ListPermutation (A xs ys : Set) :=15∀ x, listCount{A}(x, xs) = listCount{A}(x, ys)1617theorem ListPermutation.count (A x : Set) (xs ys : List(A))18requires permutation : ListPermutation(A, xs, ys)19shows listCount{A}(x, xs) = listCount{A}(x, ys)20proof21exact permutation with x2223theorem ListPermutation.of_eq (A : Set) (xs ys : List(A))24requires equal : xs = ys25shows ListPermutation(A, xs, ys)26proof27unfold ListPermutation28intro x29rw equal30rfl3132theorem ListPermutation.reflexive (A : Set) (xs : List(A))33shows ListPermutation(A, xs, xs)34proof35unfold ListPermutation36intro x37rfl3839theorem ListPermutation.symmetric (A : Set) (xs ys : List(A))40requires permutation : ListPermutation(A, xs, ys)41shows ListPermutation(A, ys, xs)42proof43unfold ListPermutation44intro x45have count := permutation with x46 symm at count47exact count4849theorem ListPermutation.transitive (A : Set) (xs ys zs : List(A))50requires first : ListPermutation(A, xs, ys)51requires second : ListPermutation(A, ys, zs)52shows ListPermutation(A, xs, zs)53proof54unfold ListPermutation55intro x56haveleft:= first with x57haveright:= second with x58rwleft59exactright6061theorem ListPermutation.cons (A a : Set) (xs ys : List(A))62requires element : a ∈ A63requires permutation : ListPermutation(A, xs, ys)64shows ListPermutation(A, a :: xs, a :: ys)65proof66unfold ListPermutation67intro x68have count := permutation with x69rw [listCount.cons, listCount.cons, count]70rfl7172theorem ListPermutation.cons_cancel (A a : Set) (xs ys : List(A))73requires element : a ∈ A74requires permutation : ListPermutation(A, a :: xs, a :: ys)75shows ListPermutation(A, xs, ys)76proof77unfold ListPermutation78intro x79have count := permutation with x80have mark := indicator.natural at {x}, a81rw [listCount.cons, listCount.cons] at count82exact add.left_cancel with count8384theorem ListPermutation.append_right (A : Set) (xs ys zs : List(A))85requires permutation : ListPermutation(A, xs, ys)86shows ListPermutation(A, append(zs, xs), append(zs, ys))87proof88unfold ListPermutation89intro x90have count := permutation with x91rw [listCount.append, listCount.append, count]92rfl9394theorem ListPermutation.append_left (A : Set) (xs ys zs : List(A))95requires permutation : ListPermutation(A, xs, ys)96shows ListPermutation(A, append(xs, zs), append(ys, zs))97proof98unfold ListPermutation99intro x100have count := permutation with x101rw [listCount.append, listCount.append, count]102rfl103104theorem ListPermutation.append (A : Set) (xs ys us vs : List(A))105requires first : ListPermutation(A, xs, ys)106requires second : ListPermutation(A, us, vs)107shows ListPermutation(A, append(us, xs), append(vs, ys))108proof109unfold ListPermutation110intro x111haveleft:= first with x112haveright:= second with x113rw [listCount.append, listCount.append, left, right]114rfl115116theorem ListPermutation.append_commutative (A : Set) (xs ys : List(A))117shows ListPermutation(A, append(ys, xs), append(xs, ys))118proof119unfold ListPermutation120intro x121have xsNat := listCount.natural at A, x, xs with*122have ysNat := listCount.natural at A, x, ys with*123rw [listCount.append, listCount.append, add.commutative]124rfl125126theorem ListPermutation.reverse (A : Set) (xs : List(A))127shows ListPermutation(A, reverse(xs), xs)128proof129unfold ListPermutation130intro x131exact listCount.reverse at A, x, xs with*132133theorem ListPermutation.swap (A a b : Set) (xs : List(A))134requires first : a ∈ A135requires second : b ∈ A136shows ListPermutation(A, a :: b :: xs, b :: a :: xs)137proof138unfold ListPermutation139intro x140have inner : List(A, b :: xs) bytypecheck141have other : List(A, a :: xs) bytypecheck142have markA := indicator.natural at {x}, a143have markB := indicator.natural at {x}, b144have count := listCount.natural at A, x, xs with*145rw [listCount.cons, listCount.cons, listCount.cons, listCount.cons]146exact add.swap at indicator({x}, b), listCount{A}(x, xs), indicator({x}, a) with*147148theorem ListPermutation.middle (A a : Set) (xs ys : List(A))149requires element : a ∈ A150shows ListPermutation(A, append(ys, a :: xs), a :: append(ys, xs))151proof152unfold ListPermutation153intro x154have whole : List(A, a :: xs) bytypecheck155have appended := append.closed at A, xs, ys with*156have mark := indicator.natural at {x}, a157have xsNat := listCount.natural at A, x, xs with*158have ysNat := listCount.natural at A, x, ys with*159rw [listCount.append, listCount.cons, listCount.cons, listCount.append]160exact add.associative at listCount{A}(x, xs), listCount{A}(x, ys), indicator({x}, a) with*161162theorem ListPermutation.rotate (A : Set) (xs : List(A)) (n : Nat)163shows ListPermutation(A, xs, append(listTake{A}(n, xs), listDrop{A}(n, xs)))164proof165havesplit:= listTake.append_drop at A, xs, n with*166have taken := listTake.closed at A, xs, n with*167have dropped := listDrop.closed at A, xs, n with*168have swapped := ListPermutation.append_commutative at A, listTake{A}(n, xs), listDrop{A}(n, xs) with*169 rwa splitat swapped170171theorem ListPermutation.mem_support (A y : Set) (xs ys : List(A))172requires permutation : ListPermutation(A, xs, ys)173requires member : y ∈ listSupport{A}(xs)174shows y ∈ listSupport{A}(ys)175proof176have positive := listCount.positive_of_mem with member177have count := permutation with y178rw count at positive179exact listCount.mem_of_positive with positive180181theorem ListPermutation.support (A : Set) (xs ys : List(A))182requires permutation : ListPermutation(A, xs, ys)183shows listSupport{A}(xs) = listSupport{A}(ys)184proof185have back := ListPermutation.symmetric with permutation186ext y187constructor188 . intro member189exact ListPermutation.mem_support with permutation, member190 . intro member191exact ListPermutation.mem_support with back, member192193theorem ListPermutation.nil (A : Set) (xs : List(A))194requires permutation : ListPermutation(A, xs, [])195shows xs = []196proof197have support := ListPermutation.support with permutation198rw listSupport.nil at support199exact listSupport.eq_empty with support200201theorem ListPermutation.filter (A S : Set) (xs ys : List(A))202requires permutation : ListPermutation(A, xs, ys)203shows ListPermutation(A, listFilter{A}(S, xs), listFilter{A}(S, ys))204proof205unfold ListPermutation206intro x207have alternatives : x ∈ S ∨¬(x ∈ S) bytauto208cases alternatives with member | absent209 . haveleft:= listCount.filter_member at A, S, x, xs with member210haveright:= listCount.filter_member at A, S, x, ys with member211rw [left, right]212exact permutation with x213 . haveleft:= listCount.filter_other at A, S, x, xs with absent214haveright:= listCount.filter_other at A, S, x, ys with absent215rw [left, right]216rfl217218-- A list is split at any element of its support.219theorem listSupport.split (A a : Set) (xs : List(A))220requires member : a ∈ listSupport{A}(xs)221shows∃ front, List(A, front) ∧∃ back, List(A, back) ∧ xs = append(a :: back, front)222proof223induction xs224| List.nil =>225intro member226rw listSupport.nil at member227exfalso228exactset.empty with member229| List.cons(b, tail) using ih =>230intro member231have element : b ∈ A byassumption232havesplit:= listSupport.mem_cons_iff.forward with element, member233casessplitwith head | rest234 . have nilList : List(A, []) bytypecheck235refine⟨[], nilList, tail, assumption, ?_⟩236rw head237simp [append]238 . obtain⟨front, frontList, back, backList, equal⟩:= ih with rest239have subset := listSupport.subset at A, tail with*240have aA := subset with a, rest241have frontCons : List(A, b :: front) bytypecheck242have backCons : List(A, a :: back) bytypecheck243refine⟨b :: front, frontCons, back, backList, ?_⟩244rw [append.cons, ← equal]245rfl246247-- Removing the head of a permuted list from the other side.248theorem ListPermutation.cons_split (A a : Set) (xs ys : List(A))249requires element : a ∈ A250requires permutation : ListPermutation(A, a :: xs, ys)251shows∃ front, List(A, front) ∧∃ back, List(A, back) ∧252 ys = append(a :: back, front) ∧ ListPermutation(A, xs, append(back, front))253proof254have headIn := listSupport.mem_cons_self at A, a, xs with element255have member := ListPermutation.mem_support with permutation, headIn256obtain⟨front, frontList, back, backList, equal⟩:= listSupport.split with member257refine⟨front, frontList, back, backList, equal, ?_⟩258unfold ListPermutation259intro x260have count := permutation with x261have backCons : List(A, a :: back) bytypecheck262have mark := indicator.natural at {x}, a263have xsNat := listCount.natural at A, x, xs with*264have frontNat := listCount.natural at A, x, front with*265have backNat := listCount.natural at A, x, back with*266rw [equal, listCount.cons, listCount.append, listCount.cons] at count267rw listCount.append268have shuffled : listCount{A}(x, front) + (indicator({x}, a) + listCount{A}(x, back)) =269 indicator({x}, a) + (listCount{A}(x, front) + listCount{A}(x, back))270exact add.swap at indicator({x}, a), listCount{A}(x, back), listCount{A}(x, front) with*271rw shuffled at count272exact add.left_cancel with count273274theorem ListPermutation.length (A : Set) (xs ys : List(A))275requires permutation : ListPermutation(A, xs, ys)276shows length{A}(xs) = length{A}(ys)277proof278induction xs generalizing ys279| List.nil =>280intros ys ysList permutation281have back := ListPermutation.symmetric with permutation282have empty := ListPermutation.nil with back283rw empty284rfl285| List.cons(a, tail) using ih =>286intros ys ysList permutation287have element : a ∈ A byassumption288obtain⟨front, frontList, back, backList, equal, rest⟩:=289 ListPermutation.cons_split with element, permutation290have appended := append.closed at A, front, back with*291have inner := ih with append(back, front), appended, rest292have backCons : List(A, a :: back) bytypecheck293rw [equal, length.cons, inner, length.append, length.append, length.cons]294have shift : succ(length{A}(front) + length{A}(back)) = length{A}(front) + succ(length{A}(back))295rw add.succ_right296rfl297exact shift298299theorem ListPermutation.singleton (A a : Set) (xs : List(A))300requires element : a ∈ A301requires permutation : ListPermutation(A, a :: [], xs)302shows xs = a :: []303proof304obtain⟨front, frontList, back, backList, equal, rest⟩:=305 ListPermutation.cons_split with element, permutation306have appended := append.closed at A, front, back with*307have back2 := ListPermutation.symmetric with rest308have empty := ListPermutation.nil with back2309have both := append.eq_nil with empty310obtain⟨frontNil, backNil⟩:= both311rw [equal, frontNil, backNil]312simp [append]313314theorem ListPermutation.replicate (A x : Set) (xs : List(A)) (n : Nat)315requires element : x ∈ A316requires permutation : ListPermutation(A, xs, listReplicate(n, x))317shows xs = listReplicate(n, x)318proof319have list := listReplicate.closed at A, x, n with element320have support := ListPermutation.support with permutation321have lengths := ListPermutation.length with permutation322have covered := listReplicate.support_subset at A, x, n with element323rw← support at covered324have constant := List.constant_of_support with element, covered325rw [listReplicate.length] at lengths326rw lengths at constant327exact constant328329theorem ListPermutation.length_filter (A S : Set) (xs ys : List(A))330requires permutation : ListPermutation(A, xs, ys)331shows length{A}(listFilter{A}(S, xs)) = length{A}(listFilter{A}(S, ys))332proof333have filtered := ListPermutation.filter at A, S, xs, ys with permutation334have leftList := listFilter.closed at A, S, xs with*335have rightList := listFilter.closed at A, S, ys with*336exact ListPermutation.length with filtered337