/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Robert Y. Lewis, Arthur Paulino
-/
import data.bool.basic
import meta.rb_map
import tactic.lint.basic

/-!
# Various linters

This file defines several small linters:
  - `ge_or_gt` checks that `>` and `≥` do not occur in the statement of theorems.
  - `dup_namespace` checks that no declaration has a duplicated namespace such as `list.list.monad`.
  - `unused_arguments` checks that definitions and theorems do not have unused arguments.
  - `doc_blame` checks that every definition has a documentation string.
  - `doc_blame_thm` checks that every theorem has a documentation string (not enabled by default).
  - `def_lemma` checks that a declaration is a lemma iff its type is a proposition.
  - `check_type` checks that the statement of a declaration is well-typed.
  - `check_univs` checks that there are no bad `max u v` universe levels.
  - `syn_taut` checks that declarations are not syntactic tautologies.
  - `unused_haves_suffices` checks that declarations produced via term mode do not have
    ineffectual `have` or `suffices` statements
-/

open tactic expr

/-!
## Linter against use of `>`/`≥`
-/
/-- The names of `≥` and `>`, mostly disallowed in lemma statements -/
private meta def illegal_ge_gt : list name := [`gt, `ge]

set_option eqn_compiler.max_steps 20000
/--
  Checks whether `≥` and `>` occurs in an illegal way in the expression.
  The main ways we legally use these orderings are:
  - `f (≥)`
  - `∃ x ≥ t, b`. This corresponds to the expression
    `@Exists α (fun (x : α), (@Exists (x > t) (λ (H : x > t), b)))`
  This function returns `tt` when it finds `ge`/`gt`, except in the following patterns
  (which are the same for `gt`):
  - `f (@ge _ _)`
  - `f (&0 ≥ y) (λ x : t, b)`
  - `λ H : &0 ≥ t, b`
  Here `&0` is the 0-th de Bruijn variable.
-/
private meta def contains_illegal_ge_gt : expr → bool
| (const nm us) := if nm ∈ illegal_ge_gt then tt else ff
| (app f e@(app (app (const nm us) tp) tc)) :=
  contains_illegal_ge_gt f || if nm ∈ illegal_ge_gt then ff else contains_illegal_ge_gt e
| (app (app custom_binder (app (app (app (app (const nm us) tp) tc) (var 0)) t))
    e@(lam var_name bi var_type body)) :=
  contains_illegal_ge_gt e || if nm ∈ illegal_ge_gt then ff else contains_illegal_ge_gt e
| (app f x) := contains_illegal_ge_gt f || contains_illegal_ge_gt x
| (lam `H bi type@(app (app (app (app (const nm us) tp) tc) (var 0)) t) body) :=
  contains_illegal_ge_gt body || if nm ∈ illegal_ge_gt then ff else contains_illegal_ge_gt type
| (lam var_name bi var_type body) := contains_illegal_ge_gt var_type || contains_illegal_ge_gt body
| (pi `H bi type@(app (app (app (app (const nm us) tp) tc) (var 0)) t) body) :=
  contains_illegal_ge_gt body || if nm ∈ illegal_ge_gt then ff else contains_illegal_ge_gt type
| (pi var_name bi var_type body) := contains_illegal_ge_gt var_type || contains_illegal_ge_gt body
| (elet var_name type assignment body) :=
  contains_illegal_ge_gt type || contains_illegal_ge_gt assignment || contains_illegal_ge_gt body
| _ := ff

/-- Checks whether a `>`/`≥` is used in the statement of `d`.

It first does a quick check to see if there is any `≥` or `>` in the statement, and then does a
slower check whether the occurrences of `≥` and `>` are allowed.
Currently it checks only the conclusion of the declaration, to eliminate false positive from
binders such as `∀ ε > 0, ...` -/
private meta def ge_or_gt_in_statement (d : declaration) : tactic (option string) :=
return $ if d.type.contains_constant (λ n, n ∈ illegal_ge_gt) &&
  contains_illegal_ge_gt d.type
  then some "the type contains ≥/>. Use ≤/< instead."
  else none

