@@ -929,6 +929,27 @@ do l' ← replace_nat_pfs l,
929
929
930
930
end normalize
931
931
932
+ /--
933
+ `find_squares m e` collects all terms of the form `a ^ 2` and `a * a` that appear in `e`
934
+ and adds them to the set `m`.
935
+ A pair `(a, tt)` is added to `m` when `a^2` appears in `e`, and `(a, ff)` is added to `m`
936
+ when `a*a` appears in `e`. -/
937
+ meta def find_squares : rb_set (expr × bool) → expr → tactic (rb_set (expr × bool))
938
+ | s `(%%a ^ 2 ) := do s ← find_squares s a, return (s.insert (a, tt))
939
+ | s e@`(%%e1 * %%e2) := if e1 = e2 then do s ← find_squares s e1, return (s.insert (e1, ff)) else e.mfoldl find_squares s
940
+ | s e := e.mfoldl find_squares s
941
+
942
+ -- used in the `nlinarith` normalization steps. The `_` argument is for uniformity.
943
+ @[nolint unused_arguments]
944
+ lemma mul_zero_eq {α} {R : α → α → Prop } [semiring α] {a b : α} (_ : R a 0 ) (h : b = 0 ) : a * b = 0 :=
945
+ by simp [h]
946
+
947
+ -- used in the `nlinarith` normalization steps. The `_` argument is for uniformity.
948
+ @[nolint unused_arguments]
949
+ lemma zero_mul_eq {α} {R : α → α → Prop } [semiring α] {a b : α} (h : a = 0 ) (_ : R b 0 ) : a * b = 0 :=
950
+ by simp [h]
951
+
952
+
932
953
end linarith
933
954
934
955
section
@@ -1045,11 +1066,78 @@ optional arguments:
1045
1066
hypotheses.
1046
1067
* If `exfalso` is false, `linarith` will fail when the goal is neither an inequality nor `false`.
1047
1068
(True by default.)
1069
+
1070
+ A variant, `nlinarith`, does some basic preprocessing to handle some nonlinear goals.
1048
1071
-/
1049
1072
add_tactic_doc
1050
1073
{ name := " linarith" ,
1051
1074
category := doc_category.tactic,
1052
1075
decl_names := [`tactic .interactive.linarith],
1053
1076
tags := [" arithmetic" , " decision procedure" , " finishing" ] }
1054
1077
1078
+ /--
1079
+ An extension of `linarith` with some preprocessing to allow it to solve some nonlinear arithmetic
1080
+ problems. (Based on Coq's `nra` tactic.) See `linarith` for the available syntax of options,
1081
+ which are inherited by `nlinarith`; that is, `nlinarith!` and `nlinarith only [h1, h2]` all work as
1082
+ in `linarith`. The preprocessing is as follows:
1083
+
1084
+ * For every subterm `a ^ 2` or `a * a` in a hypothesis or the goal,
1085
+ the assumption `0 ≤ a ^ 2` or `0 ≤ a * a` is added to the context.
1086
+ * For every pair of hypotheses `a1 R1 b1`, `a2 R2 b2` in the context, `R1, R2 ∈ {<, ≤, =}`,
1087
+ the assumption `0 R' (b1 - a1) * (b2 - a2)` is added to the context (non-recursively),
1088
+ where `R ∈ {<, ≤, =}` is the appropriate comparison derived from `R1, R2`.
1089
+ -/
1090
+ meta def tactic.interactive.nlinarith (red : parse ((tk " !" )?))
1091
+ (restr : parse ((tk " only" )?)) (hyps : parse pexpr_list?)
1092
+ (cfg : linarith_config := {}) : tactic unit := do
1093
+ ls ← match hyps with
1094
+ | none := if restr.is_some then return [] else local_context
1095
+ | some hyps := do
1096
+ ls ← hyps.mmap i_to_expr,
1097
+ if restr.is_some then return ls else (++ ls) <$> local_context
1098
+ end ,
1099
+ (s, ge0) ← (list.mfoldr (λ h ⟨s, l⟩, do
1100
+ h ← infer_type h >>= rearr_comp h <|> return h,
1101
+ t ← infer_type h,
1102
+ s ← find_squares s t,
1103
+ return (s, match t with
1104
+ | `(%%a ≤ 0 ) := (ineq.le, h) :: l
1105
+ | `(%%a < 0 ) := (ineq.lt, h) :: l
1106
+ | `(%%a = 0 ) := (ineq.eq, h) :: l
1107
+ | _ := l end ))
1108
+ (mk_rb_set, []) ls : tactic (rb_set (expr × bool) × list (ineq × expr))),
1109
+ s ← target >>= find_squares s,
1110
+ (hyps, ge0) ← s.fold (return (hyps, ge0)) (λ ⟨e, is_sq⟩ tac, do
1111
+ (hyps, ge0) ← tac,
1112
+ (do
1113
+ t ← infer_type e,
1114
+ when cfg.restrict_type.is_some
1115
+ (is_def_eq `(some %%t : option Type ) cfg.restrict_type_reflect),
1116
+ p ← mk_app (if is_sq then ``pow_two_nonneg else ``mul_self_nonneg ) [e],
1117
+ p ← infer_type p >>= rearr_comp p <|> return p,
1118
+ t ← infer_type p,
1119
+ h ← assertv `h t p,
1120
+ return (hyps.map (λ l, pexpr.of_expr h :: l), (ineq.le, h) :: ge0)) <|>
1121
+ return (hyps, ge0)),
1122
+ ge0.mmap'_diag (λ ⟨posa, a⟩ ⟨posb, b⟩, do
1123
+ p ← match posa, posb with
1124
+ | ineq.eq, _ := mk_app ``zero_mul_eq [a, b]
1125
+ | _, ineq.eq := mk_app ``mul_zero_eq [a, b]
1126
+ | ineq.lt, ineq.lt := mk_app ``mul_pos_of_neg_of_neg [a, b]
1127
+ | ineq.lt, ineq.le := do a ← mk_app ``le_of_lt [a], mk_app ``mul_nonneg_of_nonpos_of_nonpos [a, b]
1128
+ | ineq.le, ineq.lt := do b ← mk_app ``le_of_lt [b], mk_app ``mul_nonneg_of_nonpos_of_nonpos [a, b]
1129
+ | ineq.le, ineq.le := mk_app ``mul_nonneg_of_nonpos_of_nonpos [a, b]
1130
+ end ,
1131
+ t ← infer_type p,
1132
+ assertv `h t p, skip),
1133
+ tactic.interactive.linarith red restr hyps cfg
1134
+
1135
+ add_hint_tactic " nlinarith"
1136
+
1137
+ add_tactic_doc
1138
+ { name := " nlinarith" ,
1139
+ category := doc_category.tactic,
1140
+ decl_names := [`tactic .interactive.nlinarith],
1141
+ tags := [" arithmetic" , " decision procedure" , " finishing" ] }
1142
+
1055
1143
end
0 commit comments