From 95e37d452c5c402843a132e8bede657f9f0f6b3d Mon Sep 17 00:00:00 2001 From: axexlck Date: Sat, 26 Oct 2024 02:33:26 +0200 Subject: [PATCH] Implement `IntegerDigits,NumberDigit,RealDigits` with apfloat data types - https://github.com/mtommila/apfloat/discussions/55 - https://en.wikipedia.org/wiki/Repeating_decimal --- .../core/builtin/IntegerFunctions.java | 304 ++++++++++-------- .../org/matheclipse/core/eval/Errors.java | 1 + .../core/expression/AbstractIntegerSym.java | 4 +- .../core/numerics/utils/RealDigitsResult.java | 25 +- .../core/system/LowercaseTestCase.java | 171 +++++++++- 5 files changed, 355 insertions(+), 150 deletions(-) diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/IntegerFunctions.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/IntegerFunctions.java index 28ab89863..a96f01e88 100644 --- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/IntegerFunctions.java +++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/IntegerFunctions.java @@ -6,6 +6,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apfloat.Apfloat; +import org.apfloat.Apint; +import org.apfloat.Aprational; import org.hipparchus.complex.Complex; import org.matheclipse.core.basic.Config; import org.matheclipse.core.eval.Errors; @@ -16,13 +18,11 @@ import org.matheclipse.core.eval.interfaces.AbstractCoreFunctionEvaluator; import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator; import org.matheclipse.core.eval.interfaces.INumeric; -import org.matheclipse.core.expression.ApfloatNum; import org.matheclipse.core.expression.ComplexNum; import org.matheclipse.core.expression.ComplexSym; import org.matheclipse.core.expression.F; import org.matheclipse.core.expression.ImplementationStatus; import org.matheclipse.core.expression.IntervalSym; -import org.matheclipse.core.expression.Num; import org.matheclipse.core.expression.S; import org.matheclipse.core.expression.StringX; import org.matheclipse.core.interfaces.IAST; @@ -30,11 +30,13 @@ import org.matheclipse.core.interfaces.IASTMutable; import org.matheclipse.core.interfaces.IComplex; import org.matheclipse.core.interfaces.IExpr; +import org.matheclipse.core.interfaces.IFraction; import org.matheclipse.core.interfaces.IInteger; import org.matheclipse.core.interfaces.INumber; import org.matheclipse.core.interfaces.IRational; import org.matheclipse.core.interfaces.IReal; import org.matheclipse.core.interfaces.ISymbol; +import org.matheclipse.core.numerics.utils.RealDigitsResult; import org.matheclipse.core.tensor.qty.IQuantity; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -69,48 +71,48 @@ private static void init() { } } - private static IAST integerDigits(IInteger n, IInteger base, int padLeftZeros) { - IASTAppendable list = F.ListAlloc(16); - if (n.isZero()) { - list.append(F.C0); - } else { - if (base.equals(F.C2)) { - BitSet bs = integerToBitSet(n); - int last = 0; - for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { - if (i > 0) { - for (int j = last; j < i; j++) { - list.append(F.C0); - } - } - last = i + 1; - list.append(F.C1); - } - } else { - while (n.isPositive()) { - IInteger mod = n.mod(base); - list.append(mod); - n = n.subtract(mod).div(base); - } - } - } - - if (padLeftZeros < list.argSize() && padLeftZeros > 0) { - IASTAppendable result = F.ListAlloc(list.argSize()); - result = list.reverse(result); - return result.copyFrom(list.size() - padLeftZeros); - } else { - int padSizeZeros = padLeftZeros - list.argSize(); - if (padSizeZeros < 0) { - padSizeZeros = 0; - } - IASTAppendable result = F.ListAlloc(list.argSize() + padSizeZeros); - for (int i = 0; i < padSizeZeros; i++) { - result.append(F.C0); - } - return list.reverse(result); - } - } + // private static IAST integerDigits(IInteger n, IInteger base, int padLeftZeros) { + // IASTAppendable list = F.ListAlloc(16); + // if (n.isZero()) { + // list.append(F.C0); + // } else { + // if (base.equals(F.C2)) { + // BitSet bs = integerToBitSet(n); + // int last = 0; + // for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { + // if (i > 0) { + // for (int j = last; j < i; j++) { + // list.append(F.C0); + // } + // } + // last = i + 1; + // list.append(F.C1); + // } + // } else { + // while (n.isPositive()) { + // IInteger mod = n.mod(base); + // list.append(mod); + // n = n.subtract(mod).div(base); + // } + // } + // } + // + // if (padLeftZeros < list.argSize() && padLeftZeros > 0) { + // IASTAppendable result = F.ListAlloc(list.argSize()); + // result = list.reverse(result); + // return result.copyFrom(list.size() - padLeftZeros); + // } else { + // int padSizeZeros = padLeftZeros - list.argSize(); + // if (padSizeZeros < 0) { + // padSizeZeros = 0; + // } + // IASTAppendable result = F.ListAlloc(list.argSize() + padSizeZeros); + // for (int i = 0; i < padSizeZeros; i++) { + // result.append(F.C0); + // } + // return list.reverse(result); + // } + // } public static BitSet integerToBitSet(int n) { BigInteger bn = BigInteger.valueOf(n); @@ -459,17 +461,21 @@ private static class IntegerDigits extends AbstractFunctionEvaluator { @Override public IExpr evaluate(IAST ast, EvalEngine engine) { - IInteger base = F.C10; + int base = 10; int padLeftZeros = 0; - if (ast.size() >= 3) { - IExpr arg2 = ast.arg2(); - if (arg2.isInteger() && ((IInteger) arg2).compareInt(1) > 0) { - base = (IInteger) arg2; - } else { - return F.NIL; + if (ast.argSize() >= 2) { + base = ast.arg2().toIntDefault(); + if (base < 2) { + // Base `1` is not an integer greater than `2`. + return Errors.printMessage(S.IntegerDigits, "ibase", F.List(ast.arg2(), F.C1), engine); + } + if (base > 36) { + // `1` currently not supported in `2`. + return Errors.printMessage(S.IntegerDigits, "unsupported", + F.List("Base greater than 36", "IntegerDigits"), engine); } } - if (ast.size() >= 4) { + if (ast.isAST3()) { padLeftZeros = ast.arg3().toIntDefault(); if (padLeftZeros < 0) { return F.NIL; @@ -478,7 +484,25 @@ public IExpr evaluate(IAST ast, EvalEngine engine) { IExpr arg1 = ast.arg1(); if (arg1.isInteger()) { IInteger n = ((IInteger) arg1).abs(); - return integerDigits(n, base, padLeftZeros); + + Apfloat apfloat = new Apint(n.toBigNumerator()); + RealDigitsResult rd = RealDigitsResult.create(apfloat, base); + if (rd != null) { + IASTAppendable digitsList = rd.getDigitsList(); + + if (padLeftZeros < digitsList.argSize() && padLeftZeros > 0) { + return digitsList.subList(digitsList.size() - padLeftZeros); + } else { + int padSizeZeros = padLeftZeros - digitsList.argSize(); + if (padSizeZeros < 0) { + padSizeZeros = 0; + } + for (int i = 0; i < padSizeZeros; i++) { + digitsList.append(1, F.C0); + } + return digitsList; + } + } } return F.NIL; } @@ -1187,24 +1211,35 @@ private static class NumberDigit extends AbstractFunctionEvaluator { @Override public IExpr evaluate(final IAST ast, EvalEngine engine) { - int n = ast.arg2().toIntDefault(); - if (n != Integer.MIN_VALUE) { + IExpr arg1 = ast.arg1(); + if (arg1.isList()) { + IAST list = (IAST) arg1; + return list.mapThread(ast.setAtCopy(1, F.Slot1), 1); + } + if (ast.arg2().isList()) { + IAST list = (IAST) ast.arg2(); + return list.mapThread(ast.setAtCopy(2, F.Slot1), 2); + } + long n = ast.arg2().toLongDefault(); + if (n != Long.MIN_VALUE) { IExpr x = ast.arg1(); - int b = 10; + int base = 10; if (ast.isAST3()) { - b = ast.arg3().toIntDefault(); - if (b <= 1) { - if (ast.arg3().isNumber()) { - // Base `1` is not a real number greater than 1. - Errors.printMessage(S.NumberDigit, "rbase", F.List(ast.arg3()), engine); - } - return F.NIL; + base = ast.arg3().toIntDefault(); + if (base < 2) { + // Base `1` is not an integer greater than `2`. + return Errors.printMessage(S.NumberDigit, "ibase", F.List(ast.arg3(), F.C1), engine); + } + if (base > 36) { + // `1` currently not supported in `2`. + return Errors.printMessage(S.NumberDigit, "unsupported", + F.List("Base greater than 36", "NumberDigit"), engine); } } - IExpr realDigits = realDigits(x); - if (realDigits.isList2()&&realDigits.isList()) { + IExpr realDigits = realDigits(x, base, 1L, n); + if (realDigits.isList2() && realDigits.first().isList()) { IAST resultList = (IAST) realDigits.first(); - return resultList.get(resultList.argSize() - n); + return resultList.get(1); } } return F.NIL; @@ -1220,10 +1255,6 @@ public int[] expectedArgSize(IAST ast) { return ARGS_2_3; } - @Override - public void setUp(ISymbol newSymbol) { - newSymbol.setAttributes(ISymbol.LISTABLE); - } } /** @@ -1577,7 +1608,39 @@ private static class RealDigits extends AbstractFunctionEvaluator { public IExpr evaluate(final IAST ast, EvalEngine engine) { IExpr arg1 = ast.arg1(); try { - IExpr realDigits = realDigits(arg1); + int base = 10; + long length = Long.MIN_VALUE; + long startDigit = Long.MIN_VALUE; + if (ast.argSize() >= 2) { + base = ast.arg2().toIntDefault(); + if (base < 2) { + // Base `1` is not an integer greater than `2`. + return Errors.printMessage(S.RealDigits, "ibase", F.List(ast.arg2(), F.C1), engine); + } + if (base > 36) { + // `1` currently not supported in `2`. + return Errors.printMessage(S.RealDigits, "unsupported", + F.List("Base greater than 36", "RealDigits"), engine); + } + if (ast.argSize() >= 3) { + length = ast.arg3().toLongDefault(); + if (length == Long.MIN_VALUE) { + return F.NIL; + } + if (ast.argSize() == 4) { + startDigit = ast.arg4().toLongDefault(); + if (startDigit == Long.MIN_VALUE) { + return F.NIL; + } + } + } + } + + if (length != Long.MIN_VALUE && length < 0) { + // Non-negative machine-sized integer expected at position `2` in `1` + return Errors.printMessage(ast.topHead(), "intnm", F.list(F.C3, ast.arg3()), engine); + } + IExpr realDigits = realDigits(arg1, base, length, startDigit); if (realDigits.isPresent()) { return realDigits; } @@ -1597,7 +1660,7 @@ public IExpr evaluate(final IAST ast, EvalEngine engine) { @Override public int[] expectedArgSize(IAST ast) { - return ARGS_1_1; + return ARGS_1_4; } @Override @@ -1761,70 +1824,53 @@ public void setUp(final ISymbol newSymbol) { } - public static IExpr realDigits(IExpr arg1) { - if (arg1.isInteger()) { - IInteger number = (IInteger) arg1; - if (number.isNegative()) { - number = number.abs(); - } - IAST list = integerDigits(number, F.C10, 0); - return F.list(list, F.ZZ(list.size() - 1)); - } - IReal number = null; + public static IExpr realDigits(IExpr arg1, int radix, long length, long startDigit) { + INumber number = null; if (arg1.isReal()) { number = (IReal) arg1; } else { - number = arg1.evalReal(); - } - if (number != null) { - if (number.isNegative()) { - number = number.abs(); - } - - if (number instanceof ApfloatNum) { - Apfloat apfloat = number.apfloatValue(); - String str = apfloat.toString(); - IASTAppendable list = F.ListAlloc(str.length() + 1); - int numberOfLeftDigits = 0; - for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); - if (ch == '.') { - numberOfLeftDigits = i; - continue; - } - if (ch == 'e' || ch == 'E') { - String exponentStr = str.substring(i + 1); - int exponent = Integer.parseInt(exponentStr); - - numberOfLeftDigits += exponent; - - break; - } - list.append(ch); + if (length != Long.MIN_VALUE) { + long precision = length + Math.abs(startDigit); + if (precision <= 0) { + precision = 1; } - - return F.list(list, F.ZZ(numberOfLeftDigits)); - } else if (number instanceof Num) { - String str = Double.toString(number.doubleValue()); - IASTAppendable list = F.ListAlloc(str.length() + 1); - int numberOfLeftDigits = 0; - for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); - if (ch == '.') { - numberOfLeftDigits = i; - continue; - } - if (ch == 'e' || ch == 'E') { - String exponentStr = str.substring(i + 1); - int exponent = Integer.parseInt(exponentStr); - numberOfLeftDigits += exponent; - break; - } - list.append(ch); + IExpr expr = EvalEngine.get().evaluate(F.N(arg1, precision)); + if (expr.isReal()) { + number = (IReal) expr; } + } else { + number = arg1.evalNumber(); + } + } + if (number != null && number.isReal()) { + IReal realNumber = (IReal) number; + if (realNumber.isNegative()) { + realNumber = realNumber.abs(); + } + + Apfloat apfloat; + if (realNumber.isInteger()) { + apfloat = new Apint(((IInteger) realNumber).toBigNumerator()); + } else if (realNumber.isFraction() && length == Long.MIN_VALUE + && startDigit == Long.MIN_VALUE) { + Apint numer = new Apint(((IFraction) realNumber).toBigNumerator()); + Apint denom = new Apint(((IFraction) realNumber).toBigDenominator()); + Aprational aprational = new Aprational(numer, denom); + RealDigitsResult rd = RealDigitsResult.create(aprational, radix); + if (rd != null) { + return F.list(rd.getDigitsList(), F.ZZ(rd.getNumberOfLeftDigits())); + } + return F.NIL; + } else { + apfloat = realNumber.apfloatValue(); + } - return F.list(list, F.ZZ(numberOfLeftDigits)); + + RealDigitsResult rd = RealDigitsResult.create(apfloat, radix, length, startDigit); + if (rd != null) { + return F.list(rd.getDigitsList(), F.ZZ(rd.getNumberOfLeftDigits())); } + } return F.NIL; } diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/Errors.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/Errors.java index 2e143becf..0eec24e98 100644 --- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/Errors.java +++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/Errors.java @@ -134,6 +134,7 @@ public static void initGeneralMessages() { "heads", "Heads `1` and `2` are expected to be the same.", // "heads2", "Heads `1` and `2` at positions `3` and `4` are expected to be the same.", // "herm", "The matrix `1` is not hermitian or real and symmetric.", // + "ibase", "Base `1` is not an integer greater than `2`.", // "idim", "`1` and `2` must have the same length.", // "idir", "Direction vector `1` has zero magnitude.", // "idiv", "Integral of `1` does not converge on `2`.", // diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/expression/AbstractIntegerSym.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/expression/AbstractIntegerSym.java index 3e5579476..5041f9285 100644 --- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/expression/AbstractIntegerSym.java +++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/expression/AbstractIntegerSym.java @@ -12,6 +12,7 @@ import java.util.TreeSet; import org.apfloat.Apcomplex; import org.apfloat.Apfloat; +import org.apfloat.Apint; import org.hipparchus.exception.MathRuntimeException; import org.hipparchus.util.ArithmeticUtils; import org.matheclipse.core.basic.Config; @@ -355,7 +356,8 @@ public ApfloatNum apfloatNumValue() { @Override public Apfloat apfloatValue() { - return new Apfloat(toBigNumerator(), EvalEngine.getApfloat().precision()); + return new Apint(toBigNumerator()); + // return new Apfloat(toBigNumerator(), EvalEngine.getApfloat().precision()); } @Override diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/utils/RealDigitsResult.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/utils/RealDigitsResult.java index 80c320e80..360a704fd 100644 --- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/utils/RealDigitsResult.java +++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/utils/RealDigitsResult.java @@ -11,8 +11,10 @@ import org.apfloat.Aprational; import org.apfloat.AprationalMath; import org.matheclipse.core.expression.F; +import org.matheclipse.core.expression.S; import org.matheclipse.core.interfaces.IAST; import org.matheclipse.core.interfaces.IASTAppendable; +import org.matheclipse.core.interfaces.IExpr; public class RealDigitsResult { public static RealDigitsResult create(Apfloat a) { @@ -85,13 +87,19 @@ private void digits(Apfloat a, int radix, long length, long startDigit) { if (padLength > Integer.MAX_VALUE) { throw new RuntimeException("List size exceeded"); } - IASTAppendable constantArray = F.constantArray(F.C0, (int) padLength); - digitsList.appendArgs(constantArray); + pad(padLength, F.C0); } else if (adjust > startDigit) { // Truncate first significant digits a = ApfloatMath.scale(a, -startDigit - 1); a = a.frac(); a = ApfloatMath.scale(a, startDigit + 1); + // If the number now has some extra leading zeros (i.e. the scale became smaller) they won't + // be printed, print them explicitly + adjust = a.scale() - 1; + if (adjust < startDigit) { + long padLength = Math.min(length, startDigit - adjust); + pad(padLength, F.C0); + } } numberOfLeftDigits = startDigit + 1; } @@ -134,8 +142,7 @@ public void write(char[] cbuf, int off, int len) throws IOException { if (padLength > Integer.MAX_VALUE) { throw new RuntimeException("List size exceeded"); } - IASTAppendable constantArray = F.constantArray(F.C0, (int) padLength); - digitsList.appendArgs(constantArray); + pad(padLength, F.C0); } if (length > digitsList.argSize()) { // Pad the remaining requested length with nulls as no significant digits exist @@ -143,8 +150,7 @@ public void write(char[] cbuf, int off, int len) throws IOException { if (padLength > Integer.MAX_VALUE) { throw new RuntimeException("List size exceeded"); } - IASTAppendable constantArray = F.constantArray(F.C0, (int) padLength); - digitsList.appendArgs(constantArray); + pad(padLength, S.Indeterminate); } } @@ -232,5 +238,12 @@ public long getNumberOfLeftDigits() { return numberOfLeftDigits; } + private void pad(long padLength, IExpr digit) { + if (padLength > Integer.MAX_VALUE) { + throw new RuntimeException("List size exceeded"); + } + IASTAppendable constantArray = F.constantArray(digit, (int) padLength); + digitsList.appendArgs(constantArray); + } } 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 05a6c625e..183d38ba9 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 @@ -11027,12 +11027,29 @@ public void testInteger() { @Test public void testIntegerDigits() { + // message: IntegerDigits: Base 1 is not an integer greater than 1. + check("IntegerDigits(11,1)", // + "IntegerDigits(11,1)"); + // message: IntegerDigits: Base greater than 36 currently not supported in IntegerDigits. + check("IntegerDigits(11,37)", // + "IntegerDigits(11,37)"); + + check("IntegerDigits(25, 8)", // + "{3,1}"); + check("IntegerDigits(11,2,3)", // + "{0,1,1}"); + check("IntegerDigits({123,456,789}, 2, 10)", // "{{0,0,0,1,1,1,1,0,1,1},{0,1,1,1,0,0,1,0,0,0},{1,1,0,0,0,1,0,1,0,1}}"); // Long.MAX_VALUE == 9_223_372_036_854_775_807L - // check("IntegerDigits(9223372036854775807,2)", // - // "{0,1,1}"); + check("IntegerDigits(9223372036854775807,2)", // + "{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,\n" // + + "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}"); + // Long.MAX_VALUE == 9_223_372_036_854_775_807L + 1L + check("IntegerDigits(9223372036854775808,2)", // + "{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n" // + + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}"); check("IntegerDigits(11,2,3)", // "{0,1,1}"); @@ -11072,6 +11089,16 @@ public void testIntegerDigits() { "{{0},{1},{1,0},{1,1},{1,0,0},{1,0,1},{1,1,0},{1,1,1}}"); check("IntegerDigits(Range(0,7), 2, 3)", // "{{0,0,0},{0,0,1},{0,1,0},{0,1,1},{1,0,0},{1,0,1},{1,1,0},{1,1,1}}"); + check("IntegerDigits(6345354,10,4)", // + "{5,3,5,4}"); + + check("Table(If(FreeQ(IntegerDigits(n-1, 3), 1), 1, 0), {n, 27})", // + "{1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1}"); + // TODO max radix is 36 at the moment + check("Table(FromDigits(IntegerDigits(2, b), 1/b)/b, {b,2,36})", // + "{1/4,2/3,1/2,2/5,1/3,2/7,1/4,2/9,1/5,2/11,1/6,2/13,1/7,2/15,1/8,2/17,1/9,2/19,1/\n" // + + "10,2/21,1/11,2/23,1/12,2/25,1/13,2/27,1/14,2/29,1/15,2/31,1/16,2/33,1/17,2/35,1/\n" // + + "18}"); } @Test @@ -14477,6 +14504,8 @@ public void testMersennePrimeExponent() { "37156667"); check("MersennePrimeExponent(47)", // "43112609"); + check("MersennePrimeExponent(52)", // + "136279841"); } @Test @@ -16062,13 +16091,30 @@ public void testNothing() { @Test public void testNumberDigit() { - check("RealDigits(123456)", // - "{{1,2,3,4,5,6},6}"); + check("NumberDigit(Pi, -1, 2)", // + "0"); + check("NumberDigit(Pi, -2, 2)", // + "0"); + check("NumberDigit(Pi, -3, 2)", // + "1"); + + check("Range(2, -4, -1)", // + "{2,1,0,-1,-2,-3,-4}"); + + check("NumberDigit(Pi, Range(2, -4, -1), 2)", // + "{0,1,1,0,0,1,0}"); + check("NumberDigit({Pi, E, 99352, 11/7}, {4, 0, -2})", // + "{{0,3,4},{0,2,1},{9,2,0},{0,1,7}}"); + check("NumberDigit(Pi, Range(2, -2, -1))", // + "{0,0,3,1,4}"); + check("NumberDigit(Pi, Range(2, -2, -1),16)", // + "{0,0,3,2,4}"); check("NumberDigit(123456, 2)", // "4"); - // TODO - // check("NumberDigit(12.3456, -1)", // - // "4"); + check("RealDigits(123456)", // + "{{1,2,3,4,5,6},6}"); + check("NumberDigit(12.3456, -1)", // + "3"); } @Test @@ -20242,24 +20288,121 @@ public void testReal() { @Test public void testRealDigits() { + // message RealDigits: Non-negative machine-sized integer expected at position -1 in 3. + check("RealDigits(Pi,2,-1,2)", // + "RealDigits(Pi,2,-1,2)"); + + + check("RealDigits(5.635, 10, 20)", // + "{{5,6,3,5,0,0,0,0,0,0,0,0,0,0,0,0,Indeterminate,Indeterminate,Indeterminate,Indeterminate},\n" // + + "1}"); + + check("RealDigits(Pi,2,1,2)", // + "{{0},3}"); + check("RealDigits(Pi,2,1,1)", // + "{{1},2}"); + check("RealDigits(Pi,2,1,0)", // + "{{1},1}"); + check("RealDigits(Pi,2,1,-1)", // + "{{0},0}"); + check("RealDigits(Pi,2,1,-2)", // + "{{0},-1}"); + check("RealDigits(Pi,2,1,-3)", // + "{{1},-2}"); + + check("RealDigits(Pi,2,0,2)", // + "{{},3}"); + check("RealDigits(Pi,2,0,1)", // + "{{},2}"); + check("RealDigits(Pi,2,0,0)", // + "{{},1}"); + check("RealDigits(Pi,2,0,-1)", // + "{{},0}"); + check("RealDigits(Pi,2,0,-2)", // + "{{},-1}"); + check("RealDigits(Pi,2,0,-3)", // + "{{},-2}"); + + check("RealDigits(0)", // + "{{0},1}"); + check("RealDigits(0.1, 2)", // + "{{1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,\n" // + + "0,1,1,0,0,1,1,0,0,1,1,0,0,1},-3}"); + check("RealDigits(Pi, 10, 1, -500)", // + "{{2},-499}"); + check("RealDigits(Pi, 10, 1, -501)", // + "{{9},-500}"); + + + check("RealDigits(Pi, 10, 20, -5)", // + "{{9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3},-4}"); + check("RealDigits(Pi, 10, 20, 5)", // + "{{0,0,0,0,0,3,1,4,1,5,9,2,6,5,3,5,8,9,7,9},6}"); + + check("RealDigits(1.234, 2, 15)", // + "{{1,0,0,1,1,1,0,1,1,1,1,1,0,0,1},1}"); + check("RealDigits(19/7, 10, 25)", // + "{{2,7,1,4,2,8,5,7,1,4,2,8,5,7,1,4,2,8,5,7,1,4,2,8,5},1}"); + + check("RealDigits(2,10,8,-1)", // + "{{0,0,0,0,0,0,0,0},0}"); + check("RealDigits(2,10,8,0)", // + "{{2,0,0,0,0,0,0,0},1}"); check("RealDigits(423.012345678*^-11)", // - "{{4,2,3,0,1,2,3,4,5,6,7,8},-8}"); + "{{4,2,3,0,1,2,3,4,5,6,7,8,0,0,0,0},-8}"); check("RealDigits(423.012345678*^9)", // - "{{4,2,3,0,1,2,3,4,5,6,7,8},12}"); + "{{4,2,3,0,1,2,3,4,5,6,7,8,0,0,0,0},12}"); check("RealDigits(1.012345678*^-11)", // - "{{1,0,1,2,3,4,5,6,7,8},-10}"); + "{{1,0,1,2,3,4,5,6,7,8,0,0,0,0,0,0},-10}"); check("RealDigits(1.012345678*^9)", // - "{{1,0,1,2,3,4,5,6,7,8},10}"); + "{{1,0,1,2,3,4,5,6,7,8,0,0,0,0,0,0},10}"); check("RealDigits(1.012345678*^-42)", // - "{{1,0,1,2,3,4,5,6,7,8},-41}"); + "{{1,0,1,2,3,4,5,6,7,8,0,0,0,0,0,0},-41}"); check("RealDigits(1.012345678*^123)", // - "{{1,0,1,2,3,4,5,6,7,8},124}"); + "{{1,0,1,2,3,4,5,6,7,8,0,0,0,0,0,0},124}"); + check(" RealDigits(N(Pi+42 ,30))", // + "{{4,5,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3,3,8,3,2},2}"); check("N(RealDigits(Pi+42),30)", // "{{4,5,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3,3,8,3,2},2}"); check("RealDigits(-42)", // "{{4,2},2}"); check("RealDigits(123.33333)", // - "{{1,2,3,3,3,3,3,3},3}"); + "{{1,2,3,3,3,3,3,3,0,0,0,0,0,0,0,0},3}"); + } + + @Test + public void testRealDigitsRational() { + check("RealDigits(19/7)", // + "{{2,{7,1,4,2,8,5}},1}"); + check("RealDigits(19/7, 11)", // + "{{2,{7,9,4}},1}"); + check("RealDigits(1/7)", // + "{{{1,4,2,8,5,7}},0}"); + check("RealDigits(1/32)", // + "{{3,1,2,5},-1}"); + check("RealDigits(33/32)", // + "{{1,0,3,1,2,5},1}"); + check("RealDigits(1/11)", // + "{{{9,0}},-1}"); + check("RealDigits(12/11)", // + "{{1,{0,9}},1}"); + check("RealDigits(1/81)", // + "{{{1,2,3,4,5,6,7,9,0}},-1}"); + check("RealDigits(10/13)", // + "{{{7,6,9,2,3,0}},0}"); + check("RealDigits(1/36)", // + "{{2,{7}},-1}"); + check("RealDigits(1/37)", // + "{{{2,7,0}},-1}"); + check("RealDigits(1/97)", // + "{{{1,0,3,0,9,2,7,8,3,5,0,5,1,5,4,6,3,9,1,7,5,2,5,7,7,3,1,9,5,8,7,6,2,8,8,6,5,9,7,\n" // + + "9,3,8,1,4,4,3,2,9,8,9,6,9,0,7,2,1,6,4,9,4,8,4,5,3,6,0,8,2,4,7,4,2,2,6,8,0,4,1,2,\n" // + + "3,7,1,1,3,4,0,2,0,6,1,8,5,5,6,7,0}},-1}"); + check("RealDigits(201/10)", // + "{{2,0,1},2}"); + check("RealDigits(141/7)", // + "{{2,0,{1,4,2,8,5,7}},2}"); + } @Test