diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/Algebra.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/Algebra.java
index 0328e91d85..5d8a7a5607 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/Algebra.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/Algebra.java
@@ -4795,8 +4795,10 @@ private static IAST cancelCommonFactors(IExpr part0, IExpr part1) {
}
}
if (p0.isTimes() || p1.isTimes()) {
- final IAST p0Times = p0.isTimes() ? (IAST) p0 : F.Times(p0);
- final IAST p1Times = p1.isTimes() ? (IAST) p1 : F.Times(p1);
+ IAST p0Times = AbstractFunctionEvaluator.getNegativePlusInTimes(p0);
+ IAST p1Times = AbstractFunctionEvaluator.getNegativePlusInTimes(p1);
+ // IAST p0Times = p0.isTimes() ? (IAST) p0 : F.Times(p0);
+ // IAST p1Times = p1.isTimes() ? (IAST) p1 : F.Times(p0);
IASTAppendable t0 = p0Times.copyAppendable();
IASTAppendable t1 = p1Times.copyAppendable();
int i = 1;
@@ -4840,8 +4842,7 @@ private static IAST cancelCommonFactors(IExpr part0, IExpr part1) {
t1Base = t1Arg.base();
t1Exponent = t1Arg.exponent();
}
-
- if (t0Base.equals(t1Base) && t0Exponent.isReal() && t1Exponent.isReal()) {
+ if (t0Exponent.isReal() && t1Exponent.isReal() && t0Base.equals(t1Base)) {
IReal exp0 = (IReal) t0Exponent;
IReal exp1 = (IReal) t1Exponent;
final IReal subtracted;
@@ -4862,6 +4863,7 @@ private static IAST cancelCommonFactors(IExpr part0, IExpr part1) {
subtracted = exp1.subtractFrom(exp0);
t0.remove(i);
t1.set(j, F.Power(t1Base, subtracted));
+ j++;
if (Config.TRACE_BASIC_ARITHMETIC) {
if (EvalEngine.get().isTraceMode()) {
if (commonFactors.isNIL()) {
@@ -4900,6 +4902,7 @@ private static IAST cancelCommonFactors(IExpr part0, IExpr part1) {
}
}
return F.NIL;
+
}
/**
@@ -5315,17 +5318,23 @@ private static IRational factorTermsGCD(IAST plusAST, EvalEngine engine) {
*/
public static IExpr[] getNumeratorDenominator(IAST ast, EvalEngine engine, boolean together) {
if (together) {
- IExpr[] result = new IExpr[3];
- result[2] = together(ast, engine);
- // split expr into numerator and denominator
- result[1] = engine.evaluate(F.Denominator(result[2]));
- if (!result[1].isOne()) {
- // search roots for the numerator expression
- result[0] = engine.evaluate(F.Numerator(result[2]));
- } else {
- result[0] = ast; // result[2];
+ boolean noSimplifyMode = engine.isNoSimplifyMode();
+ try {
+ engine.setNoSimplifyMode(true);
+ IExpr[] result = new IExpr[3];
+ result[2] = together(ast, engine);
+ // split expr into numerator and denominator
+ result[1] = engine.evaluate(F.Denominator(result[2]));
+ if (!result[1].isOne()) {
+ // search roots for the numerator expression
+ result[0] = engine.evaluate(F.Numerator(result[2]));
+ } else {
+ result[0] = ast; // result[2];
+ }
+ return result;
+ } finally {
+ engine.setNoSimplifyMode(noSimplifyMode);
}
- return result;
}
IExpr[] result = new IExpr[2];
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/EvalEngine.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/EvalEngine.java
index 846f0d6f77..2349fefc58 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/EvalEngine.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/EvalEngine.java
@@ -227,11 +227,16 @@ public static void setReset(final EvalEngine engine) {
transient boolean fNumericMode;
/**
- * if true
the engine evaluates in "F.Together(expr)" in IExpr#times()
- * method.
+ * if true
the engine evaluates "F.Together(expr)" in IExpr#times() method.
*/
transient boolean fTogetherMode;
+ /**
+ * If true
the engine does no simplification of "negative {@link S#Plus}
+ * expressions" inside {@link S#Times} expressions in common subexpression determining.
+ */
+ transient boolean fNoSimplifyMode;
+
transient boolean fEvalLHSMode;
transient boolean fEvalRHSMode;
@@ -601,6 +606,7 @@ public synchronized EvalEngine copy() {
engine.fSessionID = fSessionID;
engine.fStopRequested = false;
engine.fTogetherMode = fTogetherMode;
+ engine.fNoSimplifyMode = fNoSimplifyMode;
engine.fTraceMode = fTraceMode;
engine.fTraceStack = fTraceStack;
engine.f$Input = f$Input;
@@ -2982,6 +2988,7 @@ public final void init() {
fRecursionCounter = 0;
fNumericMode = false;
fTogetherMode = false;
+ fNoSimplifyMode = false;
fEvalLHSMode = false;
fEvalRHSMode = false;
fDisabledTrigRules = false;
@@ -3146,6 +3153,15 @@ public final boolean isTogetherMode() {
return fTogetherMode;
}
+ /**
+ * If true
the engine does no simplification of "negative {@link S#Plus}
+ * expressions" inside {@link S#Times} expressions in common subexpression determining.
+ *
+ */
+ public final boolean isNoSimplifyMode() {
+ return fNoSimplifyMode;
+ }
+
/**
* If the trace mode is set the system writes an evaluation trace list or if additionally the
* stop after evaluation mode is set returns the first evaluated result.
@@ -3213,6 +3229,7 @@ private void reset() {
fDisabledTrigRules = false;
fRecursionCounter = 0;
fTogetherMode = false;
+ fNoSimplifyMode = false;
fTraceMode = false;
fTraceStack = null;
fStopRequested = false;
@@ -3495,12 +3512,22 @@ public void setStopRequested(final boolean stopRequested) {
* {@link S#Together} command during the evaluation of the multiplication if the parameter is set
* to true
.
*
- * @param fTogetherMode if true
the evaluation will be wrapped by a
- * {@link S#Together} function in basic multiplication
+ * @param togetherMode if true
the evaluation will be wrapped by a {@link S#Together}
+ * function in basic multiplication
* @see #isTogetherMode()
*/
- public void setTogetherMode(boolean fTogetherMode) {
- this.fTogetherMode = fTogetherMode;
+ public void setTogetherMode(boolean togetherMode) {
+ this.fTogetherMode = togetherMode;
+ }
+
+ /**
+ * If set to true
the engine does no simplification of "negative {@link S#Plus}
+ * expressions" inside {@link S#Times} expressions in common subexpression determining.
+ *
+ * @param noSimplifyMode
+ */
+ public void setNoSimplifyMode(boolean noSimplifyMode) {
+ this.fNoSimplifyMode = noSimplifyMode;
}
/** @param b */
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/interfaces/AbstractFunctionEvaluator.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/interfaces/AbstractFunctionEvaluator.java
index efe62dbf70..ffa9f828a0 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/interfaces/AbstractFunctionEvaluator.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/interfaces/AbstractFunctionEvaluator.java
@@ -31,6 +31,127 @@
public abstract class AbstractFunctionEvaluator extends AbstractEvaluator {
private static final Logger LOGGER = LogManager.getLogger();
+ /**
+ * Determine the options only from the last arguments of ast
. Possibly additional
+ * options are null
if no matching option is found in the arguments of the ast
+ *
.
+ *
+ * @param options
+ * @param ast
+ * @param argSize
+ * @param expectedArgSize
+ * @param optionSymbol
+ * @return
+ */
+ private static int determineArgumentOptions(IExpr[] options, IAST ast, int argSize,
+ int[] expectedArgSize, IBuiltInSymbol[] optionSymbol) {
+ int minNumberOfArgs = 1;
+ if (expectedArgSize != null) {
+ // the ast function must at least contain the minimum number of arguments
+ minNumberOfArgs = expectedArgSize[0];
+ if (minNumberOfArgs < 1) {
+ minNumberOfArgs = 1;
+ }
+ }
+
+ int counter = 0;
+ boolean evaled = true;
+ while (argSize > minNumberOfArgs && evaled && counter < optionSymbol.length) {
+ IExpr arg = ast.get(argSize);
+
+ // check that arg has the correct options format:
+ if (arg.isRule()) {
+ evaled = false;
+ for (int i = 0; i < optionSymbol.length; i++) {
+ if (optionSymbol[i].equals(arg.first())) {
+ options[i] = arg.second();
+ argSize--;
+ counter++;
+ evaled = true;
+ break;
+ }
+ }
+ } else if (arg.isListOfRules(true) && !arg.isEmptyList()) {
+ IAST listOfRules = (IAST) arg;
+ for (int j = 1; j < listOfRules.size(); j++) {
+ IAST rule = (IAST) listOfRules.get(j);
+ evaled = false;
+ for (int i = 0; i < optionSymbol.length; i++) {
+ if (optionSymbol[i].equals(rule.first())) {
+ options[i] = rule.second();
+ argSize--;
+ counter++;
+ evaled = true;
+ break;
+ }
+ }
+ }
+ } else {
+ evaled = false;
+ break;
+ }
+ }
+ return argSize;
+ }
+
+ /**
+ * Replace the options which are null
only from the default options of the head
+ * symbol.
+ *
+ * @param options
+ * @param ast
+ * @param optionSymbol
+ * @param engine
+ */
+ private static void determineDefaultOptions(IExpr[] options, IAST ast,
+ IBuiltInSymbol[] optionSymbol, EvalEngine engine) {
+ int optionNullStart = -1;
+ for (int i = 0; i < options.length; i++) {
+ if (options[i] == null) {
+ optionNullStart = i;
+ break;
+ }
+ }
+ if (optionNullStart >= 0) {
+ final IExpr temp = OptionsPattern.optionsList(ast.topHead(), false);
+ // final IExpr temp = engine.evaluate(F.Options(ast.topHead()));
+ if (temp.isList() && temp.size() > 1) {
+ IAST list = (IAST) temp;
+ for (int i = optionNullStart; i < options.length; i++) {
+ if (options[i] == null) {
+ options[i] = S.None;
+ for (int j = 1; j < list.size(); j++) {
+ IAST rule = (IAST) list.get(j);
+ if (optionSymbol[i].equals(rule.first())) {
+ options[i] = rule.second();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine the options from the last arguments of ast
or from the default options.
+ *
+ * @param options the result array of options and their (possibly default) values.
+ * @param ast
+ * @param argSize
+ * @param expectedArgSize
+ * @param optionSymbol
+ * @param engine
+ * @return
+ */
+ public static int determineOptions(IExpr[] options, IAST ast, int argSize, int[] expectedArgSize,
+ IBuiltInSymbol[] optionSymbol, EvalEngine engine) {
+ argSize = determineArgumentOptions(options, ast, argSize, expectedArgSize, optionSymbol);
+
+ determineDefaultOptions(options, ast, optionSymbol, engine);
+ return argSize;
+ }
+
/**
* Check if the expression has a complex number factor I (imaginary unit).
*
@@ -79,6 +200,33 @@ public static IExpr extractImaginaryUnit(final IExpr expression, boolean checkTi
return F.NIL;
}
+ public static IExpr getComplexExpr(final IExpr expr, IExpr factor) {
+ if (expr.isComplex() && (expr.re().isZero() || expr.re().isNegative())) {
+ return F.Times(factor, expr);
+ } else {
+ if (expr.isTimes() && expr.first().isComplex()) {
+ IComplex arg1 = (IComplex) expr.first();
+ if (arg1.re().isZero() || arg1.re().isNegative()) {
+ return F.Times(factor, expr);
+ }
+ } else if (expr.isPlus()) {
+ IExpr arg1 = expr.first();
+ if (arg1.isComplex() && (arg1.re().isZero() || arg1.re().isNegative())) {
+ // distribute the factor over the Plus() args
+ return F.Distribute(F.Times(factor, expr));
+ }
+ if (arg1.isTimes() && arg1.first().isComplex()) {
+ arg1 = arg1.first();
+ if (arg1.re().isZero() || arg1.re().isNegative()) {
+ // distribute the factor over the Plus() args
+ return F.Distribute(F.Times(factor, expr));
+ }
+ }
+ }
+ }
+ return F.NIL;
+ }
+
/**
* Check if the expression is canonical negative.
*
@@ -127,56 +275,7 @@ public static IExpr getNormalizedNegativeExpression(final IExpr expression,
return timesAST.setAtCopy(index, F.CInfinity);
}
} else if (checkTimesPlus && expression.isPlus()) {
- final IAST plusAST = ((IAST) expression);
- IExpr arg1 = plusAST.arg1();
- if (arg1.isNumber()) {
- if (((INumber) arg1).complexSign() < 0) {
- result = plusAST.copy();
- result.set(1, arg1.negate());
- for (int i = 2; i < plusAST.size(); i++) {
- result.set(i, plusAST.get(i).negate());
- }
- return result;
- }
- } else if (arg1.isTimes()) {
- IExpr arg1Negated = getNormalizedNegativeExpression(arg1, checkTimesPlus);
- if (arg1Negated.isPresent()) {
- // int positiveElementsCounter = 0;
- result = plusAST.copy();
- result.set(1, arg1Negated);
- for (int i = 2; i < plusAST.size(); i++) {
- IExpr temp = plusAST.get(i);
- // if (!temp.isTimes() && !temp.isPower()) {
- // return F.NIL;
- // }
-
- // arg1Negated = getNormalizedNegativeExpression(temp, checkTimesPlus);
- // if (arg1Negated.isPresent()) {
- // result.set(i, arg1Negated);
- // } else {
-
- // positiveElementsCounter++;
- // if (positiveElementsCounter * 2 > plusAST.argSize()) {
- // number of positive elements is greater
- // than number of negative elements
- // return F.NIL;
- // }
- result.set(i, temp.negate());
-
- // }
- }
- return result;
- } else if (arg1.isNegativeInfinity()) {
- result = plusAST.copy();
- result.set(1, F.CInfinity);
- for (int i = 2; i < plusAST.size(); i++) {
- result.set(i, plusAST.get(i).negate());
- }
- return result;
- }
- }
- // } else if (expression.isNegativeInfinity()) {
- // return F.CInfinity;
+ return getNormalizedNegativePlus((IAST) expression, checkTimesPlus);
} else if (expression.isDirectedInfinity() && expression.isAST1()) {
IExpr arg1 = expression.first();
if (arg1.isMinusOne()) {
@@ -194,82 +293,109 @@ public static IExpr getNormalizedNegativeExpression(final IExpr expression,
return F.NIL;
}
- /**
- * Return true
if the number of negative terms of the ast
expression
- * countWeight
is greater than the half of the number of the arguments of ast
- *
. I.e. int halfSize = ast.size() / 2; return countWeight > halfSize;
- *
- * @param ast
- * @param checkTimesPlus check Times(...)
and Plus(...)
expressions
- * @return
- */
- public static boolean isNegativeWeighted(final IAST ast, boolean checkTimesPlus) {
- return isNegativeWeighted(ast, checkTimesPlus, ast.size() / 2);
- }
-
- /**
- * Return the number of negative terms of the ast
expression.
- *
- * @param ast
- * @param checkTimesPlus check Times(...)
and Plus(...)
expressions
- * @param maxNegativeExpr maximum number of negative valued terms which have to be found before
- * returning true
- * @return
- */
- public static boolean isNegativeWeighted(final IAST ast, boolean checkTimesPlus,
- int maxNegativeExpr) {
- int count = maxNegativeExpr - 1;
- for (int i = 1; i < ast.size(); i++) {
- if (isNegativeValued(ast.get(i), checkTimesPlus)) {
- if (--count < 0) {
- return true;
+ private static IExpr getNormalizedNegativePlus(final IAST plusAST, boolean checkTimesPlus) {
+ IASTMutable result;
+ IExpr arg1 = plusAST.arg1();
+ if (arg1.isNumber()) {
+ if (((INumber) arg1).complexSign() < 0) {
+ result = plusAST.copy();
+ result.set(1, arg1.negate());
+ for (int i = 2; i < plusAST.size(); i++) {
+ result.set(i, plusAST.get(i).negate());
+ }
+ return result;
+ }
+ } else if (arg1.isTimes()) {
+ IExpr arg1Negated = getNormalizedNegativeExpression(arg1, checkTimesPlus);
+ if (arg1Negated.isPresent()) {
+ // int positiveElementsCounter = 0;
+ result = plusAST.copy();
+ result.set(1, arg1Negated);
+ for (int i = 2; i < plusAST.size(); i++) {
+ IExpr temp = plusAST.get(i);
+ // if (!temp.isTimes() && !temp.isPower()) {
+ // return F.NIL;
+ // }
+
+ // arg1Negated = getNormalizedNegativeExpression(temp, checkTimesPlus);
+ // if (arg1Negated.isPresent()) {
+ // result.set(i, arg1Negated);
+ // } else {
+
+ // positiveElementsCounter++;
+ // if (positiveElementsCounter * 2 > plusAST.argSize()) {
+ // number of positive elements is greater
+ // than number of negative elements
+ // return F.NIL;
+ // }
+ result.set(i, temp.negate());
+
+ // }
+ }
+ return result;
+ } else if (arg1.isNegativeInfinity()) {
+ result = plusAST.copy();
+ result.set(1, F.CInfinity);
+ for (int i = 2; i < plusAST.size(); i++) {
+ result.set(i, plusAST.get(i).negate());
}
+ return result;
}
}
- return false;
+ return F.NIL;
}
/**
- * Return true
if the expression
is considered having a negative
- * value
- *
- * @param expression
- * @param checkTimesPlus check Times(...)
and Plus(...)
expressions
- * @return
+ * Create a {@link S#Times} form from expr
and replace all occurrences of
+ * "negative" {@link S#Plus} expressions. Collect the "minus one" factors if
+ * necessary and insert the result at index 1
.
+ *
+ * @param expr
+ * @return the original expr in {@link S#Times} form, if no "negative" {@link S#Plus}
+ * expression could b found or a new {@link S#Times} form, which contains normalized
+ * {@link S#Plus} expressions
*/
- private static boolean isNegativeValued(final IExpr expression, boolean checkTimesPlus) {
- if (expression.isNumber()) {
- return ((INumber) expression).complexSign() < 0;
- } else if (expression.isAST()) {
- if (checkTimesPlus && expression.isTimes()) {
- IExpr arg1 = expression.first();
- // see github #110: checking for arg1.isNegative() will trigger infinite recursion!
- if (arg1.isNumber()) {
- if (((INumber) arg1).complexSign() < 0) {
- return true;
+ public static IAST getNegativePlusInTimes(IExpr expr) {
+ final IAST timesAST = expr.isTimes() ? (IAST) expr : F.Times(expr);
+ IASTAppendable timesAppendable = F.NIL;
+ if (EvalEngine.get().isNoSimplifyMode()) {
+ return timesAST;
+ }
+ INumber neg = F.C1;
+ for (int i = 1; i < timesAST.size(); i++) {
+ IExpr arg = timesAST.get(i);
+ if (arg.isPlus()) {
+ IExpr negativeExpr = getNormalizedNegativePlus((IAST) arg, true);
+ if (negativeExpr.isPresent()) {
+ if (timesAppendable.isNIL()) {
+ timesAppendable = timesAST.copyAppendable();
}
- } else if (arg1.isNegativeInfinity()) {
- return true;
+ timesAppendable.set(i, negativeExpr);
+ neg = neg.negate();
}
- } else if (checkTimesPlus && expression.isPlus()) {
- IAST plusAST = ((IAST) expression);
- IExpr arg1 = plusAST.arg1();
- if (arg1.isNumber()) {
- if (((INumber) arg1).complexSign() < 0) {
- return true;
+ } else if (arg.isPower() && arg.base().isPlus() && arg.exponent().isInteger()) {
+ long exponent = arg.exponent().toLongDefault();
+ if (exponent != Long.MIN_VALUE) {
+ IExpr negativeExpr = getNormalizedNegativePlus((IAST) arg.base(), true);
+ if (negativeExpr.isPresent()) {
+ if (timesAppendable.isNIL()) {
+ timesAppendable = timesAST.copyAppendable();
+ }
+ timesAppendable.set(i, F.Power(negativeExpr, arg.exponent()));
+ neg = neg.times(F.CN1.powerRational(exponent));
}
- } else if (arg1.isNegativeInfinity()
- || (arg1.isTimes() && isNegativeValued(arg1, checkTimesPlus))) {
- return true;
- }
- } else if (expression.isDirectedInfinity() && expression.isAST1()) {
- IExpr arg1 = expression.first();
- if (arg1.isMinusOne() || arg1.isNegativeImaginaryUnit()) {
- return true;
}
}
}
- return false;
+ if (neg.isMinusOne()) {
+ if (timesAppendable.arg1().isNumber()) {
+ timesAppendable.set(1, timesAppendable.arg1().negate());
+ } else {
+ timesAppendable.append(1, neg);
+ }
+ }
+
+ return timesAppendable.orElse(timesAST);
}
/**
@@ -315,140 +441,7 @@ public static IAST getPeriodicParts(final IExpr expr, final IExpr period) {
}
/**
- * Split plusAST into two parts, a "rest" and a multiple of Pi/2. This assumes plusAST to be an
- * Plus() expression. The multiple of Pi/2 returned in the second position is always a IRational
- * number.
- *
- * @param plusAST
- * @param engine
- * @return F.NIL
if no multiple is found.
- */
- public static IAST peelOff(final IAST plusAST, final EvalEngine engine) {
- IRational k = null;
- for (int i = 1; i < plusAST.size(); i++) {
- IExpr temp = plusAST.get(i);
- if (temp.equals(S.Pi)) {
- k = F.C1;
- break;
- }
- if (temp.isTimes2()) {
- if (temp.first().isRational() && temp.second().equals(S.Pi)) {
- k = (IRational) temp.first();
- break;
- }
- }
- }
- if (k != null) {
- IASTMutable result = F.binaryAST2(S.List, plusAST, F.C0);
- IExpr m1 = F.Times(k.mod(F.C1D2), S.Pi);
- IExpr m2 = S.Subtract.of(engine, F.Times(k, S.Pi), m1);
- result.set(1, S.Subtract.of(plusAST, m2));
- result.set(2, m2);
- return result;
- }
- return F.NIL;
- }
-
- /**
- * This method assumes plusAST to be a Plus() expression. The multiple of Pi returned is a
- * IRational number or assumed to be an expression with Integer result.
- *
- * @param plusAST
- * @param engine
- * @return F.NIL
if no multiple is found.
- */
- public static IExpr peelOffPlusRational(final IAST plusAST, final EvalEngine engine) {
- IExpr k = null;
- for (int i = 1; i < plusAST.size(); i++) {
- IExpr temp = plusAST.get(i);
- if (temp.equals(S.Pi)) {
- k = F.C1;
- return k;
- }
- if (temp.isTimes()) {
- IExpr peeled = peelOfTimes((IAST) temp, S.Pi);
- if (peeled.isPresent()) {
- if (peeled.isRational() || peeled.isIntegerResult()) {
- // k = temp.first();
- return peeled;
- }
- }
- }
- }
- return null;
- }
-
- /**
- * This assumes plusAST to be an Plus() expression. The multiple of Pi returned is a IRational
- * number or assumed to be an expression with Integer result.
- *
- * @param plusAST
- * @param engine
- * @return F.NIL
if no multiple is found, otherwise return
- * List(rational-number, argument)
- */
- public static IAST peelOffPlusI(final IAST plusAST, final EvalEngine engine) {
- for (int i = 1; i < plusAST.size(); i++) {
- final IExpr arg = plusAST.get(i);
- if (arg.isTimes()) {
- IExpr peeled = peelOfTimes((IAST) arg, S.Pi);
- if (peeled.isPresent()) {
- IExpr x = S.Times.of(F.CNI, peeled);
- if (x.isRational() || x.isIntegerResult()) {
- return F.list(x, arg);
- }
- }
- }
- }
- return F.NIL;
- }
-
- /**
- * Try to split a periodic part from the Times() expression: result == timesAST / period
- *
- *
- * @param astTimes
- * @param period
- * @return F.NIL
if no periodicity was found
- */
- public static IExpr peelOfTimes(final IAST astTimes, final IExpr period) {
- for (int i = 1; i < astTimes.size(); i++) {
- if (astTimes.get(i).equals(period)) {
- return astTimes.splice(i).oneIdentity1();
- }
- }
- return F.NIL;
- }
-
- /**
- * Try to split a periodic part from the Times() expression: result == timesAST / period
- *
- *
- * @param astTimes
- * @param period1
- * @param period2
- * @return F.NIL
if no periodicity was found
- */
- public static IExpr peelOfTimes(final IAST astTimes, final IExpr period1, final IExpr period2) {
- IASTAppendable result = F.NIL;
- for (int i = 1; i < astTimes.size(); i++) {
- if (astTimes.get(i).equals(period1)) {
- result = astTimes.copyAppendable();
- result.remove(i);
- for (int j = 1; j < result.size(); j++) {
- if (result.get(j).equals(period2)) {
- result.remove(j);
- return result;
- }
- }
- return F.NIL;
- }
- }
- return F.NIL;
- }
-
- /**
- * Check if the expression is canonical negative.
+ * Check if the expression is canonical negative.
*
* @param expression
* @param checkTimesPlus check Times(...)
and Plus(...)
expressions
@@ -572,33 +565,6 @@ public static IExpr getPureImaginaryPart(final IExpr expr) {
return F.NIL;
}
- public static IExpr getComplexExpr(final IExpr expr, IExpr factor) {
- if (expr.isComplex() && (expr.re().isZero() || expr.re().isNegative())) {
- return F.Times(factor, expr);
- } else {
- if (expr.isTimes() && expr.first().isComplex()) {
- IComplex arg1 = (IComplex) expr.first();
- if (arg1.re().isZero() || arg1.re().isNegative()) {
- return F.Times(factor, expr);
- }
- } else if (expr.isPlus()) {
- IExpr arg1 = expr.first();
- if (arg1.isComplex() && (arg1.re().isZero() || arg1.re().isNegative())) {
- // distribute the factor over the Plus() args
- return F.Distribute(F.Times(factor, expr));
- }
- if (arg1.isTimes() && arg1.first().isComplex()) {
- arg1 = arg1.first();
- if (arg1.re().isZero() || arg1.re().isNegative()) {
- // distribute the factor over the Plus() args
- return F.Distribute(F.Times(factor, expr));
- }
- }
- }
- }
- return F.NIL;
- }
-
public static IExpr imaginaryPart(final IExpr expr, boolean unequalsZero) {
IExpr imPart = S.Im.of(expr);
if (unequalsZero) {
@@ -612,19 +578,6 @@ public static IExpr imaginaryPart(final IExpr expr, boolean unequalsZero) {
return F.NIL;
}
- public static IExpr realPart(final IExpr expr, boolean unequalsZero) {
- IExpr rePart = S.Re.of(expr);
- if (unequalsZero) {
- if (rePart.isZero()) {
- return F.NIL;
- }
- }
- if (rePart.isNumber() || rePart.isFree(S.Re)) {
- return rePart;
- }
- return F.NIL;
- }
-
/**
* Initialize the serialized Rubi integration rules from ressource /ser/integrate.ser
* .
@@ -656,171 +609,274 @@ public static void initSerializedRules(final ISymbol symbol) {
}
}
- private static IExpr pureImaginaryPart(final IExpr expr) {
- if (expr.isComplex() && ((IComplex) expr).re().isZero()) {
- IComplex compl = (IComplex) expr;
- return compl.im();
- }
- if (expr.isTimes()) {
- IAST times = ((IAST) expr);
- IExpr arg1 = times.arg1();
- if (arg1.isComplex() && ((IComplex) arg1).re().isZero()) {
- return times.setAtCopy(1, ((IComplex) arg1).im());
- }
- }
- return F.NIL;
- }
-
/**
- * Create a rule which invokes the method name in this class instance.
+ * Return true
if the expression
is considered having a negative
+ * value
*
- * @param symbol
- * @param patternString
- * @param methodName
+ * @param expression
+ * @param checkTimesPlus check Times(...)
and Plus(...)
expressions
+ * @return
*/
- public void createRuleFromMethod(ISymbol symbol, String patternString, String methodName) {
- PatternMatcherAndInvoker pm = new PatternMatcherAndInvoker(patternString, this, methodName);
- symbol.putDownRule(pm);
- }
-
- /** {@inheritDoc} */
- @Override
- public abstract IExpr evaluate(final IAST ast, final EvalEngine engine);
-
- /** {@inheritDoc} */
- @Override
- public void setUp(final ISymbol newSymbol) {
-
- // F.SYMBOL_OBSERVER.createPredefinedSymbol(newSymbol.toString());
- if (Config.SERIALIZE_SYMBOLS && newSymbol.containsRules()) {
- try (
- FileOutputStream out =
- new FileOutputStream("c:\\temp\\ser\\" + newSymbol.getSymbolName() + ".ser");
- ObjectOutputStream oos = new ObjectOutputStream(out);) {
- newSymbol.writeRules(oos);
- } catch (IOException e) {
- LOGGER.error("AbstractFunctionEvaluator.setUp() failed", e);
+ private static boolean isNegativeValued(final IExpr expression, boolean checkTimesPlus) {
+ if (expression.isNumber()) {
+ return ((INumber) expression).complexSign() < 0;
+ } else if (expression.isAST()) {
+ if (checkTimesPlus && expression.isTimes()) {
+ IExpr arg1 = expression.first();
+ // see github #110: checking for arg1.isNegative() will trigger infinite recursion!
+ if (arg1.isNumber()) {
+ if (((INumber) arg1).complexSign() < 0) {
+ return true;
+ }
+ } else if (arg1.isNegativeInfinity()) {
+ return true;
+ }
+ } else if (checkTimesPlus && expression.isPlus()) {
+ IAST plusAST = ((IAST) expression);
+ IExpr arg1 = plusAST.arg1();
+ if (arg1.isNumber()) {
+ if (((INumber) arg1).complexSign() < 0) {
+ return true;
+ }
+ } else if (arg1.isNegativeInfinity()
+ || (arg1.isTimes() && isNegativeValued(arg1, checkTimesPlus))) {
+ return true;
+ }
+ } else if (expression.isDirectedInfinity() && expression.isAST1()) {
+ IExpr arg1 = expression.first();
+ if (arg1.isMinusOne() || arg1.isNegativeImaginaryUnit()) {
+ return true;
+ }
}
}
+ return false;
}
/**
- * Determine the options from the last arguments of ast
or from the default options.
+ * Return true
if the number of negative terms of the ast
expression
+ * countWeight
is greater than the half of the number of the arguments of ast
+ *
. I.e. int halfSize = ast.size() / 2; return countWeight > halfSize;
*
- * @param options the result array of options and their (possibly default) values.
* @param ast
- * @param argSize
- * @param expectedArgSize
- * @param optionSymbol
- * @param engine
+ * @param checkTimesPlus check Times(...)
and Plus(...)
expressions
* @return
*/
- public static int determineOptions(IExpr[] options, IAST ast, int argSize, int[] expectedArgSize,
- IBuiltInSymbol[] optionSymbol, EvalEngine engine) {
- argSize = determineArgumentOptions(options, ast, argSize, expectedArgSize, optionSymbol);
-
- determineDefaultOptions(options, ast, optionSymbol, engine);
- return argSize;
+ public static boolean isNegativeWeighted(final IAST ast, boolean checkTimesPlus) {
+ return isNegativeWeighted(ast, checkTimesPlus, ast.size() / 2);
}
/**
- * Determine the options only from the last arguments of ast
. Possibly additional
- * options are null
if no matching option is found in the arguments of the ast
- *
.
+ * Return the number of negative terms of the ast
expression.
*
- * @param options
* @param ast
- * @param argSize
- * @param expectedArgSize
- * @param optionSymbol
+ * @param checkTimesPlus check Times(...)
and Plus(...)
expressions
+ * @param maxNegativeExpr maximum number of negative valued terms which have to be found before
+ * returning true
* @return
*/
- private static int determineArgumentOptions(IExpr[] options, IAST ast, int argSize,
- int[] expectedArgSize, IBuiltInSymbol[] optionSymbol) {
- int minNumberOfArgs = 1;
- if (expectedArgSize != null) {
- // the ast function must at least contain the minimum number of arguments
- minNumberOfArgs = expectedArgSize[0];
- if (minNumberOfArgs < 1) {
- minNumberOfArgs = 1;
+ public static boolean isNegativeWeighted(final IAST ast, boolean checkTimesPlus,
+ int maxNegativeExpr) {
+ int count = maxNegativeExpr - 1;
+ for (int i = 1; i < ast.size(); i++) {
+ if (isNegativeValued(ast.get(i), checkTimesPlus)) {
+ if (--count < 0) {
+ return true;
+ }
}
}
+ return false;
+ }
- int counter = 0;
- boolean evaled = true;
- while (argSize > minNumberOfArgs && evaled && counter < optionSymbol.length) {
- IExpr arg = ast.get(argSize);
+ /**
+ * Split plusAST into two parts, a "rest" and a multiple of Pi/2. This assumes plusAST to be an
+ * Plus() expression. The multiple of Pi/2 returned in the second position is always a IRational
+ * number.
+ *
+ * @param plusAST
+ * @param engine
+ * @return F.NIL
if no multiple is found.
+ */
+ public static IAST peelOff(final IAST plusAST, final EvalEngine engine) {
+ IRational k = null;
+ for (int i = 1; i < plusAST.size(); i++) {
+ IExpr temp = plusAST.get(i);
+ if (temp.equals(S.Pi)) {
+ k = F.C1;
+ break;
+ }
+ if (temp.isTimes2()) {
+ if (temp.first().isRational() && temp.second().equals(S.Pi)) {
+ k = (IRational) temp.first();
+ break;
+ }
+ }
+ }
+ if (k != null) {
+ IASTMutable result = F.binaryAST2(S.List, plusAST, F.C0);
+ IExpr m1 = F.Times(k.mod(F.C1D2), S.Pi);
+ IExpr m2 = S.Subtract.of(engine, F.Times(k, S.Pi), m1);
+ result.set(1, S.Subtract.of(plusAST, m2));
+ result.set(2, m2);
+ return result;
+ }
+ return F.NIL;
+ }
- // check that arg has the correct options format:
- if (arg.isRule()) {
- evaled = false;
- for (int i = 0; i < optionSymbol.length; i++) {
- if (optionSymbol[i].equals(arg.first())) {
- options[i] = arg.second();
- argSize--;
- counter++;
- evaled = true;
- break;
+ /**
+ * This assumes plusAST to be an Plus() expression. The multiple of Pi returned is a IRational
+ * number or assumed to be an expression with Integer result.
+ *
+ * @param plusAST
+ * @param engine
+ * @return F.NIL
if no multiple is found, otherwise return
+ * List(rational-number, argument)
+ */
+ public static IAST peelOffPlusI(final IAST plusAST, final EvalEngine engine) {
+ for (int i = 1; i < plusAST.size(); i++) {
+ final IExpr arg = plusAST.get(i);
+ if (arg.isTimes()) {
+ IExpr peeled = peelOfTimes((IAST) arg, S.Pi);
+ if (peeled.isPresent()) {
+ IExpr x = S.Times.of(F.CNI, peeled);
+ if (x.isRational() || x.isIntegerResult()) {
+ return F.list(x, arg);
}
}
- } else if (arg.isListOfRules(true) && !arg.isEmptyList()) {
- IAST listOfRules = (IAST) arg;
- for (int j = 1; j < listOfRules.size(); j++) {
- IAST rule = (IAST) listOfRules.get(j);
- evaled = false;
- for (int i = 0; i < optionSymbol.length; i++) {
- if (optionSymbol[i].equals(rule.first())) {
- options[i] = rule.second();
- argSize--;
- counter++;
- evaled = true;
- break;
- }
+ }
+ }
+ return F.NIL;
+ }
+
+ /**
+ * This method assumes plusAST to be a Plus() expression. The multiple of Pi returned is a
+ * IRational number or assumed to be an expression with Integer result.
+ *
+ * @param plusAST
+ * @param engine
+ * @return F.NIL
if no multiple is found.
+ */
+ public static IExpr peelOffPlusRational(final IAST plusAST, final EvalEngine engine) {
+ IExpr k = null;
+ for (int i = 1; i < plusAST.size(); i++) {
+ IExpr temp = plusAST.get(i);
+ if (temp.equals(S.Pi)) {
+ k = F.C1;
+ return k;
+ }
+ if (temp.isTimes()) {
+ IExpr peeled = peelOfTimes((IAST) temp, S.Pi);
+ if (peeled.isPresent()) {
+ if (peeled.isRational() || peeled.isIntegerResult()) {
+ // k = temp.first();
+ return peeled;
}
}
- } else {
- evaled = false;
- break;
}
}
- return argSize;
+ return null;
}
/**
- * Replace the options which are null
only from the default options of the head
- * symbol.
+ * Try to split a periodic part from the Times() expression: result == timesAST / period
+ *
*
- * @param options
- * @param ast
- * @param optionSymbol
- * @param engine
+ * @param astTimes
+ * @param period
+ * @return F.NIL
if no periodicity was found
*/
- private static void determineDefaultOptions(IExpr[] options, IAST ast,
- IBuiltInSymbol[] optionSymbol, EvalEngine engine) {
- int optionNullStart = -1;
- for (int i = 0; i < options.length; i++) {
- if (options[i] == null) {
- optionNullStart = i;
- break;
+ public static IExpr peelOfTimes(final IAST astTimes, final IExpr period) {
+ for (int i = 1; i < astTimes.size(); i++) {
+ if (astTimes.get(i).equals(period)) {
+ return astTimes.splice(i).oneIdentity1();
}
}
- if (optionNullStart >= 0) {
- final IExpr temp = OptionsPattern.optionsList(ast.topHead(), false);
- // final IExpr temp = engine.evaluate(F.Options(ast.topHead()));
- if (temp.isList() && temp.size() > 1) {
- IAST list = (IAST) temp;
- for (int i = optionNullStart; i < options.length; i++) {
- if (options[i] == null) {
- options[i] = S.None;
- for (int j = 1; j < list.size(); j++) {
- IAST rule = (IAST) list.get(j);
- if (optionSymbol[i].equals(rule.first())) {
- options[i] = rule.second();
- break;
- }
- }
+ return F.NIL;
+ }
+
+ /**
+ * Try to split a periodic part from the Times() expression: result == timesAST / period
+ *
+ *
+ * @param astTimes
+ * @param period1
+ * @param period2
+ * @return F.NIL
if no periodicity was found
+ */
+ public static IExpr peelOfTimes(final IAST astTimes, final IExpr period1, final IExpr period2) {
+ IASTAppendable result = F.NIL;
+ for (int i = 1; i < astTimes.size(); i++) {
+ if (astTimes.get(i).equals(period1)) {
+ result = astTimes.copyAppendable();
+ result.remove(i);
+ for (int j = 1; j < result.size(); j++) {
+ if (result.get(j).equals(period2)) {
+ result.remove(j);
+ return result;
}
}
+ return F.NIL;
+ }
+ }
+ return F.NIL;
+ }
+
+ private static IExpr pureImaginaryPart(final IExpr expr) {
+ if (expr.isComplex() && ((IComplex) expr).re().isZero()) {
+ IComplex compl = (IComplex) expr;
+ return compl.im();
+ }
+ if (expr.isTimes()) {
+ IAST times = ((IAST) expr);
+ IExpr arg1 = times.arg1();
+ if (arg1.isComplex() && ((IComplex) arg1).re().isZero()) {
+ return times.setAtCopy(1, ((IComplex) arg1).im());
+ }
+ }
+ return F.NIL;
+ }
+
+ public static IExpr realPart(final IExpr expr, boolean unequalsZero) {
+ IExpr rePart = S.Re.of(expr);
+ if (unequalsZero) {
+ if (rePart.isZero()) {
+ return F.NIL;
+ }
+ }
+ if (rePart.isNumber() || rePart.isFree(S.Re)) {
+ return rePart;
+ }
+ return F.NIL;
+ }
+
+ /**
+ * Create a rule which invokes the method name in this class instance.
+ *
+ * @param symbol
+ * @param patternString
+ * @param methodName
+ */
+ public void createRuleFromMethod(ISymbol symbol, String patternString, String methodName) {
+ PatternMatcherAndInvoker pm = new PatternMatcherAndInvoker(patternString, this, methodName);
+ symbol.putDownRule(pm);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public abstract IExpr evaluate(final IAST ast, final EvalEngine engine);
+
+ /** {@inheritDoc} */
+ @Override
+ public void setUp(final ISymbol newSymbol) {
+
+ // F.SYMBOL_OBSERVER.createPredefinedSymbol(newSymbol.toString());
+ if (Config.SERIALIZE_SYMBOLS && newSymbol.containsRules()) {
+ try (
+ FileOutputStream out =
+ new FileOutputStream("c:\\temp\\ser\\" + newSymbol.getSymbolName() + ".ser");
+ ObjectOutputStream oos = new ObjectOutputStream(out);) {
+ newSymbol.writeRules(oos);
+ } catch (IOException e) {
+ LOGGER.error("AbstractFunctionEvaluator.setUp() failed", e);
}
}
}
diff --git a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/LinearAlgebraTestCase.java b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/LinearAlgebraTestCase.java
index a9b1ae46e4..563182f05e 100644
--- a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/LinearAlgebraTestCase.java
+++ b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/LinearAlgebraTestCase.java
@@ -17,7 +17,7 @@ public LinearAlgebraTestCase(String name) {
public void testAdjugate() {
check("Adjugate({{E^5, 1, 3 - 2*I}, {1 + I, Pi/2, 5}, {0, 1, -4}})", //
- "{{2*(-5/2-Pi),7-I*2,(-3/2+I)*(-30/13-I*20/13+Pi)},\n" //
+ "{{2*(-5/2-Pi),7-I*2,(3/2-I)*(30/13+I*20/13-Pi)},\n" //
+ " {4+I*4,-4*E^5,5*(1+I*1/5-E^5)},\n" //
+ " {1+I,-E^5,1/2*(-2-I*2+E^5*Pi)}}");
// https://en.wikipedia.org/wiki/Adjugate_matrix
@@ -931,10 +931,9 @@ public void testIdentityMatrix() {
public void testInverse() {
check("Inverse(s*{{1,0,0},{0,1,0},{0,0,1}}-{{-1,1,1},{-4,-4,1},{1,1,1}})", //
- "{{(-5+3*s+s^2)/(-10+s+4*s^2+s^3),s/(-10+s+4*s^2+s^3),(5+s)/(-10+s+4*s^2+s^3)},\n"//
- + " {((1+s)*(-5+4*s))/((-1-s)*(-10+s+4*s^2+s^3)),(-2+s^2)/(-10+s+4*s^2+s^3),(-3+s)/(-\n"//
- + "10+s+4*s^2+s^3)},\n"//
- + " {s/(-10+s+4*s^2+s^3),(-2-s)/(10-s-4*s^2-s^3),(8+5*s+s^2)/(-10+s+4*s^2+s^3)}}");
+ "{{(5-3*s-s^2)/(10-s-4*s^2-s^3),s/(-10+s+4*s^2+s^3),(5+s)/(-10+s+4*s^2+s^3)},\n" //
+ + " {(5-4*s)/(-10+s+4*s^2+s^3),(2-s^2)/(10-s-4*s^2-s^3),(3-s)/(10-s-4*s^2-s^3)},\n" //
+ + " {-s/(10-s-4*s^2-s^3),-(2+s)/(10-s-4*s^2-s^3),(-8-5*s-s^2)/(10-s-4*s^2-s^3)}}");
check("N(Inverse({{1,2.0},{3,4}}),50)", //
"{{-2.0,1.0},{1.5,-0.5}}");
@@ -1034,7 +1033,7 @@ public void testLinearSolve() {
check("LinearSolve({{a, b,c,d,e}, {f,g,h,i,j}}, {x, y})", //
"{(g*x-b*y)/(-b*f+a*g),(-f*x+a*y)/(-b*f+a*g),0,0,0}");
check("LinearSolve({{a,b,c,d,e}, {f,g,h,i,j}, {k,l,m,n,o}}, {x,y,z})", //
- "{(-h*l*x+g*m*x+c*l*y-b*m*y-c*g*z+b*h*z)/(-c*g*k+b*h*k+c*f*l-a*h*l-b*f*m+a*g*m),(h*k*x-f*m*x-c*k*y+a*m*y+c*f*z-a*h*z)/(-c*g*k+b*h*k+c*f*l-a*h*l-b*f*m+a*g*m),(-g*k*x+f*l*x+b*k*y-a*l*y-b*f*z+a*g*z)/(-c*g*k+b*h*k+c*f*l-a*h*l-b*f*m+a*g*m),\n"
+ "{(h*l*x-g*m*x-c*l*y+b*m*y+c*g*z-b*h*z)/(c*g*k-b*h*k-c*f*l+a*h*l+b*f*m-a*g*m),(-h*k*x+f*m*x+c*k*y-a*m*y-c*f*z+a*h*z)/(c*g*k-b*h*k-c*f*l+a*h*l+b*f*m-a*g*m),(g*k*x-f*l*x-b*k*y+a*l*y+b*f*z-a*g*z)/(c*g*k-b*h*k-c*f*l+a*h*l+b*f*m-a*g*m),\n" //
+ "0,0}");
// underdetermined system:
check("LinearSolve({{1, 2, 3}, {4, 5, 6}}, {6, 15})", //
diff --git a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/LowercaseTestCase.java b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/LowercaseTestCase.java
index a1fca7ec33..07010a1b77 100644
--- a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/LowercaseTestCase.java
+++ b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/LowercaseTestCase.java
@@ -22017,6 +22017,9 @@ public void testSign() {
// }
public void testSimplify() {
+ check("Simplify((1/x-1/3)/(x-3))", //
+ "-1/(3*x)");
+
check("Simplify(Abs(Sign(z)),z!=-1&& z!=0&&z!=1)", //
"1");
check("Simplify(Sign(x), x<0)", //
@@ -24336,6 +24339,10 @@ public void testTimeValue() {
}
public void testTogether() {
+ check("Together((1/x-1/3)^3/(x-3)^2)", //
+ "(3-x)/(27*x^3)");
+ check("Together((1/x-1/3)/(x-3))", //
+ "-1/(3*x)");
check("Together(1/2+(1/3+I*1/2)*Sqrt(3))", //
"1/6*(3+(2+I*3)*Sqrt(3))");
check("Together(1/2+I*1/2*Sqrt(3))", //
@@ -24370,7 +24377,7 @@ public void testTogether() {
check("Together((x+2*Sqrt(x)+1)/(1+Sqrt(x)))", //
"1+Sqrt(x)");
check("Together(-a/(-(b-a*c)))", //
- "-a/(-b+a*c)");
+ "a/(b-a*c)");
check("Together(Simplify(Together(-a/(-(b-a*c)))))", //
"a/(b-a*c)");
check("Together(1/2+I/3 + 3*a^(-1))", //
diff --git a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SolveTest.java b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SolveTest.java
index 4b4e3e91cd..b71051a0ef 100644
--- a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SolveTest.java
+++ b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SolveTest.java
@@ -216,7 +216,6 @@ public void testSolveX7_15002() {
}
public void testSolve001() {
-
check("Solve((5*x^4-2)/(x+1)/(x^2-1)==0,x)", //
"{{x->-(2/5)^(1/4)},{x->-I*(2/5)^(1/4)},{x->I*(2/5)^(1/4)},{x->(2/5)^(1/4)}}");
}
diff --git a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SumTest.java b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SumTest.java
index 1549d295f5..baba139e0b 100644
--- a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SumTest.java
+++ b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SumTest.java
@@ -242,7 +242,7 @@ public void testSum007() {
check("Sum(a^i,{i,0,1})", //
"1+a");
check("Sum(a^i,{i,0,Infinity})", //
- "-1/(-1+a)");
+ "1/(1-a)");
check("Sum(a^i,{i,1,Infinity})", //
"-a/(-1+a)");
check("Sum(a^i,{i,3,Infinity})", //