Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Z mod n as Fin #2073

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3361,3 +3361,20 @@ This is a full list of proofs that have changed form to use irrelevant instance
c ≈⟨ c≈d ⟩
d ∎
```

* Added new file `Data.Fin.Mod`
```agda
suc : Fin n → Fin n
pred : Fin n → Fin n
_ℕ+_ : ℕ → Fin n → Fin n
_+_ : Fin m → Fin n → Fin n
_+‵_ : Fin n → Fin n → Fin n
_-_ : Fin n → Fin n → Fin n
suc-inj≡fsuc : ∀ i → suc (inject₁ i) ≡ F.suc i
pred-fsuc≡inj : ∀ i → pred (F.suc i) ≡ inject₁ i
suc-pred≡id : ∀ i → suc (pred i) ≡ i
pred-suc≡id : ∀ i → pred (suc i) ≡ i
+-identityˡ : LeftIdentity {ℕ.suc n} zero _+_
+ℕ-identityʳ : m ℕ.≤ suc n → toℕ (m ℕ+ zero) ≡ m
+-identityʳ : RightIdentity zero _+_
```
107 changes: 107 additions & 0 deletions src/Data/Fin/Mod.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
------------------------------------------------------------------------
-- The Agda standard library
--
-- ℤ module n
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
------------------------------------------------------------------------

{-# OPTIONS --cubical-compatible --safe #-}

module Data.Fin.Mod where

open import Function using (_$_; _∘_)
open import Data.Bool using (true; false)
open import Data.Nat as ℕ using (ℕ; s≤s)
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
open import Data.Nat.Properties as ℕ
using (m≤n⇒m≤1+n; 1+n≰n; module ≤-Reasoning)
open import Data.Fin as F hiding (suc; pred; _+_; _-_)
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
open import Data.Fin.Properties
open import Relation.Nullary using (Dec; yes; no; contradiction)
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
open import Relation.Binary.PropositionalEquality
using (_≡_; _≢_; refl; sym; trans; cong; module ≡-Reasoning)
import Algebra.Definitions as ADef

private variable
m n : ℕ
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved

open module AD {n} = ADef {A = Fin n} _≡_
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be an anonymous module?


private
hs : ∀ {i : Fin (ℕ.suc n)} → i ≢ fromℕ n → Fin (ℕ.suc n)
hs {i = i} i≢n = lower₁ (F.suc i) help
where
help : _
help = i≢n ∘ sym ∘ toℕ-injective ∘ trans (toℕ-fromℕ _) ∘ cong ℕ.pred

suc : Fin n → Fin n
suc {ℕ.suc n} i with i ≟ fromℕ n
... | yes _ = zero
... | no i≢n = hs i≢n
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the following alternative definition (which doesn't actually use the view; but simulates its effect, in the same way as the definition of Data.Fin.Base.opposite:

suc : Fin n  Fin n
suc {n = ℕ.suc ℕ.zero}      zero        = zero
suc {n = ℕ.suc n@(ℕ.suc _)} zero        = Fin.suc zero
suc {n = ℕ.suc n@(ℕ.suc _)} (Fin.suc i) with suc {n} i
... | zero          = zero
... | j@(Fin.suc _) = Fin.suc j

for which one may then prove injectivity, and the view-related inversions, as follows:

suc[fromℕ]≡zero :  n  suc (fromℕ n) ≡ zero
suc[fromℕ]≡zero ℕ.zero                              = refl
suc[fromℕ]≡zero (ℕ.suc n) rewrite suc[fromℕ]≡zero n = refl

suc[fromℕ]≡zero⁻¹ : (i : Fin (ℕ.suc n))  (suc i ≡ zero)  i ≡ fromℕ n
suc[fromℕ]≡zero⁻¹ {n = ℕ.zero}  zero        _ = refl
suc[fromℕ]≡zero⁻¹ {n = ℕ.suc _} (Fin.suc i) _ with zero  suc i in eq
  = cong Fin.suc (suc[fromℕ]≡zero⁻¹ i eq)

suc[inject₁]≡suc[j] : (j : Fin n)  suc (inject₁ j) ≡ Fin.suc j
suc[inject₁]≡suc[j] zero                                      = refl
suc[inject₁]≡suc[j] (Fin.suc j) rewrite suc[inject₁]≡suc[j] j = refl

suc[inject₁]≡suc[j]⁻¹ : (i : Fin (ℕ.suc n)) {j : Fin n} 
                        (suc i ≡ Fin.suc j)  i ≡ inject₁ j
suc[inject₁]≡suc[j]⁻¹ zero        {zero} = λ _  refl
suc[inject₁]≡suc[j]⁻¹ (Fin.suc i) {j}    with suc i in eqᵢ
... | zero      rewrite eqᵢ = λ ()
... | Fin.suc p rewrite eqᵢ = λ eq  begin
  Fin.suc i           ≡⟨ cong Fin.suc (suc[inject₁]≡suc[j]⁻¹ i eqᵢ) ⟩
  inject₁ (Fin.suc p) ≡⟨ cong inject₁ (Fin.suc-injective eq) ⟩
  inject₁ j ∎ where open ≡-Reasoning

suc-injective : {i j : Fin n}  suc i ≡ suc j  i ≡ j
suc-injective {n = n} {i} {j} eq with suc i in eqᵢ | suc j in eqⱼ
... | zero      | zero
    = trans (suc[fromℕ]≡zero⁻¹ i eqᵢ) (sym (suc[fromℕ]≡zero⁻¹ j eqⱼ))
... | Fin.suc p | Fin.suc q
    = begin
  i         ≡⟨ suc[inject₁]≡suc[j]⁻¹ i eqᵢ ⟩
  inject₁ p ≡⟨ cong inject₁ (Fin.suc-injective eq) ⟩
  inject₁ q ≡⟨ sym (suc[inject₁]≡suc[j]⁻¹ j eqⱼ) ⟩
  j ∎ where open ≡-Reasoning


pred : Fin n → Fin n
pred zero = fromℕ _
pred (F.suc i) = inject₁ i

_ℕ+_ : ℕ → Fin n → Fin n
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
ℕ.zero ℕ+ i = i
ℕ.suc n ℕ+ i = suc (n ℕ+ i)

_+_ : Fin m → Fin n → Fin n
Copy link
Contributor

@MatthewDaggitt MatthewDaggitt Jan 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give me an example of a use-case of this operation? Seems a little unnatural to me adding together two numbers of different mods and arbitrarily returning the result mod the right one?

And why does _-_ return the result mod the left one?

i + j = toℕ i ℕ+ j

_+‵_ : Fin n → Fin n → Fin n
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this ever necessary over _+_? A lot of the time Agda should be able to unify the two dimensions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be necessary if someone wants a more restrictive version.
This is one example:

_∘′_ : (B C) (A B) (A C)

_+‵_ = _+_

_-_ : Fin n → Fin n → Fin n
i - j = i + opposite j
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggests that opposite is an inverse function for _+_ (which I agree it should be). Can you prove this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, for a possible alternative (re-)definition of opposite, see also #1923

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, it is with the alternative re-definition.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except that there's still an asymmetry. _+_ has heterogeneous sizes but _-_ is homogeneous?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it assymmetric.


guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
private
inject₁≢fromℕ : {i : Fin n} → inject₁ i ≢ fromℕ n
inject₁≢fromℕ {n} {i} i≡n = 1+n≰n $ begin-strict
toℕ (fromℕ n) ≡˘⟨ cong toℕ i≡n ⟩
toℕ (inject₁ i) <⟨ inject₁ℕ< i ⟩
n ≡˘⟨ toℕ-fromℕ n ⟩
toℕ (fromℕ n) ∎
where open ≤-Reasoning

suc-inj≡fsuc : (i : Fin n) → suc (inject₁ i) ≡ F.suc i
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
suc-inj≡fsuc {n} i with inject₁ i ≟ fromℕ _
... | yes i≡n = contradiction i≡n inject₁≢fromℕ
... | no i≢n = cong F.suc (toℕ-injective (trans (toℕ-lower₁ _ _) (toℕ-inject₁ _)))

pred-fsuc≡inj : (i : Fin n) → pred (F.suc i) ≡ inject₁ i
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
pred-fsuc≡inj _ = refl

suc-pred≡id : (i : Fin n) → suc (pred i) ≡ i
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be suc-pred

suc-pred≡id {ℕ.suc n} zero with fromℕ n ≟ fromℕ n
... | yes _ = refl
... | no n≢n = contradiction refl n≢n
suc-pred≡id {ℕ.suc n} (F.suc i) = suc-inj≡fsuc i

pred-suc≡id : (i : Fin n) → pred (suc i) ≡ i
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
pred-suc≡id {ℕ.suc n} i with i ≟ fromℕ n
... | yes refl = refl
... | no _ = inject₁-lower₁ _ _

+-identityˡ : LeftIdentity {ℕ.suc n} zero _+_
+-identityˡ _ = refl

+ℕ-identityʳ : ∀ {n′} (let n = ℕ.suc n′) → m ℕ.≤ n → toℕ (m ℕ+ zero {n}) ≡ m
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proof of this seems very fiddly, but I admit I'm finding it harder to do better, even for the alternative definition of suc. Hmmm...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this lemma not also provable in the case n = ℕ.zero? By inversion of the hypothesis m ℕ.≤ n, we would obtain m = ℕ.zero, and then the proof is simply refl... or am I missing something?
UPDATED: it seems I might be, after banging my head against your proof for a while...

+ℕ-identityʳ {ℕ.zero} m≤n = refl
+ℕ-identityʳ {ℕ.suc m} {n} (s≤s m≤n) with (m ℕ+ zero) ≟ F.suc (fromℕ n)
... | yes m+0≡sn = contradiction (begin-strict
ℕ.suc n ≡˘⟨ cong ℕ.suc (toℕ-fromℕ n) ⟩
ℕ.suc (toℕ (fromℕ n)) ≡˘⟨ cong toℕ m+0≡sn ⟩
toℕ (m ℕ+ zero) ≡⟨ +ℕ-identityʳ (m≤n⇒m≤1+n m≤n) ⟩
m <⟨ s≤s m≤n ⟩
ℕ.suc n ∎) 1+n≰n
where open ≤-Reasoning

... | no _ = cong ℕ.suc (begin
toℕ (lower₁ (m ℕ+ zero) _) ≡⟨ toℕ-lower₁ (m ℕ+ zero) _ ⟩
toℕ (m ℕ+ zero) ≡⟨ +ℕ-identityʳ (m≤n⇒m≤1+n m≤n) ⟩
m ∎)
where open ≡-Reasoning

+-identityʳ : RightIdentity {ℕ.suc n} zero _+_
guilhermehas marked this conversation as resolved.
Show resolved Hide resolved
+-identityʳ {ℕ.zero} zero = refl
+-identityʳ {ℕ.suc _} = toℕ-injective ∘ +ℕ-identityʳ ∘ toℕ≤pred[n]