    ------------------------------------------------------------------------
    -- The Agda standard library
    --
    -- Properties related to negation
    ------------------------------------------------------------------------

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

    module Relation.Nullary.Negation where

    open import Data.Bool.Base using (Bool; false; true; if_then_else_)
    open import Data.Product.Base as Product using (_,_; Σ; Σ-syntax; ∃; curry; uncurry)
    open import Data.Sum.Base as Sum using (_⊎_; inj₁; inj₂; [_,_])
    open import Effect.Monad using (RawMonad; mkRawMonad)
    open import Function.Base using (flip; _∘_; const; _∘′_)
    open import Level using (Level)
    open import Relation.Nullary.Decidable.Core using (Dec; yes; no; ¬¬-excluded-middle)
    open import Relation.Unary using (Universal; Pred)

    private
      variable
        a b c d p w : Level
        A B C D : Set a
        P : Pred A p
        Whatever : Set w

    ------------------------------------------------------------------------
    -- Re-export public definitions

    open import Relation.Nullary.Negation.Core public

    ------------------------------------------------------------------------
    -- Quantifier juggling

    ∃⟶¬∀¬ : ∃ P → ¬ (∀ x → ¬ P x)
    ∃⟶¬∀¬ = flip uncurry

    ∀⟶¬∃¬ : (∀ x → P x) → ¬ ∃ λ x → ¬ P x
    ∀⟶¬∃¬ ∀xPx (x , ¬Px) = ¬Px (∀xPx x)

    ¬∃⟶∀¬ : ¬ ∃ (λ x → P x) → ∀ x → ¬ P x
    ¬∃⟶∀¬ = curry

    ∀¬⟶¬∃ : (∀ x → ¬ P x) → ¬ ∃ (λ x → P x)
    ∀¬⟶¬∃ = uncurry

    ∃¬⟶¬∀ : ∃ (λ x → ¬ P x) → ¬ (∀ x → P x)
    ∃¬⟶¬∀ = flip ∀⟶¬∃¬

    ------------------------------------------------------------------------
    -- Double Negation

    -- Double-negation is a monad (if we assume that all elements of ¬ ¬ P
    -- are equal).

    ¬¬-Monad : RawMonad {a} DoubleNegation
    ¬¬-Monad = mkRawMonad
      DoubleNegation
      contradiction
      (λ x f → negated-stable (¬¬-map f x))

    ¬¬-push : DoubleNegation Π[ P ] → Π[ DoubleNegation ∘ P ]
    ¬¬-push ¬¬∀P a ¬Pa = ¬¬∀P (λ ∀P → ¬Pa (∀P a))

    -- If Whatever is instantiated with ¬ ¬ something, then this function
    -- is call with current continuation in the double-negation monad, or,
    -- if you will, a double-negation translation of Peirce's law.
    --
    -- In order to prove ¬ ¬ P one can assume ¬ P and prove ⊥. However,
    -- sometimes it is nice to avoid leaving the double-negation monad; in
    -- that case this function can be used (with Whatever instantiated to
    -- ⊥).

    call/cc : ((A → Whatever) → DoubleNegation A) → DoubleNegation A
    call/cc hyp ¬a = hyp (flip contradiction ¬a) ¬a

    -- The "independence of premise" rule, in the double-negation monad.
    -- It is assumed that the index set (A) is inhabited.

    independence-of-premise : A → (B → Σ A P) → DoubleNegation (Σ[ x ∈ A ] (B → P x))
    independence-of-premise {A = A} {B = B} {P = P} q f = ¬¬-map helper ¬¬-excluded-middle
      where
      helper : Dec B → Σ[ x ∈ A ] (B → P x)
      helper (yes p) = Product.map₂ const (f p)
      helper (no ¬p) = (q , flip contradiction ¬p)

    -- The independence of premise rule for binary sums.

    independence-of-premise-⊎ : (A → B ⊎ C) → DoubleNegation ((A → B) ⊎ (A → C))
    independence-of-premise-⊎ {A = A} {B = B} {C = C} f = ¬¬-map helper ¬¬-excluded-middle
      where
      helper : Dec A → (A → B) ⊎ (A → C)
      helper (yes p) = Sum.map const const (f p)
      helper (no ¬p) = inj₁ (flip contradiction ¬p)

    private

      -- Note that independence-of-premise-⊎ is a consequence of
      -- independence-of-premise (for simplicity it is assumed that Q and
      -- R have the same type here):

      corollary : {B C : Set b} → (A → B ⊎ C) → DoubleNegation ((A → B) ⊎ (A → C))
      corollary {A = A} {B = B} {C = C} f =
        ¬¬-map helper (independence-of-premise true ([ _,_ true , _,_ false ] ∘′ f))
        where
        helper : ∃ (λ b → A → if b then B else C) → (A → B) ⊎ (A → C)
        helper (true  , f) = inj₁ f
        helper (false , f) = inj₂ f