------------------------------------------------------------------------
-- The Agda standard library
--
-- Definition of and lemmas related to "true infinitely often"
------------------------------------------------------------------------

module Data.Nat.InfinitelyOften where

import Level
open import Algebra
open import Category.Monad
open import Data.Empty
open import Function
open import Data.Nat
import Data.Nat.Properties as NatProp
open import Data.Product as Prod hiding (map)
open import Data.Sum hiding (map)
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Relation.Nullary.Negation
open import Relation.Unary using (_∪_; _⊆_)
open RawMonad (¬¬-Monad {p = Level.zero})
private
  module NatLattice = DistributiveLattice NatProp.distributiveLattice

-- Only true finitely often.

Fin : (  Set)  Set
Fin P =  λ i   j  i  j  ¬ P j

-- Fin is preserved by binary sums.

_∪-Fin_ :  {P Q}  Fin P  Fin Q  Fin (P  Q)
_∪-Fin_ {P} {Q} (i , ¬p) (j , ¬q) = (i  j , helper)
  where
  open ≤-Reasoning

  helper :  k  i  j  k  ¬ (P  Q) k
  helper k i⊔j≤k (inj₁ p) = ¬p k (begin
    i      ≤⟨ NatProp.m≤m⊔n i j 
    i  j  ≤⟨ i⊔j≤k 
    k      ) p
  helper k i⊔j≤k (inj₂ q) = ¬q k (begin
    j      ≤⟨ NatProp.m≤m⊔n j i 
    j  i  ≡⟨ NatLattice.∧-comm j i 
    i  j  ≤⟨ i⊔j≤k 
    k      ) q

-- A non-constructive definition of "true infinitely often".

Inf : (  Set)  Set
Inf P = ¬ Fin P

-- Inf commutes with binary sums (in the double-negation monad).

commutes-with-∪ :  {P Q}  Inf (P  Q)  ¬ ¬ (Inf P  Inf Q)
commutes-with-∪ p∪q =
  call/cc λ ¬[p⊎q] 
   ¬p ¬q  ⊥-elim (p∪q (¬p ∪-Fin ¬q)))
    <$> ¬[p⊎q]  inj₁  ¬[p⊎q]  inj₂

-- Inf is functorial.

map :  {P Q}  P  Q  Inf P  Inf Q
map P⊆Q ¬fin = ¬fin  Prod.map id  fin j i≤j  fin j i≤j  P⊆Q)

-- Inf is upwards closed.

up :  {P} n  Inf P  Inf (P  _+_ n)
up     zero    = id
up {P} (suc n) = up n  up₁
  where
  up₁ : Inf P  Inf (P  suc)
  up₁ ¬fin (i , fin) = ¬fin (suc i , helper)
    where
    helper :  j  1 + i  j  ¬ P j
    helper ._ (s≤s i≤j) = fin _ i≤j

-- A witness.

witness :  {P}  Inf P  ¬ ¬  P
witness ¬fin ¬p = ¬fin (0 , λ i _ Pi  ¬p (i , Pi))

-- Two different witnesses.

twoDifferentWitnesses
  :  {P}  Inf P  ¬ ¬ ∃₂ λ m n  m  n × P m × P n
twoDifferentWitnesses inf =
  witness inf                     >>= λ w₁ 
  witness (up (1 + proj₁ w₁) inf) >>= λ w₂ 
  return (_ , _ , NatProp.m≢1+m+n (proj₁ w₁) , proj₂ w₁ , proj₂ w₂)