-- TODO: the commented out code also checks for classicality in statements, but needs fixing
-- TODO: this probably needs to also check whether the argument is a variable or @eq <var> _ _
-- meta def illegal_constants_in_statement (d : declaration) : tactic (option string) :=
-- return $ if d.type.contains_constant (λ n, (n.get_prefix = `classical ∧
--   n.last ∈ ["prop_decidable", "dec", "dec_rel", "dec_eq"]) ∨ n ∈ [`gt, `ge])
-- then
--   let illegal1 := [`classical.prop_decidable, `classical.dec, `classical.dec_rel,
--     `classical.dec_eq],
--       illegal2 := [`gt, `ge],
--       occur1 := illegal1.filter (λ n, d.type.contains_constant (eq n)),
--       occur2 := illegal2.filter (λ n, d.type.contains_constant (eq n)) in
--   some $ sformat!"the type contains the following declarations: {occur1 ++ occur2}." ++
--     (if occur1 = [] then "" else " Add decidability type-class arguments instead.") ++
--     (if occur2 = [] then "" else " Use ≤/< instead.")
-- else none

/-- A linter for checking whether illegal constants (≥, >) appear in a declaration's type. -/
@[linter] meta def linter.ge_or_gt : linter :=
{ test := ge_or_gt_in_statement,
  auto_decls := ff,
  no_errors_found := "Not using ≥/> in declarations.",
  errors_found := "The following declarations use ≥/>, probably in a way where we would prefer
  to use ≤/< instead. See note [nolint_ge] for more information.",
  is_fast := ff }

/--
Currently, the linter forbids the use of `>` and `≥` in definitions and
statements, as they cause problems in rewrites.
They are still allowed in statements such as `bounded (≥)` or `∀ ε > 0` or `⨆ n ≥ m`,
and the linter allows that.
If you write a pattern where you bind two or more variables, like `∃ n m > 0`, the linter will
flag this as illegal, but it is also allowed. In this case, add the line
```
@[nolint ge_or_gt] -- see Note [nolint_ge]
```
-/
library_note "nolint_ge"

/-!
## Linter for duplicate namespaces
-/

/-- Checks whether a declaration has a namespace twice consecutively in its name -/
private meta def dup_namespace (d : declaration) : tactic (option string) :=
is_instance d.to_name >>= λ is_inst,
return $ let nm := d.to_name.components in if nm.chain' (≠) ∨ is_inst then none
  else let s := (nm.find $ λ n, nm.count n ≥ 2).iget.to_string in
  some $ "The namespace `" ++ s ++ "` is duplicated in the name"

/-- A linter for checking whether a declaration has a namespace twice consecutively in its name. -/
@[linter] meta def linter.dup_namespace : linter :=
{ test := dup_namespace,
  auto_decls := ff,
  no_errors_found := "No declarations have a duplicate namespace.",
  errors_found := "DUPLICATED NAMESPACES IN NAME:" }

attribute [nolint dup_namespace] iff.iff

/-!
## Linter for unused arguments
-/

/-- Auxiliary definition for `check_unused_arguments` -/
private meta def check_unused_arguments_aux : list ℕ → ℕ → ℕ → expr → list ℕ | l n n_max e :=
if n > n_max then l else
if ¬ is_lambda e ∧ ¬ is_pi e then l else
  let b := e.binding_body in
  let l' := if b.has_var_idx 0 then l else n :: l in check_unused_arguments_aux l' (n+1) n_max b

/-- Check which arguments of a declaration are not used.
Prints a list of natural numbers corresponding to which arguments are not used (e.g.
  this outputs [1, 4] if the first and fourth arguments are unused).
Checks both the type and the value of `d` for whether the argument is used
(in rare cases an argument is used in the type but not in the value).
We return [] if the declaration was automatically generated.
We print arguments that are larger than the arity of the type of the declaration
(without unfolding definitions). -/
meta def check_unused_arguments (d : declaration) : option (list ℕ) :=
let l := check_unused_arguments_aux [] 1 d.type.pi_arity d.value in
if l = [] then none else
let l2 := check_unused_arguments_aux [] 1 d.type.pi_arity d.type in
(l.filter $ λ n, n ∈ l2).reverse

/-- Check for unused arguments, and print them with their position, variable name, type and whether
the argument is a duplicate.
See also `check_unused_arguments`.
This tactic additionally filters out all unused arguments of type `parse _`.
We skip all declarations that contain `sorry` in their value. -/
private meta def unused_arguments (d : declaration) : tactic (option string) := do
  ff ← d.to_name.contains_sorry | return none,
  let ns := check_unused_arguments d,
  tt ← return ns.is_some | return none,
  let ns := ns.iget,
  (ds, _) ← get_pi_binders d.type,
  let ns := ns.map (λ n, (n, (ds.nth $ n - 1).iget)),
  let ns := ns.filter (λ x, x.2.type.get_app_fn ≠ const `interactive.parse []),
  ff ← return ns.empty | return none,
  ds' ← ds.mmap pp,
  ns ← ns.mmap (λ ⟨n, b⟩, (λ s, to_fmt "argument " ++ to_fmt n ++ ": " ++ s ++
    (if ds.countp (λ b', b.type = b'.type) ≥ 2 then " (duplicate)" else "")) <$> pp b),
  return $ some $ ns.to_string_aux tt

/-- A linter object for checking for unused arguments. This is in the default linter set. -/
@[linter] meta def linter.unused_arguments : linter :=
{ test := unused_arguments,
  auto_decls := ff,
  no_errors_found := "No unused arguments.",
  errors_found := "UNUSED ARGUMENTS." }

attribute [nolint unused_arguments] imp_intro

/-!
## Linter for documentation strings
-/

/-- Reports definitions and constants that are missing doc strings -/
private meta def doc_blame_report_defn : declaration → tactic (option string)
| (declaration.defn n _ _ _ _ _) := doc_string n >> return none <|> return "def missing doc string"
| (declaration.cnst n _ _ _) := doc_string n >> return none <|> return "constant missing doc string"
| _ := return none

/-- Reports definitions and constants that are missing doc strings -/
private meta def doc_blame_report_thm : declaration → tactic (option string)
| (declaration.thm n _ _ _) := doc_string n >> return none <|> return "theorem missing doc string"
| _ := return none

/-- A linter for checking definition doc strings -/
@[linter] meta def linter.doc_blame : linter :=
{ test := λ d, mcond (bnot <$> has_attribute' `instance d.to_name)
    (doc_blame_report_defn d) (return none),
  auto_decls := ff,
  no_errors_found := "No definitions are missing documentation.",
  errors_found := "DEFINITIONS ARE MISSING DOCUMENTATION STRINGS:" }

/-- A linter for checking theorem doc strings. This is not in the default linter set. -/
meta def linter.doc_blame_thm : linter :=
{ test := doc_blame_report_thm,
  auto_decls := ff,
  no_errors_found := "No theorems are missing documentation.",
  errors_found := "THEOREMS ARE MISSING DOCUMENTATION STRINGS:",
  is_fast := ff }

/-!
## Linter for correct usage of `lemma`/`def`
-/

/--
Checks whether the correct declaration constructor (definition or theorem) by
comparing it to its sort. Instances will not be printed.

This test is not very quick: maybe we can speed-up testing that something is a proposition?
This takes almost all of the execution time.
-/
private meta def incorrect_def_lemma (d : declaration) : tactic (option string) :=
  if d.is_constant ∨ d.is_axiom
  then return none else do
    is_instance_d ← is_instance d.to_name,
    if is_instance_d then return none else do
      -- the following seems to be a little quicker than `is_prop d.type`.
      expr.sort n ← infer_type d.type,
      is_pattern ← has_attribute' `pattern d.to_name,
      return $
        if d.is_theorem ↔ n = level.zero then none
        else if d.is_theorem then "is a lemma/theorem, should be a def"
        else if is_pattern then none -- declarations with `@[pattern]` are allowed to be a `def`.
        else "is a def, should be a lemma/theorem"

/-- A linter for checking whether the correct declaration constructor (definition or theorem)
has been used. -/
@[linter] meta def linter.def_lemma : linter :=
{ test := incorrect_def_lemma,
  auto_decls := ff,
  no_errors_found := "All declarations correctly marked as def/lemma.",
  errors_found := "INCORRECT DEF/LEMMA:" }

/-!
## Linter that checks whether declarations are well-typed
-/

/-- Checks whether the statement of a declaration is well-typed. -/
meta def check_type (d : declaration) : tactic (option string) :=
(type_check d.type >> return none) <|> return "The statement doesn't type-check"

/-- A linter for missing checking whether statements of declarations are well-typed. -/
@[linter]
meta def linter.check_type : linter :=
{ test := check_type,
  auto_decls := ff,
  no_errors_found :=
    "The statements of all declarations type-check with default reducibility settings.",
  errors_found := "THE STATEMENTS OF THE FOLLOWING DECLARATIONS DO NOT TYPE-CHECK.
Some definitions in the statement are marked `@[irreducible]`, which means that the statement " ++
"is now ill-formed. It is likely that these definitions were locally marked as `@[reducible]` " ++
"or `@[semireducible]`. This can especially cause problems with type class inference or " ++
"`@[simps]`.",
  is_fast := tt }

/-!
## Linter for universe parameters
-/

open native
/--
  `univ_params_grouped e` computes for each `level` `u` of `e` the parameters that occur in `u`,
  and returns the corresponding set of lists of parameters.
  In pseudo-mathematical form, this returns `{ { p : parameter | p ∈ u } | (u : level) ∈ e }`
  We use `list name` instead of `name_set`, since `name_set` does not have an order.
  It will ignore `nm₀._proof_i` declarations.
-/
meta def expr.univ_params_grouped (e : expr) (nm₀ : name) : rb_set (list name) :=
e.fold mk_rb_set $ λ e n l,
  match e with
  | e@(sort u) := l.insert u.params.to_list
  | e@(const nm us) := if nm.get_prefix = nm₀ ∧ nm.last.starts_with "_proof_" then l else
      l.union $ rb_set.of_list $ us.map $ λ u : level, u.params.to_list
  | _ := l
  end

/--
  The good parameters are the parameters that occur somewhere in the `rb_set` as a singleton or
  (recursively) with only other good parameters.
  All other parameters in the `rb_set` are bad.
-/
meta def bad_params : rb_set (list name) → list name | l :=
let good_levels : name_set :=
  l.fold mk_name_set $ λ us prev, if us.length = 1 then prev.insert us.head else prev in
if good_levels.empty then
l.fold [] list.union
else bad_params $ rb_set.of_list $ l.to_list.map $ λ us, us.filter $ λ nm, !good_levels.contains nm

/--
Checks whether all universe levels `u` in the type of `d` are "good".
This means that `u` either occurs in a `level` of `d` by itself, or (recursively)
with only other good levels.
When this fails, usually this means that there is a level `max u v`, where neither `u` nor `v`
occur by themselves in a level. It is ok if *one* of `u` or `v` never occurs alone. For example,
`(α : Type u) (β : Type (max u v))` is a occasionally useful method of saying that `β` lives in
a higher universe level than `α`.
-/
meta def check_univs (d : declaration) : tactic (option string) := do
  let l := d.type.univ_params_grouped d.to_name,
  let bad := bad_params l,
  if bad.empty then return none else
    return $ some $ "universes " ++ to_string bad ++ " only occur together."

/-- A linter for checking that there are no bad `max u v` universe levels. -/
@[linter]
meta def linter.check_univs : linter :=
{ test := check_univs,
  auto_decls := ff,
  no_errors_found :=
    "All declarations have good universe levels.",
  errors_found := "THE STATEMENTS OF THE FOLLOWING DECLARATIONS HAVE BAD UNIVERSE LEVELS. " ++
"This usually means that there is a `max u v` in the type where neither `u` nor `v` " ++
"occur by themselves. Solution: Find the type (or type bundled with data) that has this " ++
"universe argument and provide the universe level explicitly. If this happens in an implicit " ++
"argument of the declaration, a better solution is to move this argument to a `variables` " ++
"command (then it's not necessary to provide the universe level).
It is possible that this linter gives a false positive on definitions where the value of the " ++
"definition has the universes occur separately, and the definition will usually be used with " ++
"explicit universe arguments. In this case, feel free to add `@[nolint check_univs]`.",
  is_fast := tt }

/-!
## Linter for syntactic tautologies
-/

/--
Checks whether a lemma is a declaration of the form `∀ a b ... z, e₁ = e₂`
where `e₁` and `e₂` are identical exprs.
We call declarations of this form syntactic tautologies.
Such lemmas are (mostly) useless and sometimes introduced unintentionally when proving basic facts
with rfl when elaboration results in a different term than the user intended.
-/
meta def syn_taut (d : declaration) : tactic (option string) :=
  (do (el, er) ← d.type.pi_codomain.is_eq,
    guardb (el =ₐ er),
    return $ some "LHS equals RHS syntactically") <|>
  return none

/-- A linter for checking that declarations aren't syntactic tautologies. -/
@[linter]
meta def linter.syn_taut : linter :=
{ test := syn_taut,
  auto_decls := ff, -- many false positives with this enabled
  no_errors_found :=
    "No declarations are syntactic tautologies.",
  errors_found := "THE FOLLOWING DECLARATIONS ARE SYNTACTIC TAUTOLOGIES. " ++
"This usually means that they are of the form `∀ a b ... z, e₁ = e₂` where `e₁` and `e₂` are " ++
"identical expressions. We call declarations of this form syntactic tautologies. " ++
"Such lemmas are (mostly) useless and sometimes introduced unintentionally when proving " ++
"basic facts using `rfl`, when elaboration results in a different term than the user intended. " ++
"You should check that the declaration really says what you think it does.",
  is_fast := tt }

attribute [nolint syn_taut] rfl

/-!
## Linters for ineffectual have and suffices statements in term mode
-/

/--
Check if an expression contains `var 0` by folding over the expression and matching the binder depth
-/
meta def expr.has_zero_var (e : expr) : bool :=
e.fold ff $ λ e' d res, res || match e' with | var k := k = d | _ := ff end

/--
Return a list of unused have and suffices terms in an expression
-/
meta def find_unused_have_suffices_macros : expr → tactic (list string)
| (app a b) := (++) <$> find_unused_have_suffices_macros a <*> find_unused_have_suffices_macros b
| (lam var_name bi var_type body) := find_unused_have_suffices_macros body
| (pi var_name bi var_type body) := find_unused_have_suffices_macros body
| (elet var_name type assignment body) := (++) <$> find_unused_have_suffices_macros assignment
                                               <*> find_unused_have_suffices_macros body
| m@(macro md [l@(lam ppnm bi vt bd)]) := do -- term mode have statements are tagged with a macro
  -- if the macro annotation is `have then this lambda came from a term mode have statement
  (++) (if m.is_annotation.iget.fst = `have ∧ ¬bd.has_zero_var then
      ["unnecessary have " ++ ppnm.to_string ++ " : " ++ vt.to_string]
    else []) <$>
  find_unused_have_suffices_macros l
| m@(macro md [app l@(lam ppnm bi vt bd) arg]) := do
  -- term mode suffices statements are tagged with a macro
  -- if the macro annotation is `suffices then this lambda came from a term mode suffices statement
  (++) (if m.is_annotation.iget.fst = `suffices ∧ ¬bd.has_zero_var then
      ["unnecessary suffices " ++ ppnm.to_string ++ " : " ++ vt.to_string]
    else []) <$>
  ((++) <$> find_unused_have_suffices_macros l <*> find_unused_have_suffices_macros arg)
