diff --git a/mathics/builtin/intfns/divlike.py b/mathics/builtin/intfns/divlike.py
index 5fb5aede9..93593f1a0 100644
--- a/mathics/builtin/intfns/divlike.py
+++ b/mathics/builtin/intfns/divlike.py
@@ -5,7 +5,7 @@
"""
import sys
-from typing import List
+from typing import List, Optional
import sympy
from sympy import Q, ask
@@ -30,6 +30,7 @@
SymbolQuotient,
SymbolQuotientRemainder,
)
+from mathics.eval.intfns.divlike import eval_GCD, eval_LCM, eval_ModularInverse
class CompositeQ(Builtin):
@@ -121,17 +122,10 @@ class GCD(Builtin):
attributes = A_FLAT | A_LISTABLE | A_ONE_IDENTITY | A_ORDERLESS | A_PROTECTED
summary_text = "greatest common divisor"
- def eval(self, ns, evaluation: Evaluation):
+ def eval(self, ns, evaluation: Evaluation) -> Optional[Integer]:
"GCD[ns___Integer]"
- ns = ns.get_sequence()
- result = 0
- for n in ns:
- value = n.value
- if value is None:
- return
- result = sympy.gcd(result, value)
- return Integer(result)
+ return eval_GCD(ns.get_sequence())
class LCM(Builtin):
@@ -157,20 +151,14 @@ class LCM(Builtin):
}
summary_text = "least common multiple"
- def eval(self, ns: List[Integer], evaluation: Evaluation):
+ def eval(self, ns: List[Integer], evaluation: Evaluation) -> Optional[Integer]:
"LCM[ns___Integer]"
- ns = ns.get_sequence()
- if len(ns) == 0:
+ ns_tuple = ns.get_sequence()
+ if len(ns_tuple) == 0:
evaluation.message("LCM", "argm")
return
- result = 1
- for n in ns:
- value = n.value
- if value is None:
- return
- result = sympy.lcm(result, value)
- return Integer(result)
+ return eval_LCM(ns_tuple)
class Mod(SympyFunction):
@@ -247,13 +235,9 @@ class ModularInverse(SympyFunction):
summary_text = "returns the modular inverse $k^(-1)$ mod $n$"
sympy_name = "mod_inverse"
- def eval_k_n(self, k: Integer, n: Integer, evaluation: Evaluation):
+ def eval(self, k: Integer, n: Integer, evaluation: Evaluation) -> Optional[Integer]:
"ModularInverse[k_Integer, n_Integer]"
- try:
- r = sympy.mod_inverse(k.value, n.value)
- except ValueError:
- return
- return Integer(r)
+ return eval_ModularInverse(k.value, n.value)
class PowerMod(Builtin):
diff --git a/mathics/builtin/intfns/misc.py b/mathics/builtin/intfns/misc.py
deleted file mode 100644
index bea98cf07..000000000
--- a/mathics/builtin/intfns/misc.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-Miscelanea of Integer Functions
-"""
-
-
-from mathics.core.attributes import A_LISTABLE, A_PROTECTED
-from mathics.core.builtin import MPMathFunction
-
-
-class BernoulliB(MPMathFunction):
- """
- :WMA link:https://reference.wolfram.com/language/ref/BernoulliB.html
-
-
- - 'BernoulliB'[$n$]
-
- represents the Bernoulli number $B_n$.
-
-
- 'BernouilliB'[$n$, $x$]
-
- represents the Bernoulli polynomial $B_n(x)$.
-
-
- >> BernoulliB[42]
- = 1520097643918070802691 / 1806
-
- First five Bernoulli numbers:
-
- >> Table[BernoulliB[k], {k, 0, 5}]
- = ...
-
- ## This must be (according to WMA)
- ## = {1, -1 / 2, 1 / 6, 0, -1 / 30, 0}
- ## but for some reason, in the CI the previous test produces
- ## the output:
- ## {1, 1 / 2, 1 / 6, 0, -1 / 30, 0}
-
- First five Bernoulli polynomials:
-
- >> Table[BernoulliB[k, z], {k, 0, 3}]
- = {1, -1 / 2 + z, 1 / 6 - z + z ^ 2, z / 2 - 3 z ^ 2 / 2 + z ^ 3}
- """
-
- attributes = A_PROTECTED | A_LISTABLE
- mpmath_name = "bernoulli"
- nargs = {1, 2}
- summary_text = "Bernoulli number and function"
- sympy_name = "bernoulli"
diff --git a/mathics/builtin/intfns/recurrence.py b/mathics/builtin/intfns/recurrence.py
index d377cfec0..1198ed5bf 100644
--- a/mathics/builtin/intfns/recurrence.py
+++ b/mathics/builtin/intfns/recurrence.py
@@ -8,9 +8,9 @@
as a function of the preceding terms.
"""
-
from sympy.functions.combinatorial.numbers import stirling
+import mathics.eval.tracing as tracing
from mathics.core.atoms import Integer
from mathics.core.attributes import (
A_LISTABLE,
@@ -22,46 +22,91 @@
from mathics.core.evaluation import Evaluation
-class Fibonacci(MPMathFunction):
+class BernoulliB(MPMathFunction):
"""
-
- :Fibonacci Sequence:
- https://en.wikipedia.org/wiki/Fibonacci_sequence, (
- :WMA link:https://reference.wolfram.com/language/ref/Fibonacci.html)
+ :Bernoulli number:
+ https://en.wikipedia.org/wiki/Bernoulli_number (:WMA link:https://reference.wolfram.com/language/ref/BernoulliB.html)
-
- - 'Fibonacci'[$n$]
-
- computes the $n$-th Fibonacci number.
-
- 'Fibonacci'[$n$, $x$]
-
- computes the Fibonacci polynomial $F_n(x)$.
-
+
+ - 'BernoulliB'[$n$]
+
- represents the Bernoulli number $B_n$.
- >> Fibonacci[0]
- = 0
- >> Fibonacci[1]
- = 1
- >> Fibonacci[10]
- = 55
- >> Fibonacci[200]
- = 280571172992510140037611932413038677189525
- >> Fibonacci[7, x]
- = 1 + 6 x ^ 2 + 5 x ^ 4 + x ^ 6
+
- 'BernouilliB'[$n$, $x$]
+
- represents the Bernoulli polynomial $B_n(x)$.
+
- See also
- :LinearRecurrence:
- /doc/reference-of-built-in-symbols/integer-functions/recurrence-and-sum-functions/linearrecurrence.
+ >> BernoulliB[42]
+ = 1520097643918070802691 / 1806
+
+ First five Bernoulli numbers:
+
+ >> Table[BernoulliB[k], {k, 0, 5}]
+ = ...
+
+ ## This must be (according to WMA)
+ ## = {1, -1 / 2, 1 / 6, 0, -1 / 30, 0}
+ ## but for some reason, in the CI the previous test produces
+ ## the output:
+ ## {1, 1 / 2, 1 / 6, 0, -1 / 30, 0}
+
+ First five Bernoulli polynomials:
+
+ >> Table[BernoulliB[k, z], {k, 0, 3}]
+ = {1, -1 / 2 + z, 1 / 6 - z + z ^ 2, z / 2 - 3 z ^ 2 / 2 + z ^ 3}
+ """
+
+ attributes = A_LISTABLE | A_PROTECTED
+ eval_error = Builtin.generic_argument_error
+ expected_args = (1, 2)
+ mpmath_name = "bernoulli"
+ nargs = {1, 2}
+ summary_text = "Bernoulli numbers and polynomials"
+ sympy_name = "bernoulli"
+
+
+class Fibonacci(MPMathFunction):
+ """
+
+ :Fibonacci Sequence:
+ https://en.wikipedia.org/wiki/Fibonacci_sequence and
+ :Fibonacci polynomials:
+ https://en.wikipedia.org/wiki/Fibonacci_polynomials (
+ :WMA link:https://reference.wolfram.com/language/ref/Fibonacci.html)
+
+
+ - 'Fibonacci'[$n$]
+
- computes the $n$-th Fibonacci number.
+
- 'Fibonacci'[$n$, $x$]
+
- computes the Fibonacci polynomial $F_n(x)$.
+
+
+ >> Fibonacci[0]
+ = 0
+ >> Fibonacci[1]
+ = 1
+ >> Fibonacci[10]
+ = 55
+ >> Fibonacci[200]
+ = 280571172992510140037611932413038677189525
+ >> Fibonacci[7, x]
+ = 1 + 6 x ^ 2 + 5 x ^ 4 + x ^ 6
+
+ See also
+ :LinearRecurrence:
+ /doc/reference-of-built-in-symbols/integer-functions/recurrence-and-sum-functions/linearrecurrence.
"""
- nargs = {1}
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED | A_READ_PROTECTED
- sympy_name = "fibonacci"
+ eval_error = Builtin.generic_argument_error
+ expected_args = (1, 2)
mpmath_name = "fibonacci"
- summary_text = "Fibonacci's numbers"
-
+ nargs = {1}
rules = {
"Fibonacci[0, x_]": "0",
"Fibonacci[n_Integer?Negative, x_]": "Fibonacci[-n, x]",
}
+ summary_text = "Fibonacci sequences and polynomials"
+ sympy_name = "fibonacci"
class HarmonicNumber(MPMathFunction):
@@ -81,19 +126,22 @@ class HarmonicNumber(MPMathFunction):
= 2.03806
"""
+ attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_READ_PROTECTED | A_PROTECTED
+ eval_error = Builtin.generic_argument_error
+ expected_args = (1, 2)
+ mpmath_name = "harmonic"
rules = {
"HarmonicNumber[-1]": "ComplexInfinity",
}
summary_text = "Harmonic numbers"
- mpmath_name = "harmonic"
sympy_name = "harmonic"
class LinearRecurrence(Builtin):
"""
:Linear recurrence with constant coefficients:
- https://en.wikipedia.org/wiki/Linear_recurrence_with_constant_coefficients,
- :WMA link:https://reference.wolfram.com/language/ref/LinearRecurrence.html
+ https://en.wikipedia.org/wiki/Linear_recurrence_with_constant_coefficients (
+ :WMA link:https://reference.wolfram.com/language/ref/LinearRecurrence.html)
- 'LinearRecurrence'[$ker$, $init$, $n$]
@@ -124,13 +172,15 @@ class LinearRecurrence(Builtin):
"""
attributes = A_PROTECTED | A_READ_PROTECTED
- summary_text = "linear recurrence"
+ eval_error = Builtin.generic_argument_error
+ expected_args = 3
rules = {
"LinearRecurrence[ker_List, init_List, n_Integer]": "Nest[Append[#, Reverse[ker] . Take[#, -Length[ker]]] &, init, n - Length[init]]",
"LinearRecurrence[ker_List, init_List, {n_Integer?Positive}]": "LinearRecurrence[ker, init, n][[n]]",
"LinearRecurrence[ker_List, init_List, {nmin_Integer?Positive, nmax_Integer?Positive}]": "LinearRecurrence[ker, init, nmax][[nmin;;nmax]]",
}
+ summary_text = "linear recurrence"
# Note: WL allows StirlingS1[{2, 4, 6}, 2], but we don't (yet).
@@ -155,17 +205,18 @@ class StirlingS1(Builtin):
"""
attributes = A_LISTABLE | A_PROTECTED
-
+ eval_error = Builtin.generic_argument_error
+ expected_args = 2
+ mpmath_name = "stirling1"
nargs = {2}
summary_text = "Stirling numbers of the first kind"
sympy_name = "functions.combinatorial.stirling"
- mpmath_name = "stirling1"
def eval(self, n: Integer, m: Integer, evaluation: Evaluation):
- "%(name)s[n_Integer, m_Integer]"
- n_value = n.value
- m_value = m.value
- return Integer(stirling(n_value, m_value, kind=1, signed=True))
+ "StirlingS1[n_Integer, m_Integer]"
+ return Integer(
+ tracing.run_sympy(stirling, n.value, m.value, kind=1, signed=True)
+ )
class StirlingS2(Builtin):
@@ -188,13 +239,13 @@ class StirlingS2(Builtin):
"""
attributes = A_LISTABLE | A_PROTECTED
- nargs = {2}
+ eval_error = Builtin.generic_argument_error
+ expected_args = 2
sympy_name = "functions.combinatorial.numbers.stirling"
mpmath_name = "stirling2"
+ nargs = {2}
summary_text = "Stirling numbers of the second kind"
def eval(self, m: Integer, n: Integer, evaluation: Evaluation):
- "%(name)s[n_Integer, m_Integer]"
- n_value = n.value
- m_value = m.value
- return Integer(stirling(n_value, m_value, kind=2))
+ "StirlingS2[n_Integer, m_Integer]"
+ return Integer(tracing.run_sympy(stirling, n.value, m.value, kind=2))
diff --git a/mathics/eval/hyperbolic.py b/mathics/eval/hyperbolic.py
index 75ac87b85..598486667 100644
--- a/mathics/eval/hyperbolic.py
+++ b/mathics/eval/hyperbolic.py
@@ -1,5 +1,5 @@
"""
-Mathics3 builtins from mathics.core.numbers.hyperbolic
+Mathics3 builtins from mathics.builtin.numbers.hyperbolic
"""
from sympy import Symbol as SympySymbol
diff --git a/mathics/eval/intfns/__init__.py b/mathics/eval/intfns/__init__.py
new file mode 100644
index 000000000..baa92ba1b
--- /dev/null
+++ b/mathics/eval/intfns/__init__.py
@@ -0,0 +1,3 @@
+"""
+Evaluation functions associated with mathics.builtin.intfns.
+"""
diff --git a/mathics/eval/intfns/divlike.py b/mathics/eval/intfns/divlike.py
new file mode 100644
index 000000000..ca29a95e1
--- /dev/null
+++ b/mathics/eval/intfns/divlike.py
@@ -0,0 +1,44 @@
+"""
+Mathics3 builtins from mathics.builtin.intfns.divlike
+"""
+
+from typing import Optional
+
+import sympy
+
+import mathics.eval.tracing as tracing
+from mathics.core.atoms import Integer, Integer0
+
+
+def eval_GCD(ns: tuple) -> Optional[Integer]:
+ if len(ns) == 0:
+ return Integer0
+ if (result := ns[0].value) is None:
+ return
+ for n in ns[1:]:
+ value = n.value
+ if value is None:
+ return
+ result = tracing.run_sympy(sympy.gcd, result, value)
+ return Integer(result)
+
+
+def eval_LCM(ns: tuple) -> Optional[Integer]:
+ if len(ns) == 0:
+ return Integer0
+ if (result := ns[0].value) is None:
+ return
+ for n in ns[1:]:
+ value = n.value
+ if value is None:
+ return
+ result = tracing.run_sympy(sympy.lcm, result, value)
+ return Integer(result)
+
+
+def eval_ModularInverse(k: int, n: int) -> Optional[Integer]:
+ try:
+ r = tracing.run_sympy(sympy.mod_inverse, k, n)
+ except ValueError:
+ return
+ return Integer(r)
diff --git a/mathics/eval/tracing.py b/mathics/eval/tracing.py
index 55ba37bc7..997598a80 100644
--- a/mathics/eval/tracing.py
+++ b/mathics/eval/tracing.py
@@ -224,13 +224,13 @@ def wrapper(*args) -> Any:
@trace_fn_call_event
-def trace_call(fn: Callable, *args) -> Any:
+def trace_call(fn: Callable, *args, **kwargs) -> Any:
"""
Runs a function inside a decorator that
traps call and return information that can be used in
a tracer or debugger
"""
- return fn(*args)
+ return fn(*args, **kwargs)
def call_event_print(event: TraceEvent, fn: Callable, *args) -> bool:
@@ -254,22 +254,22 @@ def return_event_print(event: TraceEvent, result: Any) -> Any:
return result
-def run_fast(fn: Callable, *args) -> Any:
+def run_fast(fn: Callable, *args, **kwargs) -> Any:
"""
Fast-path call to run a event-tracable function, but no tracing is
in effect. This add another level of indirection to
some function calls, but Jit'ing will probably remove this
when it is a bottleneck.
"""
- return fn(*args)
+ return fn(*args, **kwargs)
-def run_mpmath_traced(fn: Callable, *args) -> Any:
- return trace_call(TraceEvent.mpmath, fn, *args)
+def run_mpmath_traced(fn: Callable, *args, **kwargs) -> Any:
+ return trace_call(TraceEvent.mpmath, fn, *args, **kwargs)
-def run_sympy_traced(fn: Callable, *args) -> Any:
- return trace_call(TraceEvent.SymPy, fn, *args)
+def run_sympy_traced(fn: Callable, *args, **kwargs) -> Any:
+ return trace_call(TraceEvent.SymPy, fn, *args, **kwargs)
# The below functions are changed by a tracer or debugger