1-- Filtering a list by membership in a set: the entries kept, their2-- support, and how filtering passes through concatenation and reversal3-- Part of Cumulus, the Grace17 mathematics library.4-- Copyright (c) 2026 Grant Passmore.5-- Released under the Apache License 2.0. See LICENSE in the repository root.6-- Authors: Grant Passmore78module Grace.Cumulus.List.Filter9import TakeDrop10import Set.Indicator1112-- Membership in S as a Boolean, through the natural-valued indicator.13def memberTest (S x : Set) : Bool := BoolOps.not(isZero(indicator(S, x)))1415theorem memberTest.true (S x : Set)16requires member : x ∈ S17shows memberTest(S, x) = Bool.true18proof19have one := indicator.member with member20simp [memberTest, one, isZero, BoolOps.not]2122theorem memberTest.false (S x : Set)23requires absent : ¬(x ∈ S)24shows memberTest(S, x) = Bool.false25proof26have zero := indicator.absent with absent27simp [memberTest, zero, isZero, BoolOps.not]2829theorem memberTest.congr (S T x y : Set)30requires equivalent : x ∈ S ↔ y ∈ T31shows memberTest(S, x) = memberTest(T, y)32proof33have same := indicator.congr with equivalent34simp [memberTest, same]3536def listFilter {A : Set} (S : Set) (xs : List(A)) →Set37| S, List.nil => []38| S, List.cons(a, tail) =>39 boolChoice(memberTest(S, a), a :: listFilter(S, tail), listFilter(S, tail))40descends xs4142theorem listFilter.nil (A S : Set)43shows listFilter{A}(S, []) = []44proof45simp [listFilter]4647theorem listFilter.cons_member (A S a : Set) (xs : List(A))48requires element : a ∈ A49requires member : a ∈ S50shows listFilter{A}(S, a :: xs) = a :: listFilter{A}(S, xs)51proof52have test := memberTest.true with member53simp [listFilter, test, boolChoice]5455theorem listFilter.cons_other (A S a : Set) (xs : List(A))56requires element : a ∈ A57requires absent : ¬(a ∈ S)58shows listFilter{A}(S, a :: xs) = listFilter{A}(S, xs)59proof60have test := memberTest.false with absent61simp [listFilter, test, boolChoice]6263theorem listFilter.closed (A S : Set) (xs : List(A))64shows List(A, listFilter{A}(S, xs))65proof66induction xs67| List.nil =>68rw listFilter.nil69typecheck70| List.cons(a, tail) using ih =>71have element : a ∈ A byassumption72have alternatives : a ∈ S ∨¬(a ∈ S) bytauto73cases alternatives with member | absent74 . rw listFilter.cons_member75typecheck76 . rw listFilter.cons_other77exact ih7879theorem listFilter.length_le (A S : Set) (xs : List(A))80shows length{A}(listFilter{A}(S, xs)) ≤ length{A}(xs)81proof82induction xs83| List.nil =>84rw [listFilter.nil, length.nil]85arith86| List.cons(a, tail) using ih =>87have element : a ∈ A byassumption88have filtered := listFilter.closed at A, S, tail with*89have alternatives : a ∈ S ∨¬(a ∈ S) bytauto90rw length.cons91cases alternatives with member | absent92 . rw [listFilter.cons_member, length.cons]93arith94 . rw listFilter.cons_other95arith9697theorem listFilter.append (A S : Set) (xs ys : List(A))98shows listFilter{A}(S, append(ys, xs)) =99 append(listFilter{A}(S, ys), listFilter{A}(S, xs))100proof101induction xs102| List.nil =>103have filtered := listFilter.closed at A, S, ys with*104rw [append.nil, listFilter.nil, append.nil]105rfl106| List.cons(a, tail) using ih =>107have element : a ∈ A byassumption108have appended := append.closed at A, tail, ys with*109have filteredTail := listFilter.closed at A, S, tail with*110have filteredYs := listFilter.closed at A, S, ys with*111have alternatives : a ∈ S ∨¬(a ∈ S) bytauto112rw append.cons113cases alternatives with member | absent114 . rw [listFilter.cons_member, listFilter.cons_member, ih, append.cons]115rfl116 . rw [listFilter.cons_other, listFilter.cons_other, ih]117rfl118119theorem listFilter.mem_support (A S y : Set) (xs : List(A))120requires member : y ∈ listSupport{A}(listFilter{A}(S, xs))121shows y ∈ listSupport{A}(xs) ∧ y ∈ S122proof123induction xs124| List.nil =>125intro member126rw [listFilter.nil, listSupport.nil] at member127exfalso128exactset.empty with member129| List.cons(a, tail) using ih =>130intro member131have element : a ∈ A byassumption132have filtered := listFilter.closed at A, S, tail with*133have alternatives : a ∈ S ∨¬(a ∈ S) bytauto134cases alternatives with inS | absent135 . rw listFilter.cons_member at member136havesplit:= listSupport.mem_cons_iff.forward with element, member137casessplitwith head | rest138 . rw head139have headIn := listSupport.mem_cons_self at A, a, tail with element140exact⟨headIn, inS⟩141 . obtain⟨inner, yS⟩:= ih with rest142have outer := listSupport.mem_cons_of_mem with element, inner143exact⟨outer, yS⟩144 . rw listFilter.cons_other at member145obtain⟨inner, yS⟩:= ih with member146have outer := listSupport.mem_cons_of_mem with element, inner147exact⟨outer, yS⟩148149theorem listFilter.support_subset (A S : Set) (xs : List(A))150shows listSupport{A}(listFilter{A}(S, xs)) ⊆ listSupport{A}(xs)151proof152intros y hy153have both := listFilter.mem_support with hy154exact both.left155156theorem listFilter.support_in (A S : Set) (xs : List(A))157shows listSupport{A}(listFilter{A}(S, xs)) ⊆ S158proof159intros y hy160have both := listFilter.mem_support with hy161exact both.right162163theorem listFilter.mem_support_of (A S y : Set) (xs : List(A))164requires member : y ∈ listSupport{A}(xs)165requires inS : y ∈ S166shows y ∈ listSupport{A}(listFilter{A}(S, xs))167proof168induction xs169| List.nil =>170intro member171rw listSupport.nil at member172exfalso173exactset.empty with member174| List.cons(a, tail) using ih =>175intro member176have element : a ∈ A byassumption177have filtered := listFilter.closed at A, S, tail with*178havesplit:= listSupport.mem_cons_iff.forward with element, member179have alternatives : a ∈ S ∨¬(a ∈ S) bytauto180cases alternatives with aS | absent181 . rw listFilter.cons_member182casessplitwith head | rest183 . rw head184exact listSupport.mem_cons_self with element185 . have inner := ih with rest186exact listSupport.mem_cons_of_mem with element, inner187 . rw listFilter.cons_other188casessplitwith head | rest189 . rw head at inS190exfalso191exact absent with inS192 . exact ih with rest193194theorem listFilter.mem_support_iff (A S y : Set) (xs : List(A))195shows y ∈ listSupport{A}(listFilter{A}(S, xs)) ↔ y ∈ listSupport{A}(xs) ∧ y ∈ S196proof197constructor198 . intro member199exact listFilter.mem_support with member200 . intro both201obtain⟨member, inS⟩:= both202exact listFilter.mem_support_of with member, inS203204theorem listFilter.support (A S : Set) (xs : List(A))205shows listSupport{A}(listFilter{A}(S, xs)) = listSupport{A}(xs) ∩ S206proof207ext y208have kept := listFilter.mem_support_iff at A, S, y, xs with*209have meet := intersection.membership at listSupport{A}(xs), S, y210tauto211212theorem listFilter.all (A S : Set) (xs : List(A))213requires covered : listSupport{A}(xs) ⊆ S214shows listFilter{A}(S, xs) = xs215proof216induction xs217| List.nil =>218intro covered219exact listFilter.nil at A, S220| List.cons(a, tail) using ih =>221intro covered222have element : a ∈ A byassumption223have headIn := listSupport.mem_cons_self at A, a, tail with element224have member := covered with a, headIn225have rest : listSupport{A}(tail) ⊆ S226intros y hy227have outer := listSupport.mem_cons_of_mem with element, hy228exact covered with y, outer229have inner := ih with rest230rw [listFilter.cons_member, inner]231rfl232233theorem listFilter.carrier (A : Set) (xs : List(A))234shows listFilter{A}(A, xs) = xs235proof236have covered := listSupport.subset at A, xs with*237exact listFilter.all with covered238239theorem listFilter.none (A S : Set) (xs : List(A))240requires disjoint : ∀ y ∈ listSupport{A}(xs), ¬(y ∈ S)241shows listFilter{A}(S, xs) = []242proof243induction xs244| List.nil =>245intro disjoint246exact listFilter.nil at A, S247| List.cons(a, tail) using ih =>248intro disjoint249have element : a ∈ A byassumption250have headIn := listSupport.mem_cons_self at A, a, tail with element251have absent := disjoint with a, headIn252have rest : ∀ y ∈ listSupport{A}(tail), ¬(y ∈ S)253intros y hy254have outer := listSupport.mem_cons_of_mem with element, hy255exact disjoint with y, outer256have inner := ih with rest257rw [listFilter.cons_other, inner]258rfl259260theorem listFilter.empty (A : Set) (xs : List(A))261shows listFilter{A}(∅, xs) = []262proof263have disjoint : ∀ y ∈ listSupport{A}(xs), ¬(y ∈∅)264intros y hy265exactset.empty at y266exact listFilter.none with disjoint267268theorem listFilter.eq_nil (A S : Set) (xs : List(A))269requires empty : listFilter{A}(S, xs) = []270shows∀ y ∈ listSupport{A}(xs), ¬(y ∈ S)271proof272intros y hy inS273have member := listFilter.mem_support_of with hy, inS274rw [empty, listSupport.nil] at member275exactset.empty with member276277theorem listFilter.nil_iff (A S : Set) (xs : List(A))278shows listFilter{A}(S, xs) = [] ↔∀ y ∈ listSupport{A}(xs), ¬(y ∈ S)279proof280constructor281 . intro empty282exact listFilter.eq_nil with empty283 . intro disjoint284exact listFilter.none with disjoint285286theorem listFilter.congr (A S T : Set) (xs : List(A))287requires agreement : ∀ y ∈ listSupport{A}(xs), (y ∈ S ↔ y ∈ T)288shows listFilter{A}(S, xs) = listFilter{A}(T, xs)289proof290induction xs291| List.nil =>292intro agreement293rw [listFilter.nil, listFilter.nil]294rfl295| List.cons(a, tail) using ih =>296intro agreement297have element : a ∈ A byassumption298have headIn := listSupport.mem_cons_self at A, a, tail with element299have head := agreement with a, headIn300have rest : ∀ y ∈ listSupport{A}(tail), (y ∈ S ↔ y ∈ T)301intros y hy302have outer := listSupport.mem_cons_of_mem with element, hy303exact agreement with y, outer304have inner := ih with rest305have alternatives : a ∈ S ∨¬(a ∈ S) bytauto306cases alternatives with inS | absent307 . have inT : a ∈ T := head.forward with inS308rw [listFilter.cons_member, listFilter.cons_member, inner]309rfl310 . have notT : ¬(a ∈ T)311intro inT312have inS := head.backward with inT313exact absent with inS314rw [listFilter.cons_other, listFilter.cons_other, inner]315rfl316317theorem listFilter.filter (A S T : Set) (xs : List(A))318shows listFilter{A}(S, listFilter{A}(T, xs)) = listFilter{A}(S ∩ T, xs)319proof320induction xs321| List.nil =>322rw [listFilter.nil, listFilter.nil, listFilter.nil]323rfl324| List.cons(a, tail) using ih =>325have element : a ∈ A byassumption326have filtered := listFilter.closed at A, T, tail with*327have alternativesT : a ∈ T ∨¬(a ∈ T) bytauto328have alternativesS : a ∈ S ∨¬(a ∈ S) bytauto329cases alternativesT with inT | notT330 . rw listFilter.cons_member331cases alternativesS with inS | notS332 . have both : a ∈ S ∩ T bysimp333rw [listFilter.cons_member, listFilter.cons_member, ih]334rfl335 . have notBoth : ¬(a ∈ S ∩ T) bysimp336rw [listFilter.cons_other, listFilter.cons_other, ih]337rfl338 . have notBoth : ¬(a ∈ S ∩ T) bysimp339rw [listFilter.cons_other, listFilter.cons_other, ih]340rfl341342theorem listFilter.idempotent (A S : Set) (xs : List(A))343shows listFilter{A}(S, listFilter{A}(S, xs)) = listFilter{A}(S, xs)344proof345have covered := listFilter.support_in at A, S, xs with*346exact listFilter.all with covered347348theorem listFilter.commutative (A S T : Set) (xs : List(A))349shows listFilter{A}(S, listFilter{A}(T, xs)) = listFilter{A}(T, listFilter{A}(S, xs))350proof351rw [listFilter.filter, listFilter.filter, intersection.commutative]352rfl353354theorem listFilter.singleton_member (A S a : Set)355requires element : a ∈ A356requires member : a ∈ S357shows listFilter{A}(S, a :: []) = a :: []358proof359have test := memberTest.true with member360simp [listFilter, test, boolChoice]361362theorem listFilter.singleton_other (A S a : Set)363requires element : a ∈ A364requires absent : ¬(a ∈ S)365shows listFilter{A}(S, a :: []) = []366proof367have test := memberTest.false with absent368simp [listFilter, test, boolChoice]369370theorem listFilter.reverse (A S : Set) (xs : List(A))371shows listFilter{A}(S, reverse(xs)) = reverse(listFilter{A}(S, xs))372proof373induction xs374| List.nil =>375rw [reverse.nil, listFilter.nil, reverse.nil]376rfl377| List.cons(a, tail) using ih =>378have element : a ∈ A byassumption379have single : List(A, a :: []) bytypecheck380have reversed : List(A, reverse(tail)) bytypecheck381have filtered := listFilter.closed at A, S, tail with*382have filteredReversed := listFilter.closed at A, S, reverse(tail) with*383have reversedFiltered : List(A, reverse(listFilter{A}(S, tail))) bytypecheck384rw [reverse.cons, listFilter.append, ih]385have alternatives : a ∈ S ∨¬(a ∈ S) bytauto386cases alternatives with member | absent387 . rw [listFilter.singleton_member, listFilter.cons_member, reverse.cons]388rfl389 . rw [listFilter.singleton_other, listFilter.cons_other, append.right_unit]390rfl391392theorem listFilter.replicate_member (A S x : Set) (n : Nat)393requires element : x ∈ A394requires member : x ∈ S395shows listFilter{A}(S, listReplicate(n, x)) = listReplicate(n, x)396proof397have list := listReplicate.closed at A, x, n with element398have covered := listReplicate.support_subset at A, x, n with element399have inside : listSupport{A}(listReplicate(n, x)) ⊆ S400intros y hy401have single := covered with y, hy402have equal := singleton.membership.forward with single403rw equal404exact member405exact listFilter.all with inside406407theorem listFilter.replicate_other (A S x : Set) (n : Nat)408requires element : x ∈ A409requires absent : ¬(x ∈ S)410shows listFilter{A}(S, listReplicate(n, x)) = []411proof412have list := listReplicate.closed at A, x, n with element413have covered := listReplicate.support_subset at A, x, n with element414have disjoint : ∀ y ∈ listSupport{A}(listReplicate(n, x)), ¬(y ∈ S)415intros y hy416have single := covered with y, hy417have equal := singleton.membership.forward with single418rw equal419exact absent420exact listFilter.none with disjoint421422-- The kept and the discarded entries together account for the length.423theorem listFilter.length_add_complement (A S : Set) (xs : List(A))424shows length{A}(listFilter{A}(S, xs)) + length{A}(listFilter{A}(A ∖ S, xs)) =425 length{A}(xs)426proof427induction xs428| List.nil =>429rw [listFilter.nil, listFilter.nil, length.nil, add.right_unit]430rfl431| List.cons(a, tail) using ih =>432have element : a ∈ A byassumption433have kept := listFilter.closed at A, S, tail with*434have discarded := listFilter.closed at A, A ∖ S, tail with*435have alternatives : a ∈ S ∨¬(a ∈ S) bytauto436rw length.cons437cases alternatives with member | absent438 . have notDifference : ¬(a ∈ A ∖ S) bysimp439rw [listFilter.cons_member, listFilter.cons_other, length.cons]440have shift : succ(length{A}(listFilter{A}(S, tail))) + length{A}(listFilter{A}(A ∖ S, tail)) =441 succ(length{A}(listFilter{A}(S, tail)) + length{A}(listFilter{A}(A ∖ S, tail)))442simp [add, add.succ_right]443rw [shift, ih]444rfl445 . have inDifference : a ∈ A ∖ S bysimp446rw [listFilter.cons_other, listFilter.cons_member, length.cons, add.succ_right, ih]447rfl448449theorem listFilter.complement_length_le (A S : Set) (xs : List(A))450shows length{A}(listFilter{A}(A ∖ S, xs)) ≤ length{A}(xs)451proof452have total := listFilter.length_add_complement at A, S, xs with*453have kept := listFilter.closed at A, S, xs with*454have discarded := listFilter.closed at A, A ∖ S, xs with*455rw← total456arith457458theorem listFilter.length_eq_of_all (A S : Set) (xs : List(A))459requires full : length{A}(listFilter{A}(S, xs)) = length{A}(xs)460shows listFilter{A}(S, xs) = xs461proof462have total := listFilter.length_add_complement at A, S, xs with*463have kept := listFilter.closed at A, S, xs with*464have discarded := listFilter.closed at A, A ∖ S, xs with*465rw full at total466have padded : length{A}(xs) + length{A}(listFilter{A}(A ∖ S, xs)) = length{A}(xs) +0467rw add.right_unit468exact total469have zero := add.left_cancel with padded470have emptyComplement := length.eq_zero with zero471have disjoint := listFilter.eq_nil with emptyComplement472have covered : listSupport{A}(xs) ⊆ S473intros y hy474have yA := listSupport.subset at A, xs with*475have inA := yA with y, hy476have notDifference := disjoint with y, hy477have alternatives : y ∈ S ∨¬(y ∈ S) bytauto478cases alternatives with inS | notS479 . exact inS480 . have inDifference : y ∈ A ∖ S bysimp481exfalso482exact notDifference with inDifference483exact listFilter.all with covered484