diff --git a/mathics/builtin/arithfns/basic.py b/mathics/builtin/arithfns/basic.py
index 27f300298..ceb239b0f 100644
--- a/mathics/builtin/arithfns/basic.py
+++ b/mathics/builtin/arithfns/basic.py
@@ -369,11 +369,6 @@ class Power(InfixOperator, MPMathFunction):
mpmath_name = "power"
- messages = {
- "infy": "Infinite expression `1` encountered.",
- "indet": "Indeterminate expression `1` encountered.",
- }
-
nargs = {2}
rules = {
"Power[]": "1",
diff --git a/mathics/builtin/intfns/divlike.py b/mathics/builtin/intfns/divlike.py
index 93593f1a0..1c6d02d83 100644
--- a/mathics/builtin/intfns/divlike.py
+++ b/mathics/builtin/intfns/divlike.py
@@ -5,7 +5,7 @@
"""
import sys
-from typing import List, Optional
+from typing import List, Optional, Union
import sympy
from sympy import Q, ask
@@ -25,12 +25,19 @@
from mathics.core.convert.python import from_bool
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
+from mathics.core.symbols import Symbol
from mathics.core.systemsymbols import (
SymbolComplexInfinity,
+ SymbolIndeterminate,
SymbolQuotient,
SymbolQuotientRemainder,
)
-from mathics.eval.intfns.divlike import eval_GCD, eval_LCM, eval_ModularInverse
+from mathics.eval.intfns.divlike import (
+ eval_GCD,
+ eval_LCM,
+ eval_ModularInverse,
+ eval_Quotient,
+)
class CompositeQ(Builtin):
@@ -294,34 +301,94 @@ def eval(self, a: Integer, b: Integer, m: Integer, evaluation: Evaluation):
class Quotient(Builtin):
"""
- :WMA link:https://reference.wolfram.com/language/ref/Quotient.html
+ :Quotient:https://en.wikipedia.org/wiki/Quotient (
+ :WMA link:https://reference.wolfram.com/language/ref/Quotient.html)
- 'Quotient[m, n]'
-
- computes the integer quotient of $m$ and $n$.
+
- computes the integer quotient of $m$ and $n$. For non-complex numbers, this \
+ equivalent to 'Floor[m/n]'. When a complex number is involved, it is 'Round[m/n]'.
+
- 'Quotient[m, n, d]'
+
- computes the integer quotient of $m-d$ and $n$. For non-complex numbers, this \
+ is equivalent to 'Floor[(m-d)/n]'. When a complex number is involved, it is \
+ 'Round[(m-d)/n]'.
+ ## Plot showing the step-like 'Floor' behavior of 'Quotient':
+ ##
+ ## >> DiscretePlot[Quotient[n, 5], {n, 30}]
+ ## = -Graphics-
+
+ Integer-argument 'Quotient':
>> Quotient[23, 7]
= 3
+
+ Rational-argument 'Quotient':
+ >> Quotient[19/3, 5/2]
+ = 2
+
+ 'Quotient' with inexact numbers:
+ >> Quotient[4.56, 2.5]
+ = 1
+
+ 'Quotient' with two complex numbers is same as 'Round[m/n]':
+ >> Quotient[10.4 + 8 I, 4. + 5 I]
+ = 2
+
+ 'Quotient' for large integers:
+ >> Quotient[10^90, NextPrime[10^80]]
+ = 9999999999
+
+ 'Quotient' threads elementwise over lists:
+ >> Quotient[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 3]
+ = {0, 0, 1, 1, 1, 2, 2, 2, 3, 3}
"""
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
eval_error = Builtin.generic_argument_error
expected_args = (2, 3)
- messages = {
- "infy": "Infinite expression `1` encountered.",
+ rules = {
+ "Quotient[m_Complex, n_?NumberQ]": "Round[m / n]",
+ "Quotient[m_?NumberQ, n_Complex]": "Round[m / n]",
+ "Quotient[l_List, n_?NumberQ]": "Map[Quotient[#, m] &, l]",
+ "Quotient[l_List, n_?NumberQ, d_?NumberQ]": "Map[Quotient[#, m, d] &, l]",
}
- summary_text = "integer quotient"
- def eval(self, m: Integer, n: Integer, evaluation: Evaluation):
- "Quotient[m_Integer, n_Integer]"
+ summary_text = "compute integer quotient"
+
+ def eval(self, m, n, evaluation: Evaluation) -> Union[Symbol, Integer]:
+ "Quotient[m_?NumberQ, n_?NumberQ]"
+ py_m = m.value
+ py_n = n.value
+ if py_n == 0:
+ if py_m == 0:
+ tag = "indet"
+ result = SymbolIndeterminate
+ else:
+ tag = "infy"
+ result = SymbolComplexInfinity
+ evaluation.message("Quotient", tag, Expression(SymbolQuotient, m, n))
+ return result
+ return eval_Quotient(py_m, py_n, 0)
+
+ def eval_with_offset(
+ self, m, n, d, evaluation: Evaluation
+ ) -> Union[Symbol, Integer]:
+ "Quotient[m_?NumberQ, n_?NumberQ, d_?NumberQ]"
py_m = m.value
py_n = n.value
+ py_d = d.value
if py_n == 0:
- evaluation.message("Quotient", "infy", Expression(SymbolQuotient, m, n))
- return SymbolComplexInfinity
- return Integer(py_m // py_n)
+ if py_m - py_d == 0:
+ tag = "indet"
+ result = SymbolIndeterminate
+ else:
+ tag = "infy"
+ result = SymbolComplexInfinity
+ evaluation.message("Quotient", tag, Expression(SymbolQuotient, m, n, d))
+ return result
+ return eval_Quotient(py_m, py_n, py_d)
class QuotientRemainder(Builtin):
diff --git a/mathics/builtin/messages.py b/mathics/builtin/messages.py
index b26333655..d7b0998f4 100644
--- a/mathics/builtin/messages.py
+++ b/mathics/builtin/messages.py
@@ -206,6 +206,7 @@ class General(Builtin):
"Single or list of non-negative integers expected at " "position `1`."
),
"indet": "Indeterminate expression `1` encountered.",
+ "infy": "Infinite expression `1` encountered.",
"innf": "Non-negative integer or Infinity expected at position `1` in `2`",
"int": "Integer expected.",
"intp": "Positive integer expected.",
diff --git a/mathics/eval/intfns/divlike.py b/mathics/eval/intfns/divlike.py
index ca29a95e1..b3f8d8bec 100644
--- a/mathics/eval/intfns/divlike.py
+++ b/mathics/eval/intfns/divlike.py
@@ -42,3 +42,7 @@ def eval_ModularInverse(k: int, n: int) -> Optional[Integer]:
except ValueError:
return
return Integer(r)
+
+
+def eval_Quotient(m, n, d) -> Integer:
+ return Integer((m - d) // n)
diff --git a/test/builtin/intfns/test_divlike.py b/test/builtin/intfns/test_divlike.py
index 0a5cc2782..39fa39a65 100644
--- a/test/builtin/intfns/test_divlike.py
+++ b/test/builtin/intfns/test_divlike.py
@@ -15,7 +15,19 @@
"Quotient[13, 0]",
("Infinite expression Quotient[13, 0] encountered.",),
"ComplexInfinity",
- None,
+ "Check Quotient two-argument divide by 0 error result",
+ ),
+ (
+ "Quotient[1, 0, 1]",
+ ("Indeterminate expression Quotient[1, 0, 1] encountered.",),
+ "Indeterminate",
+ "Check Quotient three-argument 0/0 error result",
+ ),
+ (
+ "Quotient[0, 0]",
+ ("Indeterminate expression Quotient[0, 0] encountered.",),
+ "Indeterminate",
+ "Check Quotient two-argument 0/0 error",
),
("Quotient[-17, 7]", None, "-3", None),
("Quotient[-17, -4]", None, "4", None),