Numerically check one substituted formula instance.
(expr: BoxedExpression)
| 126 | 'Greater', |
| 127 | 'GreaterEqual', |
| 128 | 'NotEqual', |
| 129 | ]); |
| 130 | |
| 131 | /** Numerically check one substituted formula instance. */ |
| 132 | function checkInstance(expr: BoxedExpression): { |
| 133 | outcome: InstanceOutcome; |
| 134 | detail?: string; |
| 135 | } { |
| 136 | const op = expr.operator; |
| 137 | const tol = isApproximateInstance(expr.json) ? APPROX_REL_TOL : REL_TOL; |
| 138 | |
| 139 | if (op === 'Equal' && isFunction(expr) && expr.ops.length >= 2) { |
| 140 | const vals: (Complex | null)[] = expr.ops.map((o) => { |
| 141 | try { |
| 142 | return numericValue(o); |
| 143 | } catch { |
| 144 | return null; |
| 145 | } |
| 146 | }); |
| 147 | if (vals.some((v) => v === null)) |
| 148 | return { outcome: 'not-evaluable', detail: 'side not numeric' }; |
| 149 | if (vals.some((v) => !Number.isFinite(v!.re) || !Number.isFinite(v!.im))) |
| 150 | return { outcome: 'Unknown', detail: 'non-finite side' }; |
| 151 | for (let i = 0; i + 1 < vals.length; i++) { |
| 152 | if (!approxEqual(vals[i]!, vals[i + 1]!, tol)) { |
| 153 | const fmt = (z: Complex) => `${z.re}${z.im ? `+${z.im}i` : ''}`; |
| 154 | return { |
| 155 | outcome: 'False', |
| 156 | detail: `sides differ: ${vals.map((v) => fmt(v!)).join(' vs ')}`, |
| 157 | }; |
| 158 | } |
| 159 | } |
| 160 | return { outcome: 'True' }; |
| 161 | } |
| 162 | |
| 163 | if (RELATIONS.has(op) && isFunction(expr) && expr.ops.length >= 2) { |
| 164 | const vals: (Complex | null)[] = expr.ops.map((o) => { |
| 165 | try { |
| 166 | return numericValue(o); |
| 167 | } catch { |
| 168 | return null; |
| 169 | } |
| 170 | }); |
| 171 | if (vals.some((v) => v === null)) |
| 172 | return { outcome: 'not-evaluable', detail: 'side not numeric' }; |
| 173 | // Relations other than NotEqual require (numerically) real operands |
| 174 | if ( |
| 175 | op !== 'NotEqual' && |
| 176 | vals.some((v) => Math.abs(v!.im) > REL_TOL * Math.max(absC(v!), 1)) |
| 177 | ) |
| 178 | return { outcome: 'Unknown', detail: 'complex operand in order relation' }; |
| 179 | // chain semantics: every consecutive pair must satisfy the relation |
| 180 | for (let i = 0; i + 1 < vals.length; i++) { |
| 181 | const a = vals[i]!; |
| 182 | const b = vals[i + 1]!; |
| 183 | const scale = Math.max(absC(a), absC(b), 1); |
| 184 | const closeTo = approxEqual(a, b); |
| 185 | let r: InstanceOutcome; |
no test coverage detected