| (macro md l) := list.join <$> l.mmap find_unused_have_suffices_macros
| _ := return []

/--
Return a list of unused have and suffices terms in a declaration
-/
meta def unused_have_of_decl : declaration → tactic (list string)
| (declaration.defn _ _ _ bd _ _) := find_unused_have_suffices_macros bd
| (declaration.thm _ _ _ bd) := find_unused_have_suffices_macros bd.get
| _ := return []

/--
Checks whether a declaration contains term mode have statements that have no effect on the resulting
term.
-/
meta def has_unused_haves_suffices (d : declaration) : tactic (option string) := do
  ns ← unused_have_of_decl d,
  if ns.length = 0 then
    return none
  else
    return (", ".intercalate (ns.map to_string))

/-- A linter for checking that declarations don't have unused term mode have statements. We do not
tag this as `@[linter]` so that it is not in the default linter set as it is slow and an uncommon
problem. -/
meta def linter.unused_haves_suffices : linter :=
{ test := has_unused_haves_suffices,
  auto_decls := ff,
  no_errors_found := "No declarations have unused term mode have statements.",
  errors_found := "THE FOLLOWING DECLARATIONS HAVE INEFFECTUAL TERM MODE HAVE/SUFFICES BLOCKS. " ++
"In the case of `have` this is a term of the form `have h := foo, bar` where `bar` does not " ++
"refer to `foo`. Such statements have no effect on the generated proof, and can just be " ++
"replaced by `bar`, in addition to being ineffectual, they may make unnecessary assumptions " ++
"in proofs appear as if they are used. " ++
"For `suffices` this is a term of the form `suffices h : foo, proof_of_goal, proof_of_foo` where" ++
" `proof_of_goal` does not refer to `foo`. " ++
"Such statements have no effect on the generated proof, and can just be replaced by " ++
"`proof_of_goal`, in addition to being ineffectual, they may make unnecessary assumptions in " ++
"proofs appear as if they are used. ",
  is_fast := ff }

/-!
## Linter for unprintable interactive tactics
-/

/--
Ensures that every interactive tactic has arguments for which `interactive.param_desc` succeeds.
This is used to generate the parser documentation that appears in hovers on interactive tactics.
-/
meta def unprintable_interactive (d : declaration) : tactic (option string) :=
match d.to_name with
| name.mk_string _ (name.mk_string "interactive" (name.mk_string _ name.anonymous)) := do
  (ds, _) ← mk_local_pis d.type,
  ds ← ds.mfilter $ λ d, bnot <$> succeeds (interactive.param_desc d.local_type),
  ff ← return ds.empty | return none,
  ds ← ds.mmap (pp ∘ to_binder),
  return $ some $ ds.to_string_aux tt
| _ := return none
end

/-- A linter for checking that interactive tactics have parser documentation. -/
@[linter]
meta def linter.unprintable_interactive : linter :=
{ test := unprintable_interactive,
  auto_decls := tt,
  no_errors_found := "No tactics are unprintable.",
  errors_found := "THE FOLLOWING TACTICS ARE UNPRINTABLE. " ++
"This means that an interactive tactic is using `parse p` where `p` does not have " ++
"an associated description. You can fix this by wrapping `p` as `with_desc \"p\" p`, " ++
"and provide the description there, or you can stick to \"approved\" tactic combinators " ++
"like `?` `*>` `<*` `<*>` `<|>` and `<$>` (but not `>>=` or `do` blocks) " ++
"that automatically generate a description.",
  is_fast := tt }

/-!
## Linter for iff's
-/

open binder_info

/--
Recursively consumes a Pi expression while accumulating names and the complement of de-Bruijn
indexes of explicit variables, ultimately obtaining the remaining non-Pi expression as well.
-/
meta def unravel_explicits_of_pi :
  expr → ℕ → list name → list ℕ → (list name) × (list ℕ) × expr
| (pi n default _ e) i ln li := unravel_explicits_of_pi e (i + 1) (n :: ln) (i :: li)
| (pi n _ _ e)       i ln li := unravel_explicits_of_pi e (i + 1) ln        li
| e                  _ ln li := (ln, li, e)

/--
This function works as follows:
1. Call `unravel_explicits_of_pi` to obtain the names, complements of de-Bruijn indexes and the
remaining non-Pi expression;
2. Check if the remaining non-Pi expression is an iff, already obtaining the respective left and
right expressions if this is the case. Returns `none` otherwise;
3. Filter the explicit variables that appear on the left *and* right side of the iff;
4. If no variable satisfies the condition above, return `none`;
5. Return a message mentioning the variables that do, otherwise.
-/
meta def explicit_vars_of_iff (d : declaration) :
    tactic (option string) := do
  let (ln, li, e) := unravel_explicits_of_pi d.type 0 [] [],
  match e.is_iff with
  | none          := return none
  | some (el, er) := do
    let li := li.map (λ i, d.type.pi_arity - i - 1), -- fixing for the actual de-Bruijn indexes
    let l := (ln.zip li).filter (λ t, (el.has_var_idx t.2) && (er.has_var_idx t.2)),
    if l = [] then return none
    else return $ "The following variables are used on both sides of an iff and ".append $
      "should be made implicit: ".append $ ", ".intercalate (l.map (λ t, to_string t.1))
  end

/--
A linter for checking if variables appearing on both sides of an iff are explicit. Ideally, such
variables should be implicit instead.
-/
meta def linter.explicit_vars_of_iff : linter :=
{ test := explicit_vars_of_iff,
  auto_decls := ff,
  no_errors_found := "No explicit variables on both sides of iff",
  errors_found := "EXPLICIT VARIABLES ON BOTH SIDES OF IFF" }


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Johannes Hölzl, Scott Morrison, Jens Wagemaker
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.polynomial.ring_division
import Mathlib.data.polynomial.derivative
import Mathlib.algebra.gcd_monoid
import Mathlib.PostPort

universes u y v u_1

namespace Mathlib

/-!
# Theory of univariate polynomials

This file starts looking like the ring theory of $ R[X] $

-/

namespace polynomial

protected instance normalization_monoid {R : Type u} [integral_domain R] [normalization_monoid R] :
    normalization_monoid (polynomial R) :=
  normalization_monoid.mk
    (fun (p : polynomial R) =>
      units.mk (coe_fn C ↑(norm_unit (leading_coeff p))) (coe_fn C ↑(norm_unit (leading_coeff p)⁻¹))
        sorry sorry)
    sorry sorry sorry

@[simp] theorem coe_norm_unit {R : Type u} [integral_domain R] [normalization_monoid R]
    {p : polynomial R} : ↑(norm_unit p) = coe_fn C ↑(norm_unit (leading_coeff p)) :=
  sorry

theorem leading_coeff_normalize {R : Type u} [integral_domain R] [normalization_monoid R]
    (p : polynomial R) : leading_coeff (coe_fn normalize p) = coe_fn normalize (leading_coeff p) :=
  sorry

theorem is_unit_iff_degree_eq_zero {R : Type u} [field R] {p : polynomial R} :
    is_unit p ↔ degree p = 0 :=
  sorry

theorem degree_pos_of_ne_zero_of_nonunit {R : Type u} [field R] {p : polynomial R} (hp0 : p ≠ 0)
    (hp : ¬is_unit p) : 0 < degree p :=
  sorry

theorem monic_mul_leading_coeff_inv {R : Type u} [field R] {p : polynomial R} (h : p ≠ 0) :
    monic (p * coe_fn C (leading_coeff p⁻¹)) :=
  sorry

theorem degree_mul_leading_coeff_inv {R : Type u} [field R] {q : polynomial R} (p : polynomial R)
    (h : q ≠ 0) : degree (p * coe_fn C (leading_coeff q⁻¹)) = degree p :=
  sorry

theorem irreducible_of_monic {R : Type u} [field R] {p : polynomial R} (hp1 : monic p)
    (hp2 : p ≠ 1) :
    irreducible p ↔ ∀ (f g : polynomial R), monic f → monic g → f * g = p → f = 1 ∨ g = 1 :=
  sorry

/-- Division of polynomials. See polynomial.div_by_monic for more details.-/
def div {R : Type u} [field R] (p : polynomial R) (q : polynomial R) : polynomial R :=
  coe_fn C (leading_coeff q⁻¹) * (p /ₘ (q * coe_fn C (leading_coeff q⁻¹)))

/-- Remainder of polynomial division, see the lemma `quotient_mul_add_remainder_eq_aux`.
See polynomial.mod_by_monic for more details. -/
def mod {R : Type u} [field R] (p : polynomial R) (q : polynomial R) : polynomial R :=
  p %ₘ (q * coe_fn C (leading_coeff q⁻¹))

protected instance has_div {R : Type u} [field R] : Div (polynomial R) := { div := div }

protected instance has_mod {R : Type u} [field R] : Mod (polynomial R) := { mod := mod }

theorem div_def {R : Type u} [field R] {p : polynomial R} {q : polynomial R} :
    p / q = coe_fn C (leading_coeff q⁻¹) * (p /ₘ (q * coe_fn C (leading_coeff q⁻¹))) :=
  rfl

