Skip to content

Commit ff8137d

Browse files
committed
Fix bugs from fuzz testing
- Add division by zero handling in `Algebra` and `Arithmetic` - Fix edge cases in `IntegerFunctions` bit manipulation - Refine gamma and logarithm computations in numeric classes - Improve legend handling in `ECharts` for dynamic series - Update `ListLinePlot3D` to use evaluated values for plotting - Improve `ArrayPlot` handling for sparse arrays and matrix inputs
1 parent b390092 commit ff8137d

File tree

21 files changed

+209
-55
lines changed

21 files changed

+209
-55
lines changed

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/Algebra.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4563,13 +4563,17 @@ private static IExpr togetherPlus(IAST plusAST) {
45634563

45644564
IExpr exprNumerator = F.evalExpand(numerator.oneIdentity0());
45654565
IExpr denom = F.eval(denominatorList.oneIdentity1());
4566-
IExpr exprDenominator = F.evalExpand(denom);
4567-
if (exprNumerator.isZero()) {
4568-
if (exprDenominator.isZero()) {
4569-
// let the standard evaluation handle the division by zero 0^0
4570-
return F.Times(exprNumerator, F.Power(exprDenominator, F.CN1));
4566+
final IExpr exprDenominator = F.evalExpand(denom);
4567+
if (exprNumerator.isNumber()) {
4568+
if (exprNumerator.isZero()) {
4569+
if (exprDenominator.isZero()) {
4570+
// let the standard evaluation handle the division by zero 0^0
4571+
return F.Times(exprNumerator, F.Power(exprDenominator, F.CN1));
4572+
}
4573+
return F.C0;
4574+
} else if (exprDenominator.isZero()) {
4575+
return Arithmetic.printInfy(S.Divide, exprNumerator, exprDenominator);
45714576
}
4572-
return F.C0;
45734577
}
45744578
if (!exprDenominator.isOne()) {
45754579
try {

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/Arithmetic.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7342,4 +7342,19 @@ public static void initialize() {
73427342

73437343
private Arithmetic() {}
73447344

7345+
/**
7346+
* Print message <code>Infinite expression `nonZeroNumerator/zeroDenominator` encountered</code>
7347+
* an return {@link F#CComplexInfinity} as result.
7348+
*
7349+
* @param head the head symbol which should be printed in the message
7350+
* @param nonZeroNumerator the non-zero numerator expression
7351+
* @param zeroDenominator the expression which represents <code>0</code>
7352+
* @return
7353+
*/
7354+
static IExpr printInfy(ISymbol head, final IExpr nonZeroNumerator, final IExpr zeroDenominator) {
7355+
// Infinite expression `1` encountered.
7356+
Errors.printMessage(head, "infy", F.list(F.Divide(nonZeroNumerator, zeroDenominator)));
7357+
return F.CComplexInfinity;
7358+
}
7359+
73457360
}

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/BesselFunctions.java

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.matheclipse.core.expression.S;
2626
import org.matheclipse.core.interfaces.IAST;
2727
import org.matheclipse.core.interfaces.IExpr;
28+
import org.matheclipse.core.interfaces.IInexactNumber;
2829
import org.matheclipse.core.interfaces.ISymbol;
2930
import com.google.common.math.IntMath;
3031

@@ -1561,6 +1562,26 @@ public void setUp(final ISymbol newSymbol) {
15611562
}
15621563

15631564
private static final class WeberE extends AbstractFunctionEvaluator implements IFunctionExpand {
1565+
@Override
1566+
public IExpr numericFunction(IAST ast, final EvalEngine engine) {
1567+
if (ast.argSize() == 2) {
1568+
IInexactNumber n = (IInexactNumber) ast.arg1();
1569+
IInexactNumber z = (IInexactNumber) ast.arg2();
1570+
IExpr result = weber2(n, z, true);
1571+
if (result.isPresent()) {
1572+
return engine.evaluate(result);
1573+
}
1574+
return weber2Numeric(n, z, engine.isNumericMode()).eval(engine);
1575+
}
1576+
if (ast.argSize() == 3) {
1577+
IInexactNumber n = (IInexactNumber) ast.arg1();
1578+
IInexactNumber m = (IInexactNumber) ast.arg2();
1579+
IInexactNumber z = (IInexactNumber) ast.arg3();
1580+
return weber3Numeric(n, m, z, engine.isNumericMode()).eval(engine);
1581+
}
1582+
return F.NIL;
1583+
}
1584+
15641585
@Override
15651586
public IExpr functionExpand(final IAST ast, EvalEngine engine) {
15661587
IExpr a = ast.arg1();
@@ -1636,10 +1657,26 @@ public IExpr evaluate(final IAST ast, EvalEngine engine) {
16361657
}
16371658
IExpr m = ast.arg2();
16381659
IExpr z = ast.arg3();
1639-
return weber3(n, m, z, engine.isNumericMode()).eval(engine);
1660+
return weber3Numeric(n, m, z, engine.isNumericMode()).eval(engine);
16401661
}
16411662

1642-
private static IExpr weber3(final IExpr n, IExpr m, IExpr z, boolean numericMode) {
1663+
private static IExpr weber2Numeric(final IExpr n, final IExpr z, boolean numericMode) {
1664+
if (numericMode) {
1665+
if (n.isNumber() && z.isNumber()) {
1666+
try {
1667+
return functionExpand2(n, z);
1668+
// return FunctionExpand.callMatcher(F.FunctionExpand(ast), ast, engine);
1669+
1670+
} catch (RuntimeException rex) {
1671+
Errors.rethrowsInterruptException(rex);
1672+
return Errors.printMessage(S.WeberE, rex);
1673+
}
1674+
}
1675+
}
1676+
return F.NIL;
1677+
}
1678+
1679+
private static IExpr weber3Numeric(final IExpr n, IExpr m, IExpr z, boolean numericMode) {
16431680
if (numericMode) {
16441681
if (n.isNumber() && m.isNumber() && z.isNumber()) {
16451682
try {
@@ -1695,18 +1732,18 @@ private static IExpr weber2(final IExpr n, final IExpr z, boolean numericMode) {
16951732
}
16961733
}
16971734

1698-
if (numericMode) {
1699-
if (n.isNumber() && z.isNumber()) {
1700-
try {
1701-
return functionExpand2(n, z);
1702-
// return FunctionExpand.callMatcher(F.FunctionExpand(ast), ast, engine);
1703-
1704-
} catch (RuntimeException rex) {
1705-
Errors.rethrowsInterruptException(rex);
1706-
return Errors.printMessage(S.WeberE, rex);
1707-
}
1708-
}
1709-
}
1735+
// if (numericMode) {
1736+
// if (n.isNumber() && z.isNumber()) {
1737+
// try {
1738+
// return functionExpand2(n, z);
1739+
// // return FunctionExpand.callMatcher(F.FunctionExpand(ast), ast, engine);
1740+
//
1741+
// } catch (RuntimeException rex) {
1742+
// Errors.rethrowsInterruptException(rex);
1743+
// return Errors.printMessage(S.WeberE, rex);
1744+
// }
1745+
// }
1746+
// }
17101747
return F.NIL;
17111748
}
17121749

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/IntegerFunctions.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -429,19 +429,25 @@ public IExpr evaluate(final IAST ast, EvalEngine engine) {
429429
}
430430
IInteger value = (IInteger) ast.arg1();
431431
BigInteger big = value.toBigNumerator();
432+
433+
final int bit;
432434
if (power2_K >= 0) {
433-
return F.ZZ(big.flipBit(power2_K));
435+
bit = power2_K;
434436
} else if (power2_K < 0 && power2_K > Integer.MIN_VALUE) {
435-
int bit = big.bitLength() + power2_K;
437+
bit = big.bitLength() + power2_K;
436438
if (bit < 0) {
437439
return value;
438440
}
439-
try {
441+
} else {
442+
bit = Integer.MIN_VALUE;
443+
}
444+
try {
445+
if (bit != Integer.MIN_VALUE) {
440446
return F.ZZ(big.flipBit(bit));
441-
} catch (ArithmeticException ae) {
442-
// BigInteger#flipBit() may throw ArithmeticException
443-
return Errors.printMessage(S.BitFlip, ae, engine);
444447
}
448+
} catch (ArithmeticException ae) {
449+
// BigInteger#flipBit() may throw ArithmeticException
450+
return Errors.printMessage(S.BitFlip, ae, engine);
445451
}
446452
}
447453
return F.NIL;

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/ListFunctions.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5560,9 +5560,8 @@ public IExpr evaluate(final IAST ast, EvalEngine engine) {
55605560
}
55615561
if (ast.isAST3()) {
55625562
if (ast.arg3().isZero()) {
5563-
// Infinite expression `1` encountered.
5564-
return Errors.printMessage(ast.topHead(), "infy", F.list(F.Divide(ast.arg2(), F.C0)),
5565-
engine);
5563+
Arithmetic.printInfy(ast.topHead(), ast.arg2(), ast.arg3());
5564+
return F.NIL;
55665565
}
55675566
if (ast.arg3().isDirectedInfinity()) {
55685567
return ast.arg1();

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/EvalEngine.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.logging.log4j.Level;
2525
import org.apfloat.ApfloatInterruptedException;
2626
import org.apfloat.FixedPrecisionApfloatHelper;
27+
import org.apfloat.internal.BackingStorageException;
2728
import org.hipparchus.complex.Complex;
2829
import org.matheclipse.core.basic.Config;
2930
import org.matheclipse.core.builtin.Arithmetic;
@@ -1160,7 +1161,11 @@ private IExpr numericFunction(final ISymbol symbol, final IAST ast) {
11601161
return Errors.printMessage(ast.topHead(), ve, this);
11611162
} catch (FlowControlException e) {
11621163
throw e;
1163-
} catch (SymjaMathException ve) {
1164+
} catch (NumberFormatException nfe) {
1165+
// this happens if org.apfloat.internal.LongApfloatImpl throws
1166+
// java.lang.NumberFormatException "Infinity is not a valid number"
1167+
return Errors.printMessage(ast.topHead(), nfe, this);
1168+
} catch (BackingStorageException | SymjaMathException ve) {
11641169
return Errors.printMessage(ast.topHead(), ve, this);
11651170
}
11661171
}

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/ExprEvaluator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.concurrent.Executors;
99
import java.util.concurrent.TimeUnit;
1010
import org.apfloat.ApfloatInterruptedException;
11+
import org.apfloat.internal.BackingStorageException;
1112
import org.hipparchus.complex.Complex;
1213
import org.matheclipse.core.eval.exception.AbortException;
1314
import org.matheclipse.core.eval.exception.BreakException;
@@ -356,7 +357,7 @@ public static IExpr evalTopLevel(final IExpr expr, EvalEngine[] engineRef) {
356357
} catch (final SyntaxError e) { // catches parser errors
357358
// LOGGER.debug("syntax error", e);
358359
return F.stringx(e.getMessage());
359-
} catch (SymjaMathException sma) {
360+
} catch (BackingStorageException | SymjaMathException sma) {
360361
// sma.printStackTrace();
361362
// LOGGER.debug("ExprEvaluator.evalTopLevel() failed", sma);
362363
Errors.printMessage(expr.topHead(), sma, engineRef[0]);

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/expression/ApcomplexNum.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,13 @@ public IExpr factorial() {
766766
try {
767767
FixedPrecisionApfloatHelper h = EvalEngine.getApfloat();
768768
return valueOf(h.gamma(h.add(fApcomplex, Apfloat.ONE)));
769+
} catch (ApfloatArithmeticException aaex) {
770+
if ("gamma.ofZero".equals(aaex.getLocalizationKey())) {
771+
return F.ComplexInfinity;
772+
}
773+
if ("gamma.ofNegativeInteger".equals(aaex.getLocalizationKey())) {
774+
return F.ComplexInfinity;
775+
}
769776
} catch (ArithmeticException | NumericComputationException e) {
770777
// try as computation with complex numbers
771778
}
@@ -1294,8 +1301,19 @@ public ApcomplexNum log() {
12941301
@Override
12951302
public IExpr log(final IExpr base) {
12961303
if (base instanceof INumber) {
1297-
return ApcomplexNum.valueOf(
1298-
EvalEngine.getApfloat().log(apcomplexValue(), ((INumber) base).apcomplexValue()));
1304+
if (base.isZero()) {
1305+
return S.Indeterminate;
1306+
}
1307+
try {
1308+
return valueOf(EvalEngine.getApfloat().log(fApcomplex, ((INumber) base).apcomplexValue()));
1309+
} catch (ApfloatArithmeticException aex) {
1310+
if (aex.getLocalizationKey().equals("divide.byZero")) {
1311+
// log(x,0) is undefined
1312+
return F.CComplexInfinity;
1313+
}
1314+
}
1315+
// return ApcomplexNum.valueOf(
1316+
// EvalEngine.getApfloat().log(apcomplexValue(), ((INumber) base).apcomplexValue()));
12991317
}
13001318
return IComplexNum.super.log(base);
13011319
}

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/expression/ApfloatNum.java

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.apfloat.ApcomplexMath;
77
import org.apfloat.Apfloat;
88
import org.apfloat.ApfloatArithmeticException;
9+
// import org.apfloat.ApfloatArithmeticException;
910
import org.apfloat.ApfloatMath;
1011
import org.apfloat.Apint;
1112
import org.apfloat.FixedPrecisionApfloatHelper;
@@ -303,6 +304,10 @@ public IExpr besselJ(IExpr arg2) {
303304
Apfloat besselJ =
304305
EvalEngine.getApfloat().besselJ(apfloatValue(), ((IReal) arg2).apfloatValue());
305306
return F.num(besselJ);
307+
} catch (LossOfPrecisionException lopex) {
308+
if (lopex.getLocalizationKey().equals("lossOfPrecision")) {
309+
return F.NIL;
310+
}
306311
} catch (ArithmeticException aex) {
307312
// result would be complex exception
308313
}
@@ -811,7 +816,17 @@ public IExpr factorial() {
811816
} catch (ArithmeticException | NumericComputationException e) {
812817
// try as computation with complex numbers
813818
}
814-
return F.complexNum(EvalEngine.getApfloat().gamma(apcomplexValue().add(Apfloat.ONE)));
819+
try {
820+
return F.complexNum(EvalEngine.getApfloat().gamma(apcomplexValue().add(Apfloat.ONE)));
821+
} catch (ApfloatArithmeticException aaex) {
822+
if ("gamma.ofZero".equals(aaex.getLocalizationKey())) {
823+
return F.ComplexInfinity;
824+
}
825+
if ("gamma.ofNegativeInteger".equals(aaex.getLocalizationKey())) {
826+
return F.ComplexInfinity;
827+
}
828+
}
829+
return INum.super.factorial();
815830
}
816831

817832
@Override
@@ -1536,15 +1551,25 @@ public IInexactNumber log() {
15361551
@Override
15371552
public IExpr log(final IExpr base) {
15381553
if (base instanceof INumber) {
1539-
if (base instanceof IReal) {
1540-
if (isNegative()) {
1541-
return ApcomplexNum.valueOf(
1542-
EvalEngine.getApfloat().log(apcomplexValue(), ((INumber) base).apcomplexValue()));
1554+
if (base.isZero()) {
1555+
return S.Indeterminate;
1556+
}
1557+
try {
1558+
if (base instanceof IReal) {
1559+
if (isNegative()) {
1560+
return ApcomplexNum.valueOf(
1561+
EvalEngine.getApfloat().log(apcomplexValue(), ((INumber) base).apcomplexValue()));
1562+
}
1563+
return valueOf(EvalEngine.getApfloat().log(fApfloat, ((IReal) base).apfloatValue()));
1564+
}
1565+
return ApcomplexNum.valueOf(
1566+
EvalEngine.getApfloat().log(apcomplexValue(), ((INumber) base).apcomplexValue()));
1567+
} catch (ApfloatArithmeticException aex) {
1568+
if (aex.getLocalizationKey().equals("divide.byZero")) {
1569+
// log(x,0) is undefined
1570+
return F.CComplexInfinity;
15431571
}
1544-
return valueOf(EvalEngine.getApfloat().log(fApfloat, ((IReal) base).apfloatValue()));
15451572
}
1546-
return ApcomplexNum.valueOf(
1547-
EvalEngine.getApfloat().log(apcomplexValue(), ((INumber) base).apcomplexValue()));
15481573
}
15491574
return INum.super.log(base);
15501575
}

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/expression/ComplexNum.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,9 @@ public IExpr expIntegralEi() {
817817

818818
@Override
819819
public IExpr factorial() {
820+
if (isMathematicalIntegerNegative()) {
821+
return F.CComplexInfinity;
822+
}
820823
try {
821824
return F.complexNum(Arithmetic.lanczosApproxGamma(fComplex.add(Complex.ONE)));
822825
} catch (ArithmeticException | NumericComputationException e) {

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/expression/Num.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ public IExpr besselJ(IExpr arg2) {
318318
Apfloat besselJ =
319319
EvalEngine.getApfloatDouble().besselJ(apfloatValue(), ((IReal) arg2).apfloatValue());
320320
return F.num(besselJ.doubleValue());
321+
} catch (LossOfPrecisionException lopex) {
322+
if (lopex.getLocalizationKey().equals("lossOfPrecision")) {
323+
return F.NIL;
324+
}
321325
} catch (ArithmeticException aex) {
322326
// result would be complex exception
323327
}
@@ -865,6 +869,9 @@ public static double factorial(final double arg) {
865869

866870
@Override
867871
public IExpr factorial() {
872+
if (isMathematicalIntegerNegative()) {
873+
return F.CComplexInfinity;
874+
}
868875
return valueOf(factorial(value));
869876
}
870877

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/graphics/ECharts.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ public static void seriesData(StringBuilder yAxisSeriesBuffer, IAST listOfLists,
134134
// yAxisString.append(" step: '" + step + i + "',\n");
135135
stepI = step + i;
136136
}
137-
ECharts.yAxisSingleSeries(yAxisString, singlePointList, legends[i - 1], type, stepI, minMax);
137+
String legend = "";
138+
if (legends.length > i - 1) {
139+
legend = legends[i - 1];
140+
}
141+
ECharts.yAxisSingleSeries(yAxisString, singlePointList, legend, type, stepI, minMax);
138142
yAxisSeriesBuffer.append(yAxisString);
139143
if (i < listOfLists.argSize()) {
140144
yAxisSeriesBuffer.append(",\n");

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/interfaces/IAST.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,7 @@ default IAST getItems(int[] items, int length) {
11671167
* @param positions index of the element to return
11681168
* @return the element at the specified positions in this nested AST or {@link F#NIL}
11691169
* @throws IndexOutOfBoundsException if one of the positions are out of range
1170+
* @see ITensorAccess#getIndex(int...) which gets the value at the full index of a tensor
11701171
*/
11711172
public IExpr getPart(final int... positions) throws IndexOutOfBoundsException;
11721173

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/reflection/system/ListLinePlot3D.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,10 @@ private IExpr heightLinePlot(IAST heights, IAST plotStyle, EvalEngine engine) {
129129
IASTAppendable lineList = F.ListAlloc(rowListSize);
130130

131131
for (int j = 1; j < rowListSize; j++) {
132+
double value = rowList.get(j).evalf();
132133
// ListLinePlot3D size is 2.5 × 2.5 × 1 independently from its coordinates
133134
lineList.append(F.List(F.num(i * 2.5 / valuesSize), F.num(j * 2.5 / rowListSize),
134-
rowList.get(j).divide(deltaHeight)));
135+
F.num(value / deltaHeight)));
135136
}
136137

137138
final IAST color = GraphicsOptions.plotStyleColorExpr(lineColorNumber++, plotStyle);

0 commit comments

Comments
 (0)