1-- Lists without repeated entries: every count is at most one, and this2-- passes to tails, prefixes, suffixes, filters, reversals, and permutations3-- 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.NoDup9import Range1011predicate ListNoDup (A xs : Set) :=∀ x, listCount{A}(x, xs) ≤11213theorem ListNoDup.nil (A : Set)14shows ListNoDup(A, [])15proof16unfold ListNoDup17intro x18rw listCount.nil19arith2021theorem ListNoDup.count_le (A x : Set) (xs : List(A))22requires nodup : ListNoDup(A, xs)23shows listCount{A}(x, xs) ≤124proof25exact nodup with x2627theorem ListNoDup.count_eq_one (A x : Set) (xs : List(A))28requires nodup : ListNoDup(A, xs)29requires member : x ∈ listSupport{A}(xs)30shows listCount{A}(x, xs) =131proof32have upper := nodup with x33have positive := listCount.positive_of_mem with member34have natural := listCount.natural at A, x, xs with*35have lower : 1≤ listCount{A}(x, xs) byarith36exact LessEqual.antisymmetric with upper, lower3738theorem ListNoDup.singleton (A a : Set)39requires element : a ∈ A40shows ListNoDup(A, a :: [])41proof42unfold ListNoDup43intro x44have alternatives : x = a ∨ x ≠ a bytauto45cases alternatives with same | different46 . rw [same, listCount.singleton_self]47arith48 . rw listCount.singleton_other49arith5051theorem ListNoDup.head_absent (A a : Set) (xs : List(A))52requires element : a ∈ A53requires nodup : ListNoDup(A, a :: xs)54shows¬(a ∈ listSupport{A}(xs))55proof56intro member57have count := nodup with a58have positive := listCount.positive_of_mem with member59have natural := listCount.natural at A, a, xs with*60rw listCount.cons_self at count61arith6263theorem ListNoDup.tail (A a : Set) (xs : List(A))64requires element : a ∈ A65requires nodup : ListNoDup(A, a :: xs)66shows ListNoDup(A, xs)67proof68unfold ListNoDup69intro x70have count := nodup with x71have mark := indicator.natural at {x}, a72have natural := listCount.natural at A, x, xs with*73rw listCount.cons at count74arith7576theorem ListNoDup.cons (A a : Set) (xs : List(A))77requires element : a ∈ A78requires absent : ¬(a ∈ listSupport{A}(xs))79requires nodup : ListNoDup(A, xs)80shows ListNoDup(A, a :: xs)81proof82unfold ListNoDup83intro x84have natural := listCount.natural at A, x, xs with*85have alternatives : x = a ∨ x ≠ a bytauto86cases alternatives with same | different87 . have zero := listCount.zero_of_absent with absent88rw [same, listCount.cons_self, zero]89arith90 . rw listCount.cons_other91exact nodup with x9293theorem ListNoDup.cons_iff (A a : Set) (xs : List(A))94requires element : a ∈ A95shows ListNoDup(A, a :: xs) ↔ (¬(a ∈ listSupport{A}(xs)) ∧ ListNoDup(A, xs))96proof97constructor98 . intro nodup99have absent := ListNoDup.head_absent with nodup100have tail := ListNoDup.tail with nodup101exact⟨absent, tail⟩102 . intro both103obtain⟨absent, tail⟩:= both104exact ListNoDup.cons with absent, tail105106theorem ListNoDup.of_permutation (A : Set) (xs ys : List(A))107requires permutation : ListPermutation(A, xs, ys)108requires nodup : ListNoDup(A, xs)109shows ListNoDup(A, ys)110proof111unfold ListNoDup112intro x113have count := permutation with x114rw← count115exact nodup with x116117theorem ListNoDup.reverse (A : Set) (xs : List(A))118requires nodup : ListNoDup(A, xs)119shows ListNoDup(A, reverse(xs))120proof121unfold ListNoDup122intro x123rw listCount.reverse124exact nodup with x125126theorem ListNoDup.prefix (A : Set) (xs ys : List(A))127requiresprefix : ListPrefix(A, xs, ys)128requires nodup : ListNoDup(A, ys)129shows ListNoDup(A, xs)130proof131unfold ListNoDup132intro x133have bound := ListPrefix.count_le at A, x, xs, ys withprefix134have upper := nodup with x135exact LessEqual.transitive with bound, upper136137theorem ListNoDup.suffix (A : Set) (xs ys : List(A))138requires suffix : ListSuffix(A, xs, ys)139requires nodup : ListNoDup(A, ys)140shows ListNoDup(A, xs)141proof142unfold ListNoDup143intro x144have bound := ListSuffix.count_le at A, x, xs, ys with suffix145have upper := nodup with x146exact LessEqual.transitive with bound, upper147148theorem ListNoDup.take (A : Set) (xs : List(A)) (n : Nat)149requires nodup : ListNoDup(A, xs)150shows ListNoDup(A, listTake{A}(n, xs))151proof152have taken := listTake.closed at A, xs, n with*153haveprefix:= ListPrefix.take at A, xs, n with*154exact ListNoDup.prefix withprefix, nodup155156theorem ListNoDup.drop (A : Set) (xs : List(A)) (n : Nat)157requires nodup : ListNoDup(A, xs)158shows ListNoDup(A, listDrop{A}(n, xs))159proof160have dropped := listDrop.closed at A, xs, n with*161have suffix := ListSuffix.drop at A, xs, n with*162exact ListNoDup.suffix with suffix, nodup163164theorem ListNoDup.filter (A S : Set) (xs : List(A))165requires nodup : ListNoDup(A, xs)166shows ListNoDup(A, listFilter{A}(S, xs))167proof168unfold ListNoDup169intro x170have alternatives : x ∈ S ∨¬(x ∈ S) bytauto171cases alternatives with member | absent172 . rw listCount.filter_member173exact nodup with x174 . rw listCount.filter_other175arith176177theorem ListNoDup.append_disjoint (A : Set) (xs ys : List(A))178requires first : ListNoDup(A, xs)179requires second : ListNoDup(A, ys)180requires disjoint : ∀ x ∈ listSupport{A}(xs), ¬(x ∈ listSupport{A}(ys))181shows ListNoDup(A, append(ys, xs))182proof183unfold ListNoDup184intro x185have xsNat := listCount.natural at A, x, xs with*186have ysNat := listCount.natural at A, x, ys with*187rw listCount.append188have alternatives : x ∈ listSupport{A}(xs) ∨¬(x ∈ listSupport{A}(xs)) bytauto189cases alternatives with member | absent190 . have other := disjoint with x, member191have zero := listCount.zero_of_absent with other192have bound := first with x193rw zero194arith195 . have zero := listCount.zero_of_absent with absent196have bound := second with x197rw zero198arith199200theorem ListNoDup.append_left (A : Set) (xs ys : List(A))201requires nodup : ListNoDup(A, append(ys, xs))202shows ListNoDup(A, xs)203proof204haveprefix:= ListPrefix.append at A, xs, ys with*205exact ListNoDup.prefix withprefix, nodup206207theorem ListNoDup.append_right (A : Set) (xs ys : List(A))208requires nodup : ListNoDup(A, append(ys, xs))209shows ListNoDup(A, ys)210proof211have suffix := ListSuffix.append at A, ys, xs with*212exact ListNoDup.suffix with suffix, nodup213214theorem ListNoDup.append_disjoint_supports (A : Set) (xs ys : List(A))215requires nodup : ListNoDup(A, append(ys, xs))216shows∀ x ∈ listSupport{A}(xs), ¬(x ∈ listSupport{A}(ys))217proof218intros x member other219have bound := nodup with x220haveleft:= listCount.positive_of_mem with member221haveright:= listCount.positive_of_mem with other222have xsNat := listCount.natural at A, x, xs with*223have ysNat := listCount.natural at A, x, ys with*224rw listCount.append at bound225arith226227theorem ListNoDup.replicate (A x : Set) (n : Nat)228requires element : x ∈ A229requires nodup : ListNoDup(A, listReplicate(n, x))230shows n ≤1231proof232have list := listReplicate.closed at A, x, n with element233have bound := nodup with x234 rwa listCount.replicate_self at bound235236theorem ListNoDup.range (n : Nat)237shows ListNoDup(ℕ, listRange(n))238proof239have list := listRange.closed at n with*240unfold ListNoDup241intro x242have alternatives : x ∈ listSupport{ℕ}(listRange(n)) ∨¬(x ∈ listSupport{ℕ}(listRange(n))) bytauto243cases alternatives with member | absent244 . have both := listRange.mem_support_iff.forward at n, x with member245obtain⟨xNat, below⟩:= both246have one := listRange.count_below at n, x with below247rw one248arith249 . have zero := listCount.zero_of_absent with absent250rw zero251arith252