theorem mod_def {R : Type u} [field R] {p : polynomial R} {q : polynomial R} :
    p % q = p %ₘ (q * coe_fn C (leading_coeff q⁻¹)) :=
  rfl

theorem mod_by_monic_eq_mod {R : Type u} [field R] {q : polynomial R} (p : polynomial R)
    (hq : monic q) : p %ₘ q = p % q :=
  sorry

theorem div_by_monic_eq_div {R : Type u} [field R] {q : polynomial R} (p : polynomial R)
    (hq : monic q) : p /ₘ q = p / q :=
  sorry

theorem mod_X_sub_C_eq_C_eval {R : Type u} [field R] (p : polynomial R) (a : R) :
    p % (X - coe_fn C a) = coe_fn C (eval a p) :=
  mod_by_monic_eq_mod p (monic_X_sub_C a) ▸ mod_by_monic_X_sub_C_eq_C_eval p a

theorem mul_div_eq_iff_is_root {R : Type u} {a : R} [field R] {p : polynomial R} :
    (X - coe_fn C a) * (p / (X - coe_fn C a)) = p ↔ is_root p a :=
  div_by_monic_eq_div p (monic_X_sub_C a) ▸ mul_div_by_monic_eq_iff_is_root

protected instance euclidean_domain {R : Type u} [field R] : euclidean_domain (polynomial R) :=
  euclidean_domain.mk comm_ring.add sorry comm_ring.zero sorry sorry comm_ring.neg comm_ring.sub
    sorry sorry comm_ring.mul sorry comm_ring.one sorry sorry sorry sorry sorry sorry Div.div sorry
    Mod.mod quotient_mul_add_remainder_eq_aux (fun (p q : polynomial R) => degree p < degree q)
    sorry sorry sorry

theorem mod_eq_self_iff {R : Type u} [field R] {p : polynomial R} {q : polynomial R} (hq0 : q ≠ 0) :
    p % q = p ↔ degree p < degree q :=
  sorry

theorem div_eq_zero_iff {R : Type u} [field R] {p : polynomial R} {q : polynomial R} (hq0 : q ≠ 0) :
    p / q = 0 ↔ degree p < degree q :=
  sorry

theorem degree_add_div {R : Type u} [field R] {p : polynomial R} {q : polynomial R} (hq0 : q ≠ 0)
    (hpq : degree q ≤ degree p) : degree q + degree (p / q) = degree p :=
  sorry

theorem degree_div_le {R : Type u} [field R] (p : polynomial R) (q : polynomial R) :
    degree (p / q) ≤ degree p :=
  sorry

theorem degree_div_lt {R : Type u} [field R] {p : polynomial R} {q : polynomial R} (hp : p ≠ 0)
    (hq : 0 < degree q) : degree (p / q) < degree p :=
  sorry

@[simp] theorem degree_map {R : Type u} {k : Type y} [field R] [field k] (p : polynomial R)
    (f : R →+* k) : degree (map f p) = degree p :=
  degree_map_eq_of_injective (ring_hom.injective f) p

@[simp] theorem nat_degree_map {R : Type u} {k : Type y} [field R] {p : polynomial R} [field k]
    (f : R →+* k) : nat_degree (map f p) = nat_degree p :=
  nat_degree_eq_of_degree_eq (degree_map p f)

@[simp] theorem leading_coeff_map {R : Type u} {k : Type y} [field R] {p : polynomial R} [field k]
    (f : R →+* k) : leading_coeff (map f p) = coe_fn f (leading_coeff p) :=
  sorry

theorem monic_map_iff {R : Type u} {k : Type y} [field R] [field k] {f : R →+* k}
    {p : polynomial R} : monic (map f p) ↔ monic p :=
  sorry

theorem is_unit_map {R : Type u} {k : Type y} [field R] {p : polynomial R} [field k] (f : R →+* k) :
    is_unit (map f p) ↔ is_unit p :=
  sorry

theorem map_div {R : Type u} {k : Type y} [field R] {p : polynomial R} {q : polynomial R} [field k]
    (f : R →+* k) : map f (p / q) = map f p / map f q :=
  sorry

theorem map_mod {R : Type u} {k : Type y} [field R] {p : polynomial R} {q : polynomial R} [field k]
    (f : R →+* k) : map f (p % q) = map f p % map f q :=
  sorry

theorem gcd_map {R : Type u} {k : Type y} [field R] {p : polynomial R} {q : polynomial R} [field k]
    (f : R →+* k) : euclidean_domain.gcd (map f p) (map f q) = map f (euclidean_domain.gcd p q) :=
  sorry

theorem eval₂_gcd_eq_zero {R : Type u} {k : Type y} [field R] [comm_semiring k] {ϕ : R →+* k}
    {f : polynomial R} {g : polynomial R} {α : k} (hf : eval₂ ϕ α f = 0) (hg : eval₂ ϕ α g = 0) :
    eval₂ ϕ α (euclidean_domain.gcd f g) = 0 :=
  sorry

theorem eval_gcd_eq_zero {R : Type u} [field R] {f : polynomial R} {g : polynomial R} {α : R}
    (hf : eval α f = 0) (hg : eval α g = 0) : eval α (euclidean_domain.gcd f g) = 0 :=
  eval₂_gcd_eq_zero hf hg

theorem root_left_of_root_gcd {R : Type u} {k : Type y} [field R] [comm_semiring k] {ϕ : R →+* k}
    {f : polynomial R} {g : polynomial R} {α : k} (hα : eval₂ ϕ α (euclidean_domain.gcd f g) = 0) :
    eval₂ ϕ α f = 0 :=
  sorry

theorem root_right_of_root_gcd {R : Type u} {k : Type y} [field R] [comm_semiring k] {ϕ : R →+* k}
    {f : polynomial R} {g : polynomial R} {α : k} (hα : eval₂ ϕ α (euclidean_domain.gcd f g) = 0) :
    eval₂ ϕ α g = 0 :=
  sorry

theorem root_gcd_iff_root_left_right {R : Type u} {k : Type y} [field R] [comm_semiring k]
    {ϕ : R →+* k} {f : polynomial R} {g : polynomial R} {α : k} :
    eval₂ ϕ α (euclidean_domain.gcd f g) = 0 ↔ eval₂ ϕ α f = 0 ∧ eval₂ ϕ α g = 0 :=
  sorry

theorem is_root_gcd_iff_is_root_left_right {R : Type u} [field R] {f : polynomial R}
    {g : polynomial R} {α : R} : is_root (euclidean_domain.gcd f g) α ↔ is_root f α ∧ is_root g α :=
  root_gcd_iff_root_left_right

theorem is_coprime_map {R : Type u} {k : Type y} [field R] {p : polynomial R} {q : polynomial R}
    [field k] (f : R →+* k) : is_coprime (map f p) (map f q) ↔ is_coprime p q :=
  sorry

@[simp] theorem map_eq_zero {R : Type u} {S : Type v} [field R] {p : polynomial R} [semiring S]
    [nontrivial S] (f : R →+* S) : map f p = 0 ↔ p = 0 :=
  sorry

theorem map_ne_zero {R : Type u} {S : Type v} [field R] {p : polynomial R} [semiring S]
    [nontrivial S] {f : R →+* S} (hp : p ≠ 0) : map f p ≠ 0 :=
  mt (iff.mp (map_eq_zero f)) hp

theorem mem_roots_map {R : Type u} {k : Type y} [field R] {p : polynomial R} [field k] {f : R →+* k}
    {x : k} (hp : p ≠ 0) : x ∈ roots (map f p) ↔ eval₂ f x p = 0 :=
  sorry

theorem exists_root_of_degree_eq_one {R : Type u} [field R] {p : polynomial R} (h : degree p = 1) :
    ∃ (x : R), is_root p x :=
  sorry

theorem coeff_inv_units {R : Type u} [field R] (u : units (polynomial R)) (n : ℕ) :
    coeff (↑u) n⁻¹ = coeff (↑(u⁻¹)) n :=
  sorry

theorem monic_normalize {R : Type u} [field R] {p : polynomial R} (hp0 : p ≠ 0) :
    monic (coe_fn normalize p) :=
  sorry

theorem coe_norm_unit_of_ne_zero {R : Type u} [field R] {p : polynomial R} (hp : p ≠ 0) :
    ↑(norm_unit p) = coe_fn C (leading_coeff p⁻¹) :=
  sorry

theorem normalize_monic {R : Type u} [field R] {p : polynomial R} (h : monic p) :
    coe_fn normalize p = p :=
  sorry

theorem map_dvd_map' {R : Type u} {k : Type y} [field R] [field k] (f : R →+* k) {x : polynomial R}
    {y : polynomial R} : map f x ∣ map f y ↔ x ∣ y :=
  sorry

theorem degree_normalize {R : Type u} [field R] {p : polynomial R} :
    degree (coe_fn normalize p) = degree p :=
  sorry

theorem prime_of_degree_eq_one {R : Type u} [field R] {p : polynomial R} (hp1 : degree p = 1) :
    prime p :=
  sorry

theorem irreducible_of_degree_eq_one {R : Type u} [field R] {p : polynomial R}
    (hp1 : degree p = 1) : irreducible p :=
  irreducible_of_prime (prime_of_degree_eq_one hp1)

theorem not_irreducible_C {R : Type u} [field R] (x : R) : ¬irreducible (coe_fn C x) := sorry

theorem degree_pos_of_irreducible {R : Type u} [field R] {p : polynomial R} (hp : irreducible p) :
    0 < degree p :=
  lt_of_not_ge
    fun (hp0 : 0 ≥ degree p) =>
      (fun (this : p = coe_fn C (coeff p 0)) => not_irreducible_C (coeff p 0) (this ▸ hp))
        (eq_C_of_degree_le_zero hp0)

theorem pairwise_coprime_X_sub {α : Type u} [field α] {I : Type v} {s : I → α}
    (H : function.injective s) : pairwise (is_coprime on fun (i : I) => X - coe_fn C (s i)) :=
  sorry

/-- If `f` is a polynomial over a field, and `a : K` satisfies `f' a ≠ 0`,
then `f / (X - a)` is coprime with `X - a`.
Note that we do not assume `f a = 0`, because `f / (X - a) = (f - f a) / (X - a)`. -/
theorem is_coprime_of_is_root_of_eval_derivative_ne_zero {K : Type u_1} [field K] (f : polynomial K)
    (a : K) (hf' : eval a (coe_fn derivative f) ≠ 0) :
    is_coprime (X - coe_fn C a) (f /ₘ (X - coe_fn C a)) :=
  sorry

theorem prod_multiset_root_eq_finset_root {R : Type u} [field R] {p : polynomial R}
    (hzero : p ≠ 0) :
    multiset.prod (multiset.map (fun (a : R) => X - coe_fn C a) (roots p)) =
        finset.prod (multiset.to_finset (roots p))
          fun (a : R) => (fun (a : R) => (X - coe_fn C a) ^ root_multiplicity a p) a :=
  sorry

/-- The product `∏ (X - a)` for `a` inside the multiset `p.roots` divides `p`. -/
theorem prod_multiset_X_sub_C_dvd {R : Type u} [field R] (p : polynomial R) :
    multiset.prod (multiset.map (fun (a : R) => X - coe_fn C a) (roots p)) ∣ p :=
  sorry

theorem roots_C_mul {R : Type u} [field R] (p : polynomial R) {a : R} (hzero : a ≠ 0) :
    roots (coe_fn C a * p) = roots p :=
  sorry

theorem roots_normalize {R : Type u} [field R] {p : polynomial R} :
    roots (coe_fn normalize p) = roots p :=
  sorry

end Mathlib


import complexity_class.lemmas
import stack_rec

variables (C : complexity_class) {α β γ : Type} [tencodable α] [tencodable β] [tencodable γ]
  {base : γ → α → β} {pre₁ pre₂ : γ → tree unit → tree unit → α → α}
  {post : γ → β → β → tree unit → tree unit → α → β}

open_locale complexity_class

lemma complexity_class.stack_iterate {start : γ → list (tree.iterator_stack α β)}
  (hb : base ∈ₑ C) (hp₁ : pre₁ ∈ₑ C) (hp₂ : pre₂ ∈ₑ C)
  (hp : post ∈ₑ C) (hs : start ∈ₑ C) :
   C.mem (λ x : γ, tree.stack_step (base x) (pre₁ x) (pre₂ x) (post x) (start x)) :=
by { delta tree.stack_step, clean_target, complexity, }


import states observables
open set topological_space classical
local attribute [instance] prop_decidable
noncomputable theory

-- TODO: rename this file to essence.lean (maybe)
-- THIS FILE IS A WORK IN PROGRESS.

namespace ontology

variable {ω : ontology}

section relations

  variable (ω)

  -- abbreviation property (α := Prop) [topological_space α] [has_none α] := ω.observable α → ω.observable α
  abbreviation property (α : Type := Prop) [topological_space α] := ω.entity → ω.world → α

  def property.positive  {ω : ontology}{α : Type} [topological_space α] (p : ω.property α) : Prop := ∀ e, continuous (p e)

  abbreviation relation (α := Prop) [topological_space α] [has_none α] :=
    ω.observable α → ω.observable α → ω.observable α

  def {u} nary (α β : Type u) : ℕ → Type u
  | 0 := β
  | (n+1) := α → (nary n)

  def {u} tnary (β : Type u) : (list $ Type u) → Type u
  | [] := β
  | (hd::tl) := hd → (tnary tl)

  abbreviation nrelation (n : ℕ) (α := Prop) [topological_space α] [has_none α] :=
    nary (ω.observable α) (ω.observable α) n

  example : (ω.nrelation 2) = ω.relation := rfl

end relations

-- Next we must talk about predicates
section predicates

/-- A predicate is a function which outputs an event for an entity,
    i.e. the event of the entity having the predicate. -/
abbreviation predicate (ω : ontology) :=  ω.entity → ω.event

/-- An analogical predicate, or apredicate, outputs an aevent instead of an event.
    i.e the aevent of the entity having the predicate, which it can posses to a greater or lesser extent.
    This is analogy in the sense of **intrinsic attribution**.
    See thomistic manuals for a deeper informal discussion of intrinsic attribution.
   -/
abbreviation apredicate (ω : ontology) := ω.entity → ω.aevent

variable (p : ω.predicate)

-- Apparently Lean already infers correctly the proper notation for boolean algebra operations
-- (which is amazing), even though it does not elaborate the full boolean algebra instance.
-- we may consider defining this instance properly in the future, but for now, we just use the notation.
-- instance boolean_predicate : boolean_algebra ω.predicate := sorry

/-! Now, a predicate which, unlike an apredicate, does not return an aevent is in some sense univocal,
 but this univocity might occur in two different ways: in language alone, or both in language and in reality.
 The latter we call univocal predicates properly speaking, while the former we call *hidden* or *abstracted*
 analogical predicates. The difference between these two is that real univocal predicates preserve hierarchical
 distinctions in being, while abstracted, "fake", univocal predicates ignore such ontological distinctions.
 So essentially we say that a predicate is univocal in a possible world if and only if the entities which exemplify
 it in that world are incomparable with respect to existential entailment.
-/

-- TODO: change this definition to use existential entailment notation when it becomes available.
def predicate.lunivocal : ω.event :=
    { w |
      ∀ e₁ e₂, w ∈ p e₁ ∩ p e₂ → ¬ e₁.exists ⊂ e₂.exists ∧ ¬ e₁.exists ⊃ e₂.exists
    }
@[reducible]
def predicate.lhidden := -p.lunivocal
@[reducible]
def predicate.univocal := p.lunivocal.necessary
@[reducible]
def predicate.hidden := p.lhidden.necessary

/- The whole point of the Thomistic theory of the analogy of being consists
   in the realization that existence is a hidden analogy abstracted away from a deeper
   apredicate: Being.
-/
def existence (ω : ontology) : ω.predicate := entity.exists

-- In any possible world with at least 2 entities (hence at least one contingent entity)
-- existence is a hidden analogy.
theorem analogical_existence : ∀ w e₁ e₂, e₁ ≠ e₂ → e₁ ∈ w → e₂ ∈ w → w ∈ ω.existence.lhidden := sorry

instance predicate_inhabited : inhabited ω.predicate := ⟨ω.existence⟩

/-- a predicate `p` is **exemplifiable** if there is some `entity` which can possibly be `p`. -/
@[reducible]
def predicate.exemplifiable : Prop := ∃ e, ⋄(p e)

-- an entity is said to possibly exemplify a predicate if it does so in some possible world
@[reducible]
def entity.pexemplifies (e : ω.entity) := ⋄(p e)

-- a predicate is existential if an entity having the predicate implies its existence
@[reducible]
def predicate.existential := ∀ e, p e ⊆ e.exists

-- the common (sensical) predicates
structure predicate.common : Prop :=
    (axiom₀ : p.exemplifiable)
    (axiom₁ : p.existential)

-- A common predicate is positive if in any possible world in which an entity has the predicate,
-- there exists something in that world (e.g. an accident) to ground the predicate.
structure predicate.positive extends predicate.common p : Prop :=
    (axiom₂ : ∀ e, is_open (p e))

/-- A predicate is said to be ***de re* necessary** of an entity if the entity has that predicate in
    all and only the possible worlds in which it exists.
    Which is also to say that the entity is a *fixed point* of the predicate. -/
@[reducible]
def predicate.dere_of (e : ω.entity) := p e = e.exists
/-- Negation of `predicate.dere_of` -/
@[reducible]
def predicate.ndere_of (e : ω.entity) := p e ≠ e.exists

/-- A predicate is ***de re* necessary** in itself if it is *de re* necessary of all entities which may possibly
    exemplify it. -/
@[reducible]
def predicate.dere := ∀ e, ⋄(p e) → p.dere_of e
/-- A predicate is `adere` or **anti-*de re* necessary** if it fails to be *de re* necessary everywhere. -/
@[reducible]
def predicate.adere := ∀ e, ⋄(p e) → p.ndere_of e

/-- A positive predicate is possessed if it "talks about" its subject,
    which is to say that it is an accident or essential of a substance,
    or essential of an accident. -/
structure predicate.possessed extends predicate.positive p : Prop  :=
    (axiom₃ : ∀ (e : ω.entity) (h : e.pexemplifies p),
              let r := (entity.mk (p e) (axiom₂ e) h) in
              e.perfect → r.subsists e
    )
    (axiom₄ : ∀ (e : ω.entity), e.pexemplifies p → e.imperfect → p.dere_of e)

/-- The *significatum* or *res significata* is the entity signified by a possessed predicate `p`,
    i.e. the entity such that its existence causes the truth of the predication
    for some entity `e`. -/
def predicate.possessed.sign {p : ω.predicate} (h : p.possessed)
                                     {e : ω.entity} (ne : e.pexemplifies p)
                                     : ω.entity := ⟨p e, h.axiom₂ e, ne⟩

/-  Intensionally, the previous definition is intended to be open to the idea
    that the *significatum* of a predicate with respect to an entity
    might be an intensional entity, and which entity it is might
    even vary across possible worlds. Nevertheless, any two intensional
    *siginificata* of a *de re* necessary predicate must be existentially
    equivalent, so for this case the definition returns the corresponding extensional
    entity. Introducing intensional *significata* for extensional predicates
    would require an additional primitive beyond the introduction of an intensional
    ontology, but it might be useful for e.g. defending an **intensional** distinction between
    essence and existence/being (*esse*), if one is of the interpretation that the Thomistic distinction
    is an intensional one. We however are not of this latter position, so we prefer
    rather to introduce an **extensional** distinction between essence and existence,
    that we present further below. -/

-- TODO: consider changing the name of these definitions in the future to potentially
-- avoid the same Kit Finean "essence is not de re necessity" objection we wished to avoid
-- when renaming "essential" to "dere".

-- A possessed predicate is accidental of an entity if it is not essential.
-- From this we can infer (informally) that a predicate is either essential or accidental
-- of an entity or does not "talk about" an entity at all
-- (or at least does not "talk about" all entities to which it is predicated).
@[reducible]
def predicate.accidental_of (e : ω.entity) := p.possessed ∧ p.ndere_of e
@[reducible]
def predicate.accidental := p.possessed ∧ p.adere

-- Notice there can be possessed predicates which are neither essential nor accidental in themselves,
-- but only with respect to a particular entity (it is in this sense that some say heat is
-- essential of fire but accidental of burning coal). But with respect to any particular entity
-- a possessed predicate is either essential or accidental. Notice also that this sort of predicate
-- is (almost) never univocal, because predicating something of both a substance and its accidents is never univocal.

-- a "proper" predicate, or a predicate in a more "proper" sense of the word,
-- is an univocal possessed predicate
structure predicate.proper extends predicate.possessed p : Prop :=
    (axiom₅ : p.univocal)

variables (e : ω.entity) (ev : ω.event)

def predicate.bindp  : ω.predicate :=
  assume e₂, if e = e₂ then ev else p e₂

def predicate.localize : ω.predicate := (⊥ : ω.predicate).bindp e (p e)

variables (e) (p) (ev)

@[reducible, simp]
def entity.bindp : ω.predicate := p.bindp e ev

@[reducible, simp]
def entity.localize (p : ω.predicate) : ω.predicate := p.localize e

/-- The existence of an entity is the "existence" predicate localized to that entity.
    It is an essential proper predicate from which all of its essential predicates
    follow, or as Aquinas puts it, existence (esse) is the greatest
    perfection of a thing, as it actualizes its very essence. -/
def entity.existence : ω.predicate := ω.existence.localize e

-- Note: "esse" used above is often translated as "existence" but this translation
-- has been disputed (see works of Cornelio Fabro CSS for an idea). More properly,
-- we consider "esse" to refer to "being" in Thomistic philosophy, which we shall
-- define further down.

theorem existence_dere : e.existence.dere := sorry
theorem existence_proper : e.existence.proper := sorry
theorem existence_follows_dere : p.dere_of e → e.existence ≤ p  := sorry

/-- The haecceity of an entity is its incommunicable individual essence.
    It differs from the existence of an entity in that while the existence
    is *de re* necessary, the haecceity is absolutely necessary, or alternatively,
    haecceity predication does not have existential import, while
    existential predication (obviously) does.
    However, for the necessary being existence and haecceity coincide. -/
def entity.haecceity : ω.predicate := λ e₂, {w | e = e₂}

theorem existence_haecceity_distinction : e.existence = e.haecceity ↔ e.necessary :=
  begin
    simp [ entity.existence
         , entity.haecceity
         , predicate.localize
         , predicate.bindp
         , existence
         , function.funext_iff
         , -entity_ext_iff
         ],
    constructor; intro h,
      ext w,
      simp [nbe, univ, has_mem.mem, set.mem],
      specialize h e w,
      replace h := h.2,
      simp at h,
      exact h true.intro,
    intros e₂ w,
    by_cases c : e = e₂; simp [c],
      rw h at c,
      simp [nbe, ext_iff] at c,
      specialize c w,
      exact ⟨λ_,true.intro, λ_,c⟩,
    refine ⟨_, false.elim⟩,
    intro hyp,
    simp [ has_bot.bot
         , order_bot.bot
         , bounded_lattice.bot
         , complete_lattice.bot
         ] at hyp,
    exact hyp,
  end

-- a predicate is communicable if it can be possibly exemplified by more than one entity
def predicate.communicable := ∃ e₁ e₂ : ω.entity,
                             e₁ ≠ e₂ ∧
                             e₁.pexemplifies p ∧
                             e₂.pexemplifies p

def predicate.incommunicable := ¬ p.communicable

-- the individual existence of an entity is of course incommunicable
lemma existence_incommunicable : e.existence.incommunicable := sorry

-- and so is its haecceity
lemma haecceity_incommunicable : e.haecceity.incommunicable := sorry

-- A normal, "everyday", predicate like "being red"
-- (when e.g. it is predicated of substances, and not of "red" accidents)
-- is a communicable proper predicate.
structure predicate.normal extends predicate.proper p : Prop :=
    (axiom₆ : p.communicable)

-- Don't know if this is true
-- there are counterexamples when S contains contradictory predicates
-- but otherwise maybe an adaptation of this lemma is true
-- lemma inf_normal_normal : ∀ S : set ω.predicate, (∀ p : ω.predicate, p ∈ S → p.normal) → (Inf S).normal :=
--     begin
--         intros S h,
--         have ne : S.nonempty,
--             admit,
--         obtain ⟨p, hp⟩ := ne,
--         have pn := h p hp,
--         constructor,
--             admit,
--         obtain ⟨e₁,e₂,neq, he₁,he₂⟩  := pn.axiom₆,
--     end

-- The specific essence, or species, of an entity is a normal essential predicate from which
-- all its normal essential predicates follow.
def predicate.species_of := e.pexemplifies p ∧
                           p.normal ∧
                           p.dere ∧
                           ∀ p', e.pexemplifies p' →
                                p'.normal →
                                p'.dere →
                                p ≤ p'

def predicate.species := ∃ e, p.species_of e
def entity.has_species := ∃ p : ω.predicate, p.species_of e

-- An entity has at most one species
lemma unique_species : ∀ p₁ p₂ : ω.predicate, (∃ e, p₁.species_of e ∧ p₂.species_of e) → p₁ = p₂ := sorry

-- Now, essence is predicated in multiple ways, the foremost
-- of which is in the sense of species. However, among
-- things which have no species, essence only signifies haecceity,
-- leading us to the following definition:

/-- A predicate is the essence of an entity if it is either its specific essence or its haecceity. -/
def entity.is_essence := p.species_of e ∨ p = e.haecceity

-- We can then prove essence × existence distinction as well:
theorem existence_essence_distinction : e.is_essence e.existence ↔ e.necessary := sorry

end predicates

section apredicates

  variables (p : ω.apredicate) (e : ω.entity)

  def apredicate.sup := Sup (subtype.val '' (range $ p e))
  def apredicate.inf := Inf (subtype.val '' (range $ p e))
  def apredicate.max := Sup ⋃ e, (subtype.val '' (range $ p e))
  -- def apredicate.min := Inf ⋃ e, (subtype.val '' (range $ p e)) -- I think maybe this is always 0
  def apredicate.complete := p.max = 1

  def apredicate.existential := ∀ e, ↑(p e) ⊆ e.exists

end apredicates

section happiness

  variables (e : ω.entity) (p : ω.apredicate)

  /-- An `entity` is said to be **happy**, or **naturally perfect**, w.r.t some apredicate `p` in possible world `w`
      if it attains the greatest degree of `p` it can achieve, at `w`. -/
  def entity.happy : ω.event := {w | e.exists w ∧ ↑(p e w) = p.sup e}

  /-- An `entity` is said to be **wholesome** w.r.t some apredicate `p`
      if it can possibly be happy w.r.t. `p`. -/
  def entity.wholesome := ⋄e.happy p

  /-- An `entity` is said to be **miserable** w.r.t some apredicate `p`
      if it cannot possibly be happy w.r.t. `p`. -/
  def entity.miserable := ¬ e.wholesome p

  -- TODO: maybe this one should be generalized to arbitrary observables.
  def entity.invariantly := ∀ w₁ w₂, e.exists w₁ → e.exists w₂ → p e w₁ = p e w₂
  def entity.invariantly_happy := e.invariantly p ∧ e.wholesome p
  def entity.absolutely_happy := □e.happy p

  /-- A **maximally perfectible** `entity` w.r.t some apredicate `p` is one which
      can possibly be progressively perfected in the direction of attaining
      the greatest degree of `p` that is possible for any entity to have. -/
  def entity.mperfectible := p.sup e = p.max

  /-- A **completely perfectible** `entity` w.r.t some apredicate `p` is one which
      can possibly attain the greatest conceivable degree of `p`. -/
  def entity.cperfectible := ∃ w, p e w = 1

  /-- An `entity` is said to be **exemplary** w.r.t some apredicate `p` in some possible world `w`
      if it is maximally perfectible in itself and happy at `w`. -/
  def entity.exemplary : ω.event := {w | e.mperfectible p ∧ e.happy p w}

  /-- An `entity` is said to be an **exemplary cause of `p`**, or an **examplar** w.r.t some apredicate `p`,
      if it can possibly attain the greatest degree of `p` that is possible for any entity to achieve.
      i.e. it is an entity which is possibly `exemplary`. -/
  @[reducible, simp]
  def entity.ecause := ⋄e.exemplary p

  /-- An apredicate is **exemplarily caused** if it admits an `exemplar`. I.e., an
      `entity` which  can possibly attain the greatest degree of `p` that is
      possible for any entity to have. -/
  def apredicate.ecaused := ∃ e : ω.entity, e.ecause p

  /-- An `entity` is said to be **absolutely exemplary**, or **maximally perfect**, w.r.t some apredicate `p`
      if it is exemplary in every possible world. -/
  @[reducible, simp]
  def entity.absolutely_exemplary := □e.exemplary p
  /-- "**Maximally perfect**" is an alias for `absolutely_exemplary`. -/
  @[reducible, simp, alias]
  def entity.mperfect := e.absolutely_exemplary

  /-! # The Intuition behind exemplary causes.

      The property of real numbers of being "close" to a given
      number, say `5`, is an analogical predicate which admits an exemplary cause,
      namely `5`. We can define this predicate as something like `close₅(x) = 1 ÷ (∥x - 5∥ + 1)`,
      as we can see that `close₅(5) = 1`, `∥x∥ ⇒ +∞, close₅(x) ⇒ 0`, `∀x, 0 ≤ close₅(x) ≤ 1`.
      This predicate defines a so called "fuzzy set" of real numbers:
      the real numbers which are "close" to `5`.
      The number `5` is called an **exemplary cause** of the predicate `close₅`
      because, given the fact that it attains the greatest conceivable degree of `close₅`,
      it also serves as an ultimate criteria of comparison for determining the extent to which a real number is
      close to `5`, i.e. a number will attain higher degrees of `close₅` precisely to the extent to which
      it is close to `5`. `close₅` can then be deemed a *measure of similarity to a point*, namely the point
      `5`, the **exemplar**.

      What other kinds of predicates are measures of similarity to a point in a similar way? It appears
      that natural and moral perfections are good candidates for being exemplarily caused.
      We have an intuitive grasp, for instance, of what a good, or healthy, dog is, insofar as we can imagine
      a "perfect" dog. The dog of our dreams is one which, perhaps,
      is very strong, playful, healthy and active, lives by a healthy diet,
      is cheerful, gets a lot of sunlight, exercises regularly,
      is a very effective apologetics minister by
      putting the fear of God into the hearts of burglars and trespassers,
      is docile, etc...
      It is the kind of dog you would likely see portrayed in a dog food commercial, or
      something of the sort. He is the exemplar of what the natural powers of dogs
      can achieve when they operate in the most perfect way possible. It is an ideal dog,
      and just like with any ideal we can't help but measure other dogs with respect it.
      There are then clearly different degrees to which a dog can fully achieve the potential
      of its nature, so that we can even say, that the exemplar dog is a dog
      *in the proper sense of the word*, or absolutely, *simpliciter*, without qualification,
      while dogs not measuring up to its standards are only dogs in a more limited sense of the word,
      with qualification, relatively, or *secundum quid*. All dogs can in a sense be said to
      *participate* of the exemplar dog to the extent that they are similar to it, in a way reminiscent
      to how things were supposed by Plato to participate in their Platonic forms.

  -/

  theorem inner_life_of_the_absolutely_exemplary : e.absolutely_exemplary p ↔ e.absolutely_happy p := sorry

  lemma necessary_of_abs_exemplary : e.absolutely_exemplary p → e.necessary := sorry

end happiness

section being

/-! # The Analogy of Being

   We have seem that existence is not truly an univocal property, but a hidden analogy.
   This is obvious from the consideration that substances possess a higher degree
   of existence than their accidents, insofar as the accidents do not subsist of themselves,
   but must inhere in a substance. The analogical nature of existence formally follows
   from the fact that accidents entail the existence of their substances.

   As such we should expect there to be an analogical predicate from which existence is abstracted,
   and with respect to which "existence" will, in a sense, come by degrees. However,
   because we typically think of existence as an univocal property, having no degrees,
   we shall name this "existence" which comes by degrees **being**, rather than existence.
   Another, way to see that this "being" has degrees is to consider that being is identical
   in reality to other two so called *transcendental properties* which quite clearly have degrees, namely
   unity and actuality, which are called so because anything that exists is one individual unified thing
   and also actual. And this we can show not only by comparing accidents to substances, but also from
   comparing substances to substances.

   We observe in reality that things are more or less unified and more or less actual;
   the first we notice from the fact that all natural entities have an intrinsic unification principle which
   unifies their parts more or less, allowing the entity to be more or less complex. At the bottom of the hierarchy
   of unity we have gases, for which the unification principle is the weakest insofar as whatever
   unifies gases in a whole is not strong enough to give them a definite shape, which is rather imposed from
   without, by an external container. Next we have liquids which possess greater solidity, and hence unification,
   than gases, but are still not unified enough to possess their own shape; though they already exhibit some
   resistance against compression. We then have solids which are unified enough to have their own shapes
   and are often much more resistant to compression and various pressures than the previous.
   Plasmas are however harder to classify since the ancients would equate them with fire, which was considered
   the most perfect of the 4 elements, though fire has a less stable shape than a solid, so it is unclear whether
   they should be considered more perfect than solids or not. After the minerals, however, we have living organisms
   which are much more unified and complex than any minerals, and among them we have animals which exhibit
   even greater complexity and unification, and finally we have humans, which unify physical and metaphysical
   substances into the same whole.

   On the other hand, we have among these levels also greater degrees of actuality, since a thing is able
   to act more perfectly insofar as it is more actual, and yet anything is actual insofar as it exists. A natural
   entity is more actual to the extent that it has more energy and is habitually capable of using this energy
   to perform more complex vital operations. It is in this respect that fire/plasma is the most perfect state of
   inanimate matter, but any living organism is more perfect than it insofar as its energy does not come in the
   form of useless heat, but of direct and complex vital operations.

   It is furthermore the case that natural substances vary in perfection with respect to time,
   becoming more or less perfect depending on the circumstances. A good man can be said to be more
   perfect than a bad one, in a moral sense of the word "perfect", while a healthy man can be said
   to be more perfect than an unhealthy one, in a more natural or biological sense of the word.

   These considerations suffice for showing that natural entities exhibit greater or lesser degrees of perfection,
   but we must also extend this consideration to metaphysical entities. Metaphysical entities which have no parts,
   spatial extension and, specially, accidents, are more perfect than natural entities, not on account of a greater
   amount of complexity or energy, but on account of their greater unification and actuality. Unification
   in natural entities which have parts is exhibited in complexity insofar as a greater unifying principle is required
   to amalgamate a multiplicity of disparate entities into a single unified whole; however the more unified
   a substance is the less will their parts behave like disparate or independent entities to begin with.
   As the unity of a substance increases, its parts become so intertwined with it that
   they begin to exhibit fundamental ontological dependencies to the whole, and vice-versa.
   This is already observable in the case of living animals,
   for which the removal of an organ causes the death of the same organ,
   and often the death of the animal, in a very short period of time;
   unless artificial means are used to keep them alive.
   In the limit, this ontological dependencies would grow to become full existential dependencies,
   so that it would be impossible to distinguish a substance from its parts by extensional means,
   and we could simply say that the substance has no parts, because it has "absorbed" all of its "parts"
   within itself. A substance of this sort would exhibit greater unification than any natural
   entity, even though it would be absolutely simple.

   With respect to extension, metaphysical substances are more perfect, insofar as to have extension
   is but to be limited to a particular (compact, connected) region of space, outside of which
   the substance has no existence, while a substance without extension can be said to exist in all points
   of space without limits, as we shall later formalize (in geometry.lean). Furthermore it is obvious
   that simple substances, which have no accidents, are more perfect than composite substances
   due to the the multiplicity of states being the origin of a multiplicity
   of different ways of existing and of passive potentiality, and being the multiplicity of possible accidents
   the origin of the multiplicity of states, it is clear that a substance without accidents has no
   passive potentiality (outside perhaps of a potentiality for existence or non-existence) and no multiplicity
   of ways of existing, hence being much more unified and actual than any substance with accidents.

   Finally, metaphysical entities which are necessary are clearly more perfect than contingent ones,
   just as any substance which depends on another, should be less perfect than this other.
   These considerations suffice to show then that even among metaphysical substances,
   or when comparing metaphysical substances to natural ones, there are differing degrees of
   unification, actuality and, hence, also perfection and being. -/

  /-- The **Being**, **analogy of being**, or *actus essendi* of an ontology is an `apredicate` **is**
      which gives to every possible entity in every possible world the degree of being,
      or degree of perfection, that the entity has in that world.
      This perfection, or degree of being, is also a measurement of the degree of actuality of an entity,
      as it varies across possible worlds. Substances which are invariant with respect to
      being, are less fleeting, and as such more actual, while substances which vary greatly in being
      are more fleeting, more potential and, hence, less perfect in being. They are also less unified,
      less truthful, less good, less beautiful, etc... for all the so called *transcendentals of being*. -/
  structure being (ω : ontology) :=
    (is : ω.apredicate)
    -- Being is synonymous with existence,
    -- in the sense that an entity can only have
    -- being in any capacity or amount whatsoever
    -- if it exists, and to exist is nothing other
    -- than to participate in being to some extent
    -- or to some capacity. Existence is abstracted
    -- from being.
    (axiom₁ : ∀ e, ↑(is e) = e.exists)
    -- Being respects the hierarchies of ontological dependencies,
    -- indeed it is the very **origin** of said hierarchies. The most
    -- basic relation of ontological dependency is existential entailment,
    -- so entities which depend existentially on another shall be less perfect than
    -- that other, in any possible worlds in which they exist.
    (axiom₂ : ∀ e₁ e₂ : ω.entity, e₁ ≠ e₂ → e₁.exists ⇒ e₂.exists → is e₁ < is e₂)
    -- Being is complete, for it if were not then the maximum degree
    -- of perfection attainable by an entity would necessarily fall short
    -- of 100% of the maximum degree of perfection attainable by an entity, which is absurd.
    -- i.e., an entity that has degree of perfection 1.0 in any possible world
    -- is to be interpreted as having attained the maximum possible degree of perfection
    -- any entity could possibly achieve.
    (axiom₃ : is.complete)
    -- A substance is more or less perfect across possible worlds only because of the variability
    -- of the accidents inhering in it, so in worlds in which a substance has the same state
    -- it should have the same degree of perfection also.
    (axiom₄ : ∀ (s : ω.substance) (w₁ w₂), s.equiv w₁ w₂ → is s.up w₁ = is s.up w₂)
    -- Furthermore, in worlds in which more things subsist in a substance, it should also
    -- be more perfect, for the perfection of the subsistent entities should, in some
    -- sense, "add up" to an increase in the overall perfection of the substance.
    (axiom₅ : ∀ (s : ω.substance) (w₁ w₂), s.state w₁ ⊂ s.state w₂ → is s.up w₁ < is s.up w₂)
    -- Furthermore, states were not defined for accidents since nothing subsists in them.
    -- It should follow that accidents have being invariantly, for without variation of
    -- accidents there can be no variation of perfection.
    (axiom₆ : ∀ (a : ω.accident), a.up.invariantly is)
    -- In any possible world in which a simple substance exists, it is more perfect
    -- than all composite substances existing in the same world.
    -- Note: this does not assume that it is possible for simple substances to exist,
    -- only that **if** they do exist, they are more perfect than the composites.
    -- We also do not need this axiom to show that God is more perfect than composite things,
    -- for this follows from axiom 2, but rather we only need it to show that other, contingent,
    -- simple substances are more perfect than composite substances.
    (axiom₇ : ∀ (s₁ : ω.substance) (w), s₁.simple → s₁.exists w →
      ∀ (s₂ : ω.substance), s₂.composite → s₂.exists w → is s₂.up w < is s₁.up w)
    -- If two entities are of the same species, it should be
    -- possible for both to achieve exactly the same levels of
    -- perfection. TODO: number this axiom and make sure the numbering is consistent
    -- with pextensions.
    (axiom_to_number : ∀ (e₁ e₂ : ω.entity) (p : ω.predicate), p.species_of e₁ → p.species_of e₂ → range (is e₁) = range (is e₂))

  variable (b : ω.being)
  open predicate

  -- The necessary being is maximally perfectible w.r.t. any analogy of being.
  -- Only needed axiom₂ for this proof.
  lemma nbe_mperfectible : ω.nbe.mperfectible b.is :=
    begin
      simp [entity.mperfectible],
      symmetry,
      apply cSup_intro,
      -- goal 1
        obtain ⟨w⟩ := ω.wne,
        use b.is ω.nbe w,
        simp,
        constructor,
          exact (b.is (nbe ω) w).property,
        use ω.nbe, use w,
      -- goal 2
      intros r H,
      simp at H,
      obtain ⟨e, hr, w, eq⟩ := H,
      set rhs := apredicate.sup b.is (nbe ω),
      let r₂ := b.is ω.nbe w,
      transitivity r₂.val, swap,
        apply le_cSup, swap,
          simp [range, image],
          constructor,
            exact r₂.property,
          use w,
        simp [bdd_above, upper_bounds],
        use 1, simp, intros r₃ hr₃ _,
        simp [set.Icc] at hr₃,
        exact hr₃.right,
      by_cases h : e = ω.nbe,
        simp [r₂],
        rw [←h, eq],
      have c := b.axiom₂ e ω.nbe h _, swap,
        simp [ontology.nbe, set.subset],
      replace c := c.left w,
      rw eq at c,
      exact c,
      -- goal 3
      intros r hr,
      obtain ⟨r₂, hr₂, c⟩ := exists_lt_of_lt_cSup _ hr,
      use r₂,
      simp [range, image] at hr₂,
      simp [hr₂, c],
      use ω.nbe,
      exact hr₂,
      obtain ⟨w⟩ := ω.wne,
        use b.is ω.nbe w,
        simp,
        constructor,
          exact (b.is (nbe ω) w).property,
        use w,
    end

  /-- Misery begets misery. A wholesome entity should not depend on a miserable one.
      analogies of `being` satisfying this principle are said to be **proportionally happy**, for they
      satisfy a form of proportionate causality with respect to happiness. -/
  def being.phappy : Prop := ∀ (e₁ e₂ : ω.entity) w, e₁.happy b.is w → (e₁.exists ⇒ e₂.exists) → e₂.happy b.is w

  /-- An analogy of `being` is said to be **wholesome** if some `entity` is `wholesome` with respect to it. -/
  def being.wholesome : Prop := ∃ e : ω.entity, e.wholesome b.is

  /-- An analogy of `being` is said to be **absolutely exemplary** if some `entity` is `absolutely exemplary` with respect to it. -/
  def being.absolutely_exemplary : Prop := ∃ e : ω.entity, e.absolutely_exemplary b.is

  -- exemplary

  /-- An analogy of `being` is said to be **Exemplarily Caused** if it is exemplifiable.
      I.e. if some `entity` is an **exemplary cause** (`ecause`) of being. -/
  def being.ecaused : Prop := b.is.ecaused

  lemma ecaused_of_phappy_and_wholesome : b.phappy → b.wholesome → b.ecaused := sorry

  lemma exemplar_nbe_of_ecaused : b.ecaused → ω.nbe.ecause b.is := sorry

  /-- The perfection of a multitude of entities should increase with the number of entities.
      Analogies of `being` satisfying this principle are called **composable**. -/
  def being.composable := ∀ (s : set ω.entity) (e : ω.entity) (w₁ w₂ : ω.world),
                            e ∉ s →
                            e = Sup s →
                            w₁.entities ∩ s ⊂ w₂.entities ∩ s →
                            b.is e w₁ < b.is e w₂

  /-- An analogy of `being` is said to be **essentially exemplary** if it is essential for entities to be exemplary
      with respect to it.
      Nothing could explain otherwise why an entity would be exemplary in one world
      but not in some other.
       -/
  def being.eexemplary  : Prop := dere (flip entity.exemplary b.is)

  lemma abs_exemplary_intro {b : ω.being} : b.ecaused → b.eexemplary → b.absolutely_exemplary := sorry
  lemma nbe_eq1_of_abs_exemplary {b : ω.being} : b.absolutely_exemplary → ∀ w, b.is ω.nbe w = 1  := sorry

  /-- A **quasi-participated** `being` is essentially exemplary and exemplarily caused. -/
  @[reducible, simp]
  def being.qparticipated := b.ecaused ∧ b.eexemplary

  def being.participated := b.composable ∧ b.qparticipated

  def participated (ω : ontology) := ∃ b : ω.being, b.participated
  def composable (ω : ontology) := ∃ b : ω.being, b.composable
  def exemplary (ω : ontology) := ∃ b : ω.being, b.absolutely_exemplary

  lemma exemplary_of_participated : ω.participated → ω.exemplary :=
    begin
      rintro ⟨b, hbc, hbs, hbes⟩,
      use b,
      exact abs_exemplary_intro hbs hbes,
    end

section pextension

  /-- A **Platonic Extension** equips every property of a certain kind (e.g. normal) with an analogical extension,
      satisfying certain properties. These include possessing the predicate in a more perfect way
      than whatever possesses it in the usual way, i.e. "formally".
       This is done so that the concept of **eminence** can be defined. -/
  structure pextension (ω : ontology) (prop : ω.predicate → Prop := predicate.normal) extends being ω :=
    -- To every univocal, normal, predicate there corresponds some
    -- analogical predicate
    (extended : Π (p : ω.predicate) (h : prop p), ω.apredicate)
    -- from which it was at least partially abstracted.
    -- This means all entities having the predicate have the analogical predicate, in some
    -- capacity, or to some extent.
    (axiom₈  : ∀ (p : ω.predicate) (h : prop p) e, p e ⊆ ↑(extended p h e))
    -- However, whatever has the apredicate to some capacity but does not have the predicate,
    -- has the predicate to a strictly greater or more perfect extent than anything which can possibly
    -- have the predicate.
    (axiom₉  : ∀ (p : ω.predicate) (h : prop p) e₁ w₁, w₁ ∈ ↑(extended p h e₁) - p e₁ →
      ∀ e₂ w₂, p e₂ w₂ → extended p h e₂ w₂ < extended p h e₁ w₁)
    -- The apredicate is always existential
    (axiom₁₀  : ∀ (p : ω.predicate) (h : prop p), (extended p h).existential)
    -- Equality in being implies equality in the perfection of the analogical extension
    (axiom₁₁  : ∀ (p : ω.predicate) (h : prop p) e (w₁ w₂), is e w₁ = is e w₂ → extended p h e w₁ = extended p h e w₂)
    -- We stipulate also that the class of predicates prop, has to satisfy certain conditions in order to admit
    -- a platonic extension. All of these conditions are satisfied by the default (predicate.normal).
    (condition₁ : ∃ p, prop p)
    (condition₂ : ∀ (p : ω.predicate), prop p → p.exemplifiable)

  variables {prop : ω.predicate → Prop} (ext : ω.pextension prop) (p : ω.predicate) (h : prop p)

  def entity.eminently (e : ω.entity) : ω.event := {w | ¬ p e w ∧ ext.extended p h e w ≠ 0 }

  def entity.eminent (e : ω.entity) : Prop := e.eminently ext p h = e.exists

  def pextension.non_trivial := ∃ (p : ω.predicate) [h : prop p] (e : ω.entity), ⋄e.eminently ext p h

end pextension

end being

end ontology