/- Copyright (c) 2023 Lean FRO, LLC. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Kim Morrison -/ prelude import Lean.Elab.Tactic.Omega.Core import Lean.Elab.Tactic.FalseOrByContra import Lean.Meta.Tactic.Cases open Lean Meta Omega set_option maxHeartbeats 6000 def pushNot (h P : Expr) : MetaM (Option Expr) := do let P ← whnfR P trace[omega] "pushing negation: {P}" match P with | .forallE _ t b _ => if (← isProp t) && (← isProp b) then return some (mkApp4 (.const ``Decidable.and_not_of_not_imp []) t b (.app (.const ``Classical.propDecidable []) t) h) else return none | .app _ _ => match_expr P with | LT.lt α _ x y => match_expr α with | Nat => return some (mkApp3 (.const ``Nat.le_of_not_lt []) x y h) | Int => return some (mkApp3 (.const ``Int.le_of_not_lt []) x y h) | Fin n => return some (mkApp4 (.const ``Fin.le_of_not_lt []) n x y h) | _ => return none | LE.le α _ x y => match_expr α with | Nat => return some (mkApp3 (.const ``Nat.lt_of_not_le []) x y h) | Int => return some (mkApp3 (.const ``Int.lt_of_not_le []) x y h) | Fin n => return some (mkApp4 (.const ``Fin.lt_of_not_le []) n x y h) | _ => return none | Eq α x y => match_expr α with | Nat => return some (mkApp3 (.const ``Nat.lt_or_gt_of_ne []) x y h) | Int => return some (mkApp3 (.const ``Int.lt_or_gt_of_ne []) x y h) | Fin n => return some (mkApp4 (.const ``Fin.lt_or_gt_of_ne []) n x y h) | _ => return none | Dvd.dvd α _ k x => match_expr α with | Nat => return some (mkApp3 (.const ``Nat.emod_pos_of_not_dvd []) k x h) | Int => -- This introduces a disjunction that could be avoided by checking `k ≠ 0`. return some (mkApp3 (.const ``Int.emod_pos_of_not_dvd []) k x h) | _ => return none | Prod.Lex _ _ _ _ _ _ => return some (← mkAppM ``Prod.of_not_lex #[h]) | Not P => return some (mkApp3 (.const ``Decidable.of_not_not []) P (.app (.const ``Classical.propDecidable []) P) h) | And P Q => return some (mkApp5 (.const ``Decidable.or_not_not_of_not_and []) P Q (.app (.const ``Classical.propDecidable []) P) (.app (.const ``Classical.propDecidable []) Q) h) | Or P Q => return some (mkApp3 (.const ``and_not_not_of_not_or []) P Q h) | Iff P Q => return some (mkApp5 (.const ``Decidable.and_not_or_not_and_of_not_iff []) P Q (.app (.const ``Classical.propDecidable []) P) (.app (.const ``Classical.propDecidable []) Q) h) | _ => return none | _ => return none