Built with
doc-gen4, running Lean4.
Bubbles (
) indicate interactive fragments: hover for details, tap to reveal contents.
Use
Ctrl+βCtrl+βto navigate,
Ctrl+π±οΈto focus.
On Mac, use
Cmdinstead of
Ctrl.
/-
Copyright (c) 2019 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Simon Hudon, Alice Laroche, FrΓ©dΓ©ric Dupuis, Jireh Loreaux
-/
import Lean
import Mathlib.Lean.Expr
import Mathlib.Logic.Basic
import Mathlib.Init.Algebra.Order
import Mathlib.Tactic.Conv
namespace Mathlib.Tactic.PushNeg
open Lean Meta Elab.Tactic Parser.Tactic
variable (p q : Prop) (s : Ξ± β Prop)
theorem not_not_eq : (Β¬ Β¬ p) = p := propext: β {a b : Prop}, (a β b) β a = b
propext not_not
theorem not_and_eq : (Β¬ (p β§ q)) = (p β Β¬ q) := propext: β {a b : Prop}, (a β b) β a = b
propext not_and
theorem not_and_or_eq : (Β¬ (p β§ q)) = (Β¬ p β¨ Β¬ q) := propext: β {a b : Prop}, (a β b) β a = b
propext not_and_or
theorem not_or_eq : (Β¬ (p β¨ q)) = (Β¬ p β§ Β¬ q) := propext: β {a b : Prop}, (a β b) β a = b
propext not_or
theorem not_forall_eq: β {Ξ± : Sort u_1} (s : Ξ± β Prop), (Β¬β (x : Ξ±), s x) = β x, Β¬s x
not_forall_eq : (Β¬ β x, s x) = (β x, Β¬ s x) := propext: β {a b : Prop}, (a β b) β a = b
propext not_forall: β {Ξ± : Sort ?u.201} {p : Ξ± β Prop}, (Β¬β (x : Ξ±), p x) β β x, Β¬p x
not_forall
theorem not_exists_eq: (Β¬β x, s x) = β (x : Ξ±), Β¬s x
not_exists_eq : (Β¬ β x, s x) = (β x, Β¬ s x) := propext: β {a b : Prop}, (a β b) β a = b
propext not_exists: β {Ξ± : Sort ?u.257} {p : Ξ± β Prop}, (Β¬β x, p x) β β (x : Ξ±), Β¬p x
not_exists
theorem not_implies_eq: (Β¬(p β q)) = (p β§ Β¬q)
not_implies_eq : (Β¬ (p β q)) = (p β§ Β¬ q) := propext: β {a b : Prop}, (a β b) β a = b
propext not_imp
theorem not_ne_eq: β (x y : Ξ±), (Β¬x β y) = (x = y)
not_ne_eq (x y : Ξ±) : (Β¬ (x β y)) = (x = y) := ne_eq: β {Ξ± : Sort ?u.345} (a b : Ξ±), (a β b) = Β¬a = b
ne_eq x y βΈ not_not_eq _
variable {Ξ² : Type u} [LinearOrder: Type ?u.385 β Type ?u.385
LinearOrder Ξ²]
theorem not_le_eq: β (a b : Ξ²), (Β¬a β€ b) = (b < a)
not_le_eq (a b : Ξ²) : (Β¬ (a β€ b)) = (b < a) := propext: β {a b : Prop}, (a β b) β a = b
propext not_le
theorem not_lt_eq: β (a b : Ξ²), (Β¬a < b) = (b β€ a)
not_lt_eq (a b : Ξ²) : (Β¬ (a < b)) = (b β€ a) := propext: β {a b : Prop}, (a β b) β a = b
propext not_lt
theorem not_ge_eq: β (a b : Ξ²), (Β¬a β₯ b) = (a < b)
not_ge_eq (a b : Ξ²) : (Β¬ (a β₯ b)) = (a < b) := propext: β {a b : Prop}, (a β b) β a = b
propext not_le
theorem not_gt_eq: β (a b : Ξ²), (Β¬a > b) = (a β€ b)
not_gt_eq (a b : Ξ²) : (Β¬ (a > b)) = (a β€ b) := propext: β {a b : Prop}, (a β b) β a = b
propext not_lt
/-- Make `push_neg` use `not_and_or` rather than the default `not_and`. -/
register_option push_neg.use_distrib : Bool :=
{ defValue := false
group := ""
descr := "Make `push_neg` use `not_and_or` rather than the default `not_and`.": String
"Make `push_neg` use `not_and_or` rather than the default `not_and`." }
/-- Push negations at the top level of the current expression. -/
def transformNegationStep (e : Expr) : SimpM (Option Simp.Step) := do
let mkSimpStep (e : Expr) (pf : Expr) : Simp.Step :=
Simp.Step.visit { expr := e, proof? := some pf }
let e_whnf β whnfR e
let some ex := e_whnf.not? | return Simp.Step.visit { expr := e }
match ex.getAppFnArgs with
| (``Not, #[e]) =>
return mkSimpStep e (β mkAppM ``not_not_eq #[e]): ?m.1533
(β mkAppM(β mkAppM ``not_not_eq #[e]): ?m.1533
``not_not_eq(β mkAppM ``not_not_eq #[e]): ?m.1533
#[e(β mkAppM ``not_not_eq #[e]): ?m.1533
])
| (``And, #[p, q]) =>
match β getBoolOption `push_neg.use_distrib: Name
`push_neg.use_distrib with
| false => return mkSimpStep (.forallE `_ p (mkNot q) default) (βmkAppM ``not_and_eq #[p, q]): ?m.1939
(βmkAppM(βmkAppM ``not_and_eq #[p, q]): ?m.1939
``not_and_eq(βmkAppM ``not_and_eq #[p, q]): ?m.1939
#[p(βmkAppM ``not_and_eq #[p, q]): ?m.1939
, q(βmkAppM ``not_and_eq #[p, q]): ?m.1939
])
| true => return mkSimpStep (mkOr (mkNot p) (mkNot q)) (βmkAppM ``not_and_or_eq #[p, q]): ?m.2211
(βmkAppM(βmkAppM ``not_and_or_eq #[p, q]): ?m.2211
``not_and_or_eq(βmkAppM ``not_and_or_eq #[p, q]): ?m.2211
#[p(βmkAppM ``not_and_or_eq #[p, q]): ?m.2211
, q(βmkAppM ``not_and_or_eq #[p, q]): ?m.2211
])
| (``Or, #[p, q]) =>
return mkSimpStep (mkAnd (mkNot p) (mkNot q)) (βmkAppM ``not_or_eq #[p, q]): ?m.2622
(βmkAppM(βmkAppM ``not_or_eq #[p, q]): ?m.2622
``not_or_eq(βmkAppM ``not_or_eq #[p, q]): ?m.2622
#[p(βmkAppM ``not_or_eq #[p, q]): ?m.2622
, q(βmkAppM ``not_or_eq #[p, q]): ?m.2622
])
| (``Eq: {Ξ± : Sort u_1} β Ξ± β Ξ± β Prop
Eq, #[_ty, eβ, eβ]) =>
return Simp.Step.visit { expr := β mkAppM ``Ne: {Ξ± : Sort u} β Ξ± β Ξ± β Prop
Ne #[eβ, eβ] }
| (``Ne: {Ξ± : Sort u} β Ξ± β Ξ± β Prop
Ne, #[_ty, eβ, eβ]) =>
return mkSimpStep (β mkAppM ``Eq #[eβ, eβ]): ?m.3128
(β mkAppM(β mkAppM ``Eq #[eβ, eβ]): ?m.3128
``Eq: {Ξ± : Sort u_1} β Ξ± β Ξ± β Prop
Eq(β mkAppM ``Eq #[eβ, eβ]): ?m.3128
#[eβ(β mkAppM ``Eq #[eβ, eβ]): ?m.3128
, eβ(β mkAppM ``Eq #[eβ, eβ]): ?m.3128
]) (β mkAppM ``not_ne_eq #[eβ, eβ]): ?m.3196
(β mkAppM(β mkAppM ``not_ne_eq #[eβ, eβ]): ?m.3196
``not_ne_eq: β {Ξ± : Sort u_1} (x y : Ξ±), (Β¬x β y) = (x = y)
not_ne_eq(β mkAppM ``not_ne_eq #[eβ, eβ]): ?m.3196
#[eβ(β mkAppM ``not_ne_eq #[eβ, eβ]): ?m.3196
, eβ(β mkAppM ``not_ne_eq #[eβ, eβ]): ?m.3196
])
| (``LE.le: {Ξ± : Type u} β [self : LE Ξ±] β Ξ± β Ξ± β Prop
LE.le, #[ty, _inst, eβ, eβ]) =>
let linOrd β synthInstance? (β mkAppM ``LinearOrder #[ty]): ?m.3438
(β mkAppM(β mkAppM ``LinearOrder #[ty]): ?m.3438
``LinearOrder(β mkAppM ``LinearOrder #[ty]): ?m.3438
#[ty(β mkAppM ``LinearOrder #[ty]): ?m.3438
])
match linOrd with
| some _ => return mkSimpStep (β mkAppM ``LT.lt #[eβ, eβ]): ?m.3575
(β mkAppM(β mkAppM ``LT.lt #[eβ, eβ]): ?m.3575
``LT.lt: {Ξ± : Type u} β [self : LT Ξ±] β Ξ± β Ξ± β Prop
LT.lt(β mkAppM ``LT.lt #[eβ, eβ]): ?m.3575
#[eβ(β mkAppM ``LT.lt #[eβ, eβ]): ?m.3575
, eβ(β mkAppM ``LT.lt #[eβ, eβ]): ?m.3575
]) (β mkAppM ``not_le_eq #[eβ, eβ]): ?m.3643
(β mkAppM(β mkAppM ``not_le_eq #[eβ, eβ]): ?m.3643
``not_le_eq(β mkAppM ``not_le_eq #[eβ, eβ]): ?m.3643
#[eβ(β mkAppM ``not_le_eq #[eβ, eβ]): ?m.3643
, eβ(β mkAppM ``not_le_eq #[eβ, eβ]): ?m.3643
])
| none => return none
| (``LT.lt: {Ξ± : Type u} β [self : LT Ξ±] β Ξ± β Ξ± β Prop
LT.lt, #[ty, _inst, eβ, eβ]) =>
let linOrd β synthInstance? (β mkAppM ``LinearOrder #[ty]): ?m.4142
(β mkAppM(β mkAppM ``LinearOrder #[ty]): ?m.4142
``LinearOrder(β mkAppM ``LinearOrder #[ty]): ?m.4142
#[ty(β mkAppM ``LinearOrder #[ty]): ?m.4142
])
match linOrd with
| some _ => return mkSimpStep (β mkAppM ``LE.le #[eβ, eβ]): ?m.4279
(β mkAppM(β mkAppM ``LE.le #[eβ, eβ]): ?m.4279
``LE.le: {Ξ± : Type u} β [self : LE Ξ±] β Ξ± β Ξ± β Prop
LE.le(β mkAppM ``LE.le #[eβ, eβ]): ?m.4279
#[eβ(β mkAppM ``LE.le #[eβ, eβ]): ?m.4279
, eβ(β mkAppM ``LE.le #[eβ, eβ]): ?m.4279
]) (β mkAppM ``not_lt_eq #[eβ, eβ]): ?m.4347
(β mkAppM(β mkAppM ``not_lt_eq #[eβ, eβ]): ?m.4347
``not_lt_eq(β mkAppM ``not_lt_eq #[eβ, eβ]): ?m.4347
#[eβ(β mkAppM ``not_lt_eq #[eβ, eβ]): ?m.4347
, eβ(β mkAppM ``not_lt_eq #[eβ, eβ]): ?m.4347
])
| none => return none
| (``GE.ge: {Ξ± : Type u} β [inst : LE Ξ±] β Ξ± β Ξ± β Prop
GE.ge, #[ty, _inst, eβ, eβ]) =>
let linOrd β synthInstance? (β mkAppM ``LinearOrder #[ty]): ?m.4756
(β mkAppM(β mkAppM ``LinearOrder #[ty]): ?m.4756
``LinearOrder(β mkAppM ``LinearOrder #[ty]): ?m.4756
#[ty(β mkAppM ``LinearOrder #[ty]): ?m.4756
])
match linOrd with
| some _ => return mkSimpStep (β mkAppM ``LT.lt #[eβ, eβ]): ?m.4892
(β mkAppM(β mkAppM ``LT.lt #[eβ, eβ]): ?m.4892
``LT.lt: {Ξ± : Type u} β [self : LT Ξ±] β Ξ± β Ξ± β Prop
LT.lt(β mkAppM ``LT.lt #[eβ, eβ]): ?m.4892
#[eβ(β mkAppM ``LT.lt #[eβ, eβ]): ?m.4892
, eβ(β mkAppM ``LT.lt #[eβ, eβ]): ?m.4892
]) (β mkAppM ``not_ge_eq #[eβ, eβ]): ?m.4960
(β mkAppM(β mkAppM ``not_ge_eq #[eβ, eβ]): ?m.4960
``not_ge_eq(β mkAppM ``not_ge_eq #[eβ, eβ]): ?m.4960
#[eβ(β mkAppM ``not_ge_eq #[eβ, eβ]): ?m.4960
, eβ(β mkAppM ``not_ge_eq #[eβ, eβ]): ?m.4960
])
| none => return none
| (``GT.gt: {Ξ± : Type u} β [inst : LT Ξ±] β Ξ± β Ξ± β Prop
GT.gt, #[ty, _inst, eβ, eβ]) =>
let linOrd β synthInstance? (β mkAppM ``LinearOrder #[ty]): ?m.5313
(β mkAppM(β mkAppM ``LinearOrder #[ty]): ?m.5313
``LinearOrder(β mkAppM ``LinearOrder #[ty]): ?m.5313
#[ty(β mkAppM ``LinearOrder #[ty]): ?m.5313
])
match linOrd with
| some _ => return mkSimpStep (β mkAppM ``LE.le #[eβ, eβ]): ?m.5449
(β mkAppM(β mkAppM ``LE.le #[eβ, eβ]): ?m.5449
``LE.le: {Ξ± : Type u} β [self : LE Ξ±] β Ξ± β Ξ± β Prop
LE.le(β mkAppM ``LE.le #[eβ, eβ]): ?m.5449
#[eβ(β mkAppM ``LE.le #[eβ, eβ]): ?m.5449
, eβ(β mkAppM ``LE.le #[eβ, eβ]): ?m.5449
]) (β mkAppM ``not_gt_eq #[eβ, eβ]): ?m.5517
(β mkAppM(β mkAppM ``not_gt_eq #[eβ, eβ]): ?m.5517
``not_gt_eq(β mkAppM ``not_gt_eq #[eβ, eβ]): ?m.5517
#[eβ(β mkAppM ``not_gt_eq #[eβ, eβ]): ?m.5517
, eβ(β mkAppM ``not_gt_eq #[eβ, eβ]): ?m.5517
])
| none => return none
| (``Exists, #[_, .lam n typ bo bi]) =>
return mkSimpStep (.forallE n typ (mkNot bo) bi)
(β mkAppM ``not_exists_eq #[.lam n typ bo bi]): ?m.5863
(β mkAppM(β mkAppM ``not_exists_eq #[.lam n typ bo bi]): ?m.5863
``not_exists_eq: β {Ξ± : Sort u_1} (s : Ξ± β Prop), (Β¬β x, s x) = β (x : Ξ±), Β¬s x
not_exists_eq(β mkAppM ``not_exists_eq #[.lam n typ bo bi]): ?m.5863
#[.lam(β mkAppM ``not_exists_eq #[.lam n typ bo bi]): ?m.5863
n(β mkAppM ``not_exists_eq #[.lam n typ bo bi]): ?m.5863
typ(β mkAppM ``not_exists_eq #[.lam n typ bo bi]): ?m.5863
bo(β mkAppM ``not_exists_eq #[.lam n typ bo bi]): ?m.5863
bi(β mkAppM ``not_exists_eq #[.lam n typ bo bi]): ?m.5863
])
| (``Exists, #[_, _]) =>
return none
| _ => match ex with
| .forallE name ty body binfo => do
if (β isProp ty) then
return mkSimpStep (β mkAppM ``And #[ty, mkNot body]): ?m.6264
(β mkAppM(β mkAppM ``And #[ty, mkNot body]): ?m.6264
``And(β mkAppM ``And #[ty, mkNot body]): ?m.6264
#[ty(β mkAppM ``And #[ty, mkNot body]): ?m.6264
, mkNot(β mkAppM ``And #[ty, mkNot body]): ?m.6264
body(β mkAppM ``And #[ty, mkNot body]): ?m.6264
])
(β mkAppM ``not_implies_eq #[ty, body]): ?m.6332
(β mkAppM(β mkAppM ``not_implies_eq #[ty, body]): ?m.6332
``not_implies_eq: β (p q : Prop), (Β¬(p β q)) = (p β§ Β¬q)
not_implies_eq(β mkAppM ``not_implies_eq #[ty, body]): ?m.6332
#[ty(β mkAppM ``not_implies_eq #[ty, body]): ?m.6332
, body(β mkAppM ``not_implies_eq #[ty, body]): ?m.6332
])
else
let body' : Expr := .lam name ty (mkNot body) binfo
let body'' : Expr := .lam name ty body binfo
return mkSimpStep (β mkAppM ``Exists #[body']): ?m.6510
(β mkAppM(β mkAppM ``Exists #[body']): ?m.6510
``Exists(β mkAppM ``Exists #[body']): ?m.6510
#[body'(β mkAppM ``Exists #[body']): ?m.6510
]) (β mkAppM ``not_forall_eq #[body'']): ?m.6576
(β mkAppM(β mkAppM ``not_forall_eq #[body'']): ?m.6576
``not_forall_eq: β {Ξ± : Sort u_1} (s : Ξ± β Prop), (Β¬β (x : Ξ±), s x) = β x, Β¬s x
not_forall_eq(β mkAppM ``not_forall_eq #[body'']): ?m.6576
#[body''(β mkAppM ``not_forall_eq #[body'']): ?m.6576
])
| _ => return none
/-- Recursively push negations at the top level of the current expression. This is needed
to handle e.g. triple negation. -/
partial def transformNegation (e : Expr) : SimpM Simp.Step := do
let Simp.Step.visit rβ β transformNegationStep e | return Simp.Step.visit { expr := e }
match rβ.proof? with
| none => return Simp.Step.visit rβ
| some _ => do
let Simp.Step.visit rβ β transformNegation rβ.expr | return Simp.Step.visit rβ
return Simp.Step.visit (β Simp.mkEqTrans rβ rβ): ?m.47728
(β Simp.mkEqTrans(β Simp.mkEqTrans rβ rβ): ?m.47728
rβ(β Simp.mkEqTrans rβ rβ): ?m.47728
rβ(β Simp.mkEqTrans rβ rβ): ?m.47728
)
/-- Common entry point to `push_neg` as a conv. -/
def pushNegCore (tgt : Expr) : MetaM Simp.Result := do
let myctx : Simp.Context :=
{ config := { eta := true },
simpTheorems := #[ ]
congrTheorems := (β getSimpCongrTheorems): ?m.50485
(β getSimpCongrTheorems(β getSimpCongrTheorems): ?m.50485
) }
(Β·.1) <$> Simp.main tgt myctx (methods := { pre := transformNegation })
/--
Push negations into the conclusion of an expression.
For instance, an expression `Β¬ β x, β y, x β€ y` will be transformed by `push_neg` into
`β x, β y, y < x`. Variable names are conserved.
This tactic pushes negations inside expressions. For instance, given a hypothesis
```lean
| Β¬ β Ξ΅ > 0, β Ξ΄ > 0, β x, |x - xβ| β€ Ξ΄ β |f x - yβ| β€ Ξ΅)
```
writing `push_neg` will turn the target into
```lean
| β Ξ΅, Ξ΅ > 0 β§ β Ξ΄, Ξ΄ > 0 β (β x, |x - xβ| β€ Ξ΄ β§ Ξ΅ < |f x - yβ|),
```
(The pretty printer does *not* use the abreviations `β Ξ΄ > 0` and `β Ξ΅ > 0` but this issue
has nothing to do with `push_neg`).
Note that names are conserved by this tactic, contrary to what would happen with `simp`
using the relevant lemmas.
This tactic has two modes: in standard mode, it transforms `Β¬(p β§ q)` into `p β Β¬q`, whereas in
distrib mode it produces `Β¬p β¨ Β¬q`. To use distrib mode, use `set_option push_neg.use_distrib true`.
-/
syntax (name := pushNegConv) "push_neg" : conv
/-- Execute `push_neg` as a conv tactic. -/
@[tactic pushNegConv] def elabPushNegConv : Tactic := fun _ β¦ withMainContext do
Conv.applySimpResult (β pushNegCore (β instantiateMVars (β Conv.getLhs))): ?m.52499
(β pushNegCore(β pushNegCore (β instantiateMVars (β Conv.getLhs))): ?m.52499
(β instantiateMVars (β Conv.getLhs)): ?m.52293
(β instantiateMVars(β instantiateMVars (β Conv.getLhs)): ?m.52293
(β Conv.getLhs): ?m.52159
(β Conv.getLhs(β Conv.getLhs): ?m.52159
)(β instantiateMVars (β Conv.getLhs)): ?m.52293
)(β pushNegCore (β instantiateMVars (β Conv.getLhs))): ?m.52499
)
/--
The syntax is `#push_neg e`, where `e` is an expression,
which will print the `push_neg` form of `e`.
`#push_neg` understands local variables, so you can use them to introduce parameters.
-/
macro (name := pushNeg) tk:"#push_neg " e:term : command => `(command| #conv%$tk push_neg => $e)
/-- Execute main loop of `push_neg` at the main goal. -/
def pushNegTarget : TacticM Unit := withMainContext do
let goal β getMainGoal
let tgt β instantiateMVars (β goal.getType): ?m.55519
(β goal(β goal.getType): ?m.55519
.getType(β goal.getType): ?m.55519
)
replaceMainGoal [β applySimpResultToTarget goal tgt (β pushNegCore tgt)]: List ?m.55768
[β applySimpResultToTarget[β applySimpResultToTarget goal tgt (β pushNegCore tgt)]: List ?m.55768
goal[β applySimpResultToTarget goal tgt (β pushNegCore tgt)]: List ?m.55768
tgt[β applySimpResultToTarget goal tgt (β pushNegCore tgt)]: List ?m.55768
(β pushNegCore tgt): ?m.55690
(β pushNegCore(β pushNegCore tgt): ?m.55690
tgt(β pushNegCore tgt): ?m.55690
)[β applySimpResultToTarget goal tgt (β pushNegCore tgt)]: List ?m.55768
]
/-- Execute main loop of `push_neg` at a local hypothesis. -/
def pushNegLocalDecl (fvarId : FVarId): TacticM Unit := withMainContext do
let ldecl β fvarId.getDecl
if ldecl.isAuxDecl then return: ?m.57249 ?m.57251
return
let tgt β instantiateMVars ldecl.type
let goal β getMainGoal
let myres β pushNegCore tgt
let some (_, newGoal) β applySimpResultToLocalDecl goal fvarId myres False | failure
replaceMainGoal [newGoal]
/--
Push negations into the conclusion of a hypothesis.
For instance, a hypothesis `h : Β¬ β x, β y, x β€ y` will be transformed by `push_neg at h` into
`h : β x, β y, y < x`. Variable names are conserved.
This tactic pushes negations inside expressions. For instance, given a hypothesis
```lean
h : Β¬ β Ξ΅ > 0, β Ξ΄ > 0, β x, |x - xβ| β€ Ξ΄ β |f x - yβ| β€ Ξ΅)
```
writing `push_neg at h` will turn `h` into
```lean
h : β Ξ΅, Ξ΅ > 0 β§ β Ξ΄, Ξ΄ > 0 β (β x, |x - xβ| β€ Ξ΄ β§ Ξ΅ < |f x - yβ|),
```
(The pretty printer does *not* use the abreviations `β Ξ΄ > 0` and `β Ξ΅ > 0` but this issue
has nothing to do with `push_neg`).
Note that names are conserved by this tactic, contrary to what would happen with `simp`
using the relevant lemmas. One can also use this tactic at the goal using `push_neg`,
at every hypothesis and the goal using `push_neg at *` or at selected hypotheses and the goal
using say `push_neg at h h' β’` as usual.
This tactic has two modes: in standard mode, it transforms `Β¬(p β§ q)` into `p β Β¬q`, whereas in
distrib mode it produces `Β¬p β¨ Β¬q`. To use distrib mode, use `set_option push_neg.use_distrib true`.
-/
elab "push_neg" loc:(ppSpace location)? : tactic =>
let loc := (loc.map expandLocation).getD: {Ξ± : Type ?u.60666} β Option Ξ± β Ξ± β Ξ±
getD (.targets #[] true)
withLocation loc
pushNegLocalDecl
pushNegTarget
(fun _ β¦ logInfo "push_neg couldn't find a negation to push": String
"push_neg couldn't find a negation to push")