Math tools expanded (#293)
Migrated all interfaces to get and return strings. Added tests and evals for all functions (except the random generation) Math functions are now organized into different math categories --------- Co-authored-by: Nate Barbettini <nate@arcade-ai.com>
This commit is contained in:
parent
6cafadac9c
commit
f04087b389
16 changed files with 722 additions and 103 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import math
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from arcade.sdk import tool
|
||||
|
|
@ -6,70 +6,89 @@ from arcade.sdk import tool
|
|||
|
||||
@tool
|
||||
def add(
|
||||
a: Annotated[int, "The first number"], b: Annotated[int, "The second number"]
|
||||
) -> Annotated[int, "The sum of the two numbers"]:
|
||||
a: Annotated[str, "The first number as a string"],
|
||||
b: Annotated[str, "The second number as a string"],
|
||||
) -> Annotated[str, "The sum of the two numbers as a string"]:
|
||||
"""
|
||||
Add two numbers together
|
||||
"""
|
||||
return a + b
|
||||
# Use Decimal for arbitrary precision
|
||||
a_decimal = Decimal(a)
|
||||
b_decimal = Decimal(b)
|
||||
return str(a_decimal + b_decimal)
|
||||
|
||||
|
||||
@tool
|
||||
def subtract(
|
||||
a: Annotated[int, "The first number"], b: Annotated[int, "The second number"]
|
||||
) -> Annotated[int, "The difference of the two numbers"]:
|
||||
a: Annotated[str, "The first number as a string"],
|
||||
b: Annotated[str, "The second number as a string"],
|
||||
) -> Annotated[str, "The difference of the two numbers as a string"]:
|
||||
"""
|
||||
Subtract two numbers
|
||||
"""
|
||||
return a - b
|
||||
# Use Decimal for arbitrary precision
|
||||
a_decimal = Decimal(a)
|
||||
b_decimal = Decimal(b)
|
||||
return str(a_decimal - b_decimal)
|
||||
|
||||
|
||||
@tool
|
||||
def multiply(
|
||||
a: Annotated[int, "The first number"], b: Annotated[int, "The second number"]
|
||||
) -> Annotated[int, "The product of the two numbers"]:
|
||||
a: Annotated[str, "The first number as a string"],
|
||||
b: Annotated[str, "The second number as a string"],
|
||||
) -> Annotated[str, "The product of the two numbers as a string"]:
|
||||
"""
|
||||
Multiply two numbers together
|
||||
"""
|
||||
return a * b
|
||||
# Use Decimal for arbitrary precision
|
||||
a_decimal = Decimal(a)
|
||||
b_decimal = Decimal(b)
|
||||
return str(a_decimal * b_decimal)
|
||||
|
||||
|
||||
@tool
|
||||
def divide(
|
||||
a: Annotated[int, "The first number"], b: Annotated[int, "The second number"]
|
||||
) -> Annotated[float, "The quotient of the two numbers"]:
|
||||
a: Annotated[str, "The first number as a string"],
|
||||
b: Annotated[str, "The second number as a string"],
|
||||
) -> Annotated[str, "The quotient of the two numbers as a string"]:
|
||||
"""
|
||||
Divide two numbers
|
||||
"""
|
||||
return a / b
|
||||
|
||||
|
||||
@tool
|
||||
def sqrt(
|
||||
a: Annotated[int, "The number to square root"],
|
||||
) -> Annotated[float, "The square root of the number"]:
|
||||
"""
|
||||
Get the square root of a number
|
||||
"""
|
||||
return math.sqrt(a)
|
||||
# Use Decimal for arbitrary precision
|
||||
a_decimal = Decimal(a)
|
||||
b_decimal = Decimal(b)
|
||||
return str(a_decimal / b_decimal)
|
||||
|
||||
|
||||
@tool
|
||||
def sum_list(
|
||||
numbers: Annotated[list[float], "The list of numbers"],
|
||||
) -> Annotated[float, "The sum of the numbers in the list"]:
|
||||
numbers: Annotated[list[str], "The list of numbers as strings"],
|
||||
) -> Annotated[str, "The sum of the numbers in the list as a string"]:
|
||||
"""
|
||||
Sum all numbers in a list
|
||||
"""
|
||||
return sum(numbers)
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(sum([Decimal(n) for n in numbers]))
|
||||
|
||||
|
||||
@tool
|
||||
def sum_range(
|
||||
start: Annotated[int, "The start of the range to sum"],
|
||||
end: Annotated[int, "The end of the range to sum"],
|
||||
) -> Annotated[int, "The sum of the numbers in the list"]:
|
||||
start: Annotated[str, "The start of the range to sum as a string"],
|
||||
end: Annotated[str, "The end of the range to sum as a string"],
|
||||
) -> Annotated[str, "The sum of the numbers in the list as a string"]:
|
||||
"""
|
||||
Sum all numbers from start through end
|
||||
"""
|
||||
return sum(list(range(start, end + 1)))
|
||||
return str(sum(list(range(int(start), int(end) + 1))))
|
||||
|
||||
|
||||
@tool
|
||||
def mod(
|
||||
a: Annotated[str, "The dividend as a string"],
|
||||
b: Annotated[str, "The divisor as a string"],
|
||||
) -> Annotated[str, "The remainder after dividing a by b as a string"]:
|
||||
"""
|
||||
Calculate the remainder (modulus) of one number divided by another
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(Decimal(a) % Decimal(b))
|
||||
|
|
|
|||
29
toolkits/math/arcade_math/tools/exponents.py
Normal file
29
toolkits/math/arcade_math/tools/exponents.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import math
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from arcade.sdk import tool
|
||||
|
||||
|
||||
@tool
|
||||
def log(
|
||||
a: Annotated[str, "The number to take the logarithm of as a string"],
|
||||
base: Annotated[str, "The logarithmic base as a string"],
|
||||
) -> Annotated[str, "The logarithm of the number with the specified base as a string"]:
|
||||
"""
|
||||
Calculate the logarithm of a number with a given base
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(math.log(Decimal(a), Decimal(base)))
|
||||
|
||||
|
||||
@tool
|
||||
def power(
|
||||
a: Annotated[str, "The base number as a string"],
|
||||
b: Annotated[str, "The exponent as a string"],
|
||||
) -> Annotated[str, "The result of raising a to the power of b as a string"]:
|
||||
"""
|
||||
Calculate one number raised to the power of another
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(Decimal(a) ** Decimal(b))
|
||||
40
toolkits/math/arcade_math/tools/miscellaneous.py
Normal file
40
toolkits/math/arcade_math/tools/miscellaneous.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import math
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from arcade.sdk import tool
|
||||
|
||||
|
||||
@tool
|
||||
def abs_val(
|
||||
a: Annotated[str, "The number as a string"],
|
||||
) -> Annotated[str, "The absolute value of the number as a string"]:
|
||||
"""
|
||||
Calculate the absolute value of a number
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(abs(Decimal(a)))
|
||||
|
||||
|
||||
@tool
|
||||
def factorial(
|
||||
a: Annotated[str, "The non-negative integer to compute the factorial for as a string"],
|
||||
) -> Annotated[str, "The factorial of the number as a string"]:
|
||||
"""
|
||||
Compute the factorial of a non-negative integer
|
||||
Returns "1" for "0"
|
||||
"""
|
||||
return str(math.factorial(int(a)))
|
||||
|
||||
|
||||
@tool
|
||||
def sqrt(
|
||||
a: Annotated[str, "The number to square root as a string"],
|
||||
) -> Annotated[str, "The square root of the number as a string"]:
|
||||
"""
|
||||
Get the square root of a number
|
||||
If
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
a_decimal = Decimal(a)
|
||||
return str(a_decimal.sqrt())
|
||||
|
|
@ -6,31 +6,33 @@ from arcade.sdk import tool
|
|||
|
||||
@tool
|
||||
def generate_random_int(
|
||||
min_value: Annotated[int, "The minimum value of the random integer"],
|
||||
max_value: Annotated[int, "The maximum value of the random integer"],
|
||||
min_value: Annotated[str, "The minimum value of the random integer as a string"],
|
||||
max_value: Annotated[str, "The maximum value of the random integer as a string"],
|
||||
seed: Annotated[
|
||||
Optional[int],
|
||||
"The seed for the random number generator. If None, the current system time is used.",
|
||||
Optional[str],
|
||||
"The seed for the random number generator as a string."
|
||||
" If None, the current system time is used.",
|
||||
] = None,
|
||||
) -> Annotated[int, "A random integer between min_value and max_value"]:
|
||||
) -> Annotated[str, "A random integer between min_value and max_value as a string"]:
|
||||
"""Generate a random integer between min_value and max_value (inclusive)."""
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
random.seed(int(seed))
|
||||
|
||||
return random.randint(min_value, max_value) # noqa: S311
|
||||
return str(random.randint(int(min_value), int(max_value))) # noqa: S311
|
||||
|
||||
|
||||
@tool
|
||||
def generate_random_float(
|
||||
min_value: Annotated[float, "The minimum value of the random float"],
|
||||
max_value: Annotated[float, "The maximum value of the random float"],
|
||||
min_value: Annotated[str, "The minimum value of the random float as a string"],
|
||||
max_value: Annotated[str, "The maximum value of the random float as a string"],
|
||||
seed: Annotated[
|
||||
Optional[int],
|
||||
"The seed for the random number generator. If None, the current system time is used.",
|
||||
Optional[str],
|
||||
"The seed for the random number generator as a string."
|
||||
" If None, the current system time is used.",
|
||||
] = None,
|
||||
) -> Annotated[float, "A random float between min_value and max_value"]:
|
||||
) -> Annotated[str, "A random float between min_value and max_value as a string"]:
|
||||
"""Generate a random float between min_value and max_value."""
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
random.seed(int(seed))
|
||||
|
||||
return random.uniform(min_value, max_value) # noqa: S311
|
||||
return str(random.uniform(float(min_value), float(max_value))) # noqa: S311
|
||||
|
|
|
|||
30
toolkits/math/arcade_math/tools/rational.py
Normal file
30
toolkits/math/arcade_math/tools/rational.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import math
|
||||
from typing import Annotated
|
||||
|
||||
from arcade.sdk import tool
|
||||
|
||||
|
||||
@tool
|
||||
def gcd(
|
||||
a: Annotated[str, "First integer as a string"],
|
||||
b: Annotated[str, "Second integer as a string"],
|
||||
) -> Annotated[str, "The greatest common divisor of a and b as a string"]:
|
||||
"""
|
||||
Calculate the greatest common divisor (GCD) of two integers.
|
||||
"""
|
||||
return str(math.gcd(int(a), int(b)))
|
||||
|
||||
|
||||
@tool
|
||||
def lcm(
|
||||
a: Annotated[str, "First integer as a string"],
|
||||
b: Annotated[str, "Second integer as a string"],
|
||||
) -> Annotated[str, "The least common multiple of a and b as a string"]:
|
||||
"""
|
||||
Calculate the least common multiple (LCM) of two integers.
|
||||
Returns "0" if either integer is 0.
|
||||
"""
|
||||
a_int, b_int = int(a), int(b)
|
||||
if a_int == 0 or b_int == 0:
|
||||
return "0"
|
||||
return str(abs(a_int * b_int) // math.gcd(a_int, b_int))
|
||||
44
toolkits/math/arcade_math/tools/rounding.py
Normal file
44
toolkits/math/arcade_math/tools/rounding.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import math
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from arcade.sdk import tool
|
||||
|
||||
|
||||
@tool
|
||||
def ceil(
|
||||
a: Annotated[str, "The number to round up as a string"],
|
||||
) -> Annotated[str, "The smallest integer greater than or equal to the number as a string"]:
|
||||
"""
|
||||
Return the ceiling of a number
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(math.ceil(Decimal(a)))
|
||||
|
||||
|
||||
@tool
|
||||
def floor(
|
||||
a: Annotated[str, "The number to round down as a string"],
|
||||
) -> Annotated[str, "The largest integer less than or equal to the number as a string"]:
|
||||
"""
|
||||
Return the floor of a number
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(math.floor(Decimal(a)))
|
||||
|
||||
|
||||
@tool
|
||||
def round_num(
|
||||
value: Annotated[str, "The number to round as a string"],
|
||||
ndigits: Annotated[str, "The number of digits after the decimal point as a string"],
|
||||
) -> Annotated[str, "The number rounded to the specified number of digits as a string"]:
|
||||
"""
|
||||
Round a number to a specified number of positive digits
|
||||
"""
|
||||
ndigits_int = int(ndigits)
|
||||
if ndigits_int >= 0:
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(round(Decimal(value), int(ndigits_int)))
|
||||
# cast value from str -> float -> int here because rounding with negative
|
||||
# decimals is only useful for weird math
|
||||
return str(round(int(float(value)), int(ndigits_int)))
|
||||
31
toolkits/math/arcade_math/tools/statistics.py
Normal file
31
toolkits/math/arcade_math/tools/statistics.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from decimal import Decimal
|
||||
from statistics import median as stats_median
|
||||
from typing import Annotated
|
||||
|
||||
from arcade.sdk import tool
|
||||
|
||||
|
||||
@tool
|
||||
def avg(
|
||||
numbers: Annotated[list[str], "The list of numbers as strings"],
|
||||
) -> Annotated[str, "The average (mean) of the numbers in the list as a string"]:
|
||||
"""
|
||||
Calculate the average (mean) of a list of numbers.
|
||||
Returns "0.0" if the list is empty.
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
d_numbers = [Decimal(n) for n in numbers]
|
||||
return str(sum(d_numbers) / len(d_numbers)) if d_numbers else "0.0"
|
||||
|
||||
|
||||
@tool
|
||||
def median(
|
||||
numbers: Annotated[list[str], "A list of numbers as strings"],
|
||||
) -> Annotated[str, "The median value of the numbers in the list as a string"]:
|
||||
"""
|
||||
Calculate the median of a list of numbers.
|
||||
Returns "0.0" if the list is empty.
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
d_numbers = [Decimal(n) for n in numbers]
|
||||
return str(stats_median(d_numbers)) if d_numbers else "0.0"
|
||||
27
toolkits/math/arcade_math/tools/trigonometry.py
Normal file
27
toolkits/math/arcade_math/tools/trigonometry.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import math
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from arcade.sdk import tool
|
||||
|
||||
|
||||
@tool
|
||||
def deg_to_rad(
|
||||
degrees: Annotated[str, "Angle in degrees as a string"],
|
||||
) -> Annotated[str, "Angle in radians as a string"]:
|
||||
"""
|
||||
Convert an angle from degrees to radians.
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(math.radians(Decimal(degrees)))
|
||||
|
||||
|
||||
@tool
|
||||
def rad_to_deg(
|
||||
radians: Annotated[str, "Angle in radians as a string"],
|
||||
) -> Annotated[str, "Angle in degrees as a string"]:
|
||||
"""
|
||||
Convert an angle from radians to degrees.
|
||||
"""
|
||||
# Use Decimal for arbitrary precision
|
||||
return str(math.degrees(Decimal(radians)))
|
||||
|
|
@ -8,7 +8,41 @@ from arcade.sdk.eval import (
|
|||
)
|
||||
|
||||
import arcade_math
|
||||
from arcade_math.tools.arithmetic import add, sqrt
|
||||
from arcade_math.tools.arithmetic import (
|
||||
add,
|
||||
divide,
|
||||
mod,
|
||||
multiply,
|
||||
subtract,
|
||||
sum_list,
|
||||
sum_range,
|
||||
)
|
||||
from arcade_math.tools.exponents import (
|
||||
log,
|
||||
power,
|
||||
)
|
||||
from arcade_math.tools.miscellaneous import (
|
||||
abs_val,
|
||||
factorial,
|
||||
sqrt,
|
||||
)
|
||||
from arcade_math.tools.rational import (
|
||||
gcd,
|
||||
lcm,
|
||||
)
|
||||
from arcade_math.tools.rounding import (
|
||||
ceil,
|
||||
floor,
|
||||
round_num,
|
||||
)
|
||||
from arcade_math.tools.statistics import (
|
||||
avg,
|
||||
median,
|
||||
)
|
||||
from arcade_math.tools.trigonometry import (
|
||||
deg_to_rad,
|
||||
rad_to_deg,
|
||||
)
|
||||
|
||||
# Evaluation rubric
|
||||
rubric = EvalRubric(
|
||||
|
|
@ -30,40 +64,68 @@ def math_eval_suite():
|
|||
rubric=rubric,
|
||||
)
|
||||
|
||||
suite.add_case(
|
||||
name="Add two large numbers",
|
||||
user_message="Add 12345 and 987654321",
|
||||
expected_tool_calls=[
|
||||
ExpectedToolCall(
|
||||
func=add,
|
||||
args={
|
||||
"a": 12345,
|
||||
"b": 987654321,
|
||||
},
|
||||
)
|
||||
],
|
||||
rubric=rubric,
|
||||
critics=[
|
||||
BinaryCritic(critic_field="a", weight=0.5), # TODO: weight should be optional
|
||||
BinaryCritic(critic_field="b", weight=0.5),
|
||||
],
|
||||
)
|
||||
list_param = ["1", "2", "3", "4", "5"]
|
||||
funcs_to_expression_and_params = [
|
||||
# unary
|
||||
(sqrt, "What's the square root of {a}?", {"a": "25"}),
|
||||
(abs_val, "What's the absolute value of {a}?", {"a": "-10"}),
|
||||
(factorial, "What's the factorial of {a}?", {"a": "5"}),
|
||||
(deg_to_rad, "Convert {degrees} from degrees to radians", {"degrees": "180"}),
|
||||
(rad_to_deg, "Convert {radians} from radias to degrees", {"radians": "3.14"}),
|
||||
(ceil, "Compute the ceiling of {a}", {"a": "3.14"}),
|
||||
(floor, "Compute the floor of {a}", {"a": "3.14"}),
|
||||
# binary
|
||||
(add, "Add {a} and {b}", {"a": "12345", "b": "987654321"}),
|
||||
(subtract, "Subtract {b} from {a}", {"a": "987654321", "b": "12345"}),
|
||||
(multiply, "Multiply {a} and {b}", {"a": "12345", "b": "567890"}),
|
||||
(divide, "What is {a} divided by {b}?", {"a": "1234123479", "b": "123"}),
|
||||
(
|
||||
sum_range,
|
||||
"What's the sum of all numbers from {start} to {end}?",
|
||||
{"start": "10", "end": "345"},
|
||||
),
|
||||
(mod, "What's the remainder of dividing {a} by {b}?", {"a": "234", "b": "17"}),
|
||||
(power, "Raise {a} to the power of {b}", {"a": "2", "b": "8"}),
|
||||
(log, "What's the logarithm of {a} with base {base}?", {"a": "8", "base": "2"}),
|
||||
(
|
||||
round_num,
|
||||
"Round {value} to {ndigits} decimal places",
|
||||
{"value": "12.23746234", "ndigits": "3"},
|
||||
),
|
||||
(gcd, "Find the greatest common divisor of {a} and {b}", {"a": "50", "b": "10"}),
|
||||
(lcm, "FInd the least common multiple of {a} and {b}", {"a": "7", "b": "13"}),
|
||||
# n-nary
|
||||
(
|
||||
sum_list,
|
||||
f"Calculate the sum of these numbers: {' '.join(list_param)}",
|
||||
{"numbers": list_param},
|
||||
),
|
||||
(
|
||||
avg,
|
||||
f"Find the average of these numbers: {' '.join(list_param)}",
|
||||
{"numbers": list_param},
|
||||
),
|
||||
(
|
||||
median,
|
||||
f"Find the median of these numbers: {' '.join(list_param)}",
|
||||
{"numbers": list_param},
|
||||
),
|
||||
]
|
||||
|
||||
suite.add_case(
|
||||
name="Take the square root of a large number",
|
||||
user_message="What is the square root of 3224990521?",
|
||||
expected_tool_calls=[
|
||||
ExpectedToolCall(
|
||||
func=sqrt,
|
||||
args={
|
||||
"a": 3224990521,
|
||||
},
|
||||
)
|
||||
],
|
||||
rubric=rubric,
|
||||
critics=[
|
||||
BinaryCritic(critic_field="a", weight=1.0),
|
||||
],
|
||||
)
|
||||
for func, expression, params in funcs_to_expression_and_params:
|
||||
parametrized_expression = expression.format(**params)
|
||||
num_params = len(params)
|
||||
suite.add_case(
|
||||
name=parametrized_expression,
|
||||
user_message=parametrized_expression,
|
||||
expected_tool_calls=[
|
||||
ExpectedToolCall(
|
||||
func=func,
|
||||
args=params,
|
||||
)
|
||||
],
|
||||
rubric=rubric,
|
||||
critics=[BinaryCritic(critic_field=param, weight=1.0 / num_params) for param in params],
|
||||
)
|
||||
|
||||
return suite
|
||||
|
|
|
|||
|
|
@ -4,47 +4,139 @@ from arcade.sdk.errors import ToolExecutionError
|
|||
from arcade_math.tools.arithmetic import (
|
||||
add,
|
||||
divide,
|
||||
mod,
|
||||
multiply,
|
||||
sqrt,
|
||||
subtract,
|
||||
sum_list,
|
||||
sum_range,
|
||||
)
|
||||
|
||||
|
||||
def test_add():
|
||||
assert add(1, 2) == 3
|
||||
assert add(-1, 1) == 0
|
||||
assert add(0.5, 10.9) == 11.4
|
||||
@pytest.mark.parametrize(
|
||||
"a, b, expected",
|
||||
[
|
||||
("1", "2", "3"),
|
||||
("-1", "1", "0"),
|
||||
("0.5", "10.9", "11.4"),
|
||||
# Big ints
|
||||
("12345678901234567890", "9876543210987654321", "22222222112222222211"),
|
||||
# Big floats
|
||||
(
|
||||
"12345678901234567890.120",
|
||||
"9876543210987654321.987",
|
||||
"22222222112222222212.107",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_add(a, b, expected):
|
||||
assert add(a, b) == expected
|
||||
|
||||
|
||||
def test_subtract():
|
||||
assert subtract(2, 1) == 1
|
||||
assert subtract(2, 3.5) == -1.5
|
||||
@pytest.mark.parametrize(
|
||||
"a, b, expected",
|
||||
[
|
||||
("1", "2", "-1"),
|
||||
("-1", "1", "-2"),
|
||||
("0.5", "10.9", "-10.4"),
|
||||
# Big ints
|
||||
("12345678901234567890", "12323456679012345668", "22222222222222222"),
|
||||
# Big floats
|
||||
(
|
||||
"12345678901234567890.120",
|
||||
"12343557689113355768.9079",
|
||||
"2121212121212121.2121",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_subtract(a, b, expected):
|
||||
assert subtract(a, b) == expected
|
||||
|
||||
|
||||
def test_multiply():
|
||||
assert multiply(2, 3) == 6
|
||||
assert multiply(-1, 1.5) == -1.5
|
||||
@pytest.mark.parametrize(
|
||||
"a, b, expected",
|
||||
[
|
||||
("-1", "2", "-2"),
|
||||
("-10", "0", "-0"),
|
||||
("0.5", "10.9", "5.45"),
|
||||
# Big ints
|
||||
(
|
||||
"12345678901234567890",
|
||||
"18000000162000001474380013420000",
|
||||
"2.222222222222222222222222223E+50",
|
||||
),
|
||||
# Big floats
|
||||
(
|
||||
"12345678901234567890.120",
|
||||
"12345678901234567890.120",
|
||||
"1.524157875323883675048681628E+38",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_multiply(a, b, expected):
|
||||
assert multiply(a, b) == expected
|
||||
|
||||
|
||||
def test_divide():
|
||||
assert divide(6, 3) == 2.0
|
||||
assert divide(5, 2) == 2.5
|
||||
@pytest.mark.parametrize(
|
||||
"a, b, expected",
|
||||
[
|
||||
("-1", "2", "-0.5"),
|
||||
("-10", "1", "-10"),
|
||||
("0.5", "10.9", "0.04587155963302752293577981651"),
|
||||
# Big ints
|
||||
("152407406035740740602050", "12345678901234567890", "12345"),
|
||||
# Big floats
|
||||
(
|
||||
"152407406035740740603531.400",
|
||||
"12345678901234567890.120",
|
||||
"12345",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_divide(a, b, expected):
|
||||
assert divide(a, b) == expected
|
||||
|
||||
|
||||
def text_zero_division():
|
||||
with pytest.raises(ToolExecutionError):
|
||||
divide(1, 0)
|
||||
|
||||
|
||||
def test_sqrt():
|
||||
assert sqrt(4) == 2.0
|
||||
assert sqrt(9) == 3.0
|
||||
divide("1", "0")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
divide("1", "0.0")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
divide("1", "0.000000")
|
||||
|
||||
|
||||
def test_sum_list():
|
||||
assert sum_list([1, 2, 3]) == 6
|
||||
assert sum_list([0, -1.5, 1]) == -0.5
|
||||
assert sum_list(["1", "2", "3", "4", "5", "6"]) == "21"
|
||||
assert sum_list([]) == "0"
|
||||
assert sum_list(["-1", "-2", "-3", "-4", "-5", "-6"]) == "-21"
|
||||
assert sum_list(["0.1", "0.2", "0.3", "0.3", "0.5", "0.7"]) == "2.1"
|
||||
|
||||
|
||||
def test_sum_range():
|
||||
assert sum_range(1, 3) == 6
|
||||
assert sum_range(0, 10) == 55
|
||||
assert sum_range("8", "2") == "0"
|
||||
assert sum_range("-8", "2") == "-33"
|
||||
assert sum_range("8", "-2") == "0"
|
||||
assert sum_range("2", "3") == "5"
|
||||
assert sum_range("0", "10") == "55"
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sum_range("2", "0.5")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sum_range("-1", "0.5")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sum_range("2.", "0.5")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sum_range("-1", "0.5")
|
||||
|
||||
|
||||
def test_mod():
|
||||
assert mod("-1", "0.5") == "-0.0"
|
||||
assert mod("-8", "2") == "-0"
|
||||
assert mod("0", "10") == "0"
|
||||
assert mod("2", "0.5") == "0.0"
|
||||
assert mod("2", "3") == "2"
|
||||
assert mod("2.", "-0.5") == "0.0"
|
||||
assert mod("2.1234", "0.6") == "0.3234"
|
||||
assert mod("2.1234", "1") == "0.1234"
|
||||
assert mod("2.1234", "3") == "2.1234"
|
||||
assert mod("8", "-2") == "0"
|
||||
assert mod("8", "2") == "0"
|
||||
|
|
|
|||
32
toolkits/math/tests/test_exponents.py
Normal file
32
toolkits/math/tests/test_exponents.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import pytest
|
||||
from arcade.sdk.errors import ToolExecutionError
|
||||
|
||||
from arcade_math.tools.exponents import (
|
||||
log,
|
||||
power,
|
||||
)
|
||||
|
||||
|
||||
def test_log():
|
||||
assert log("8", "2") == "3.0"
|
||||
assert log("2", "3") == "0.6309297535714574"
|
||||
assert log("2", "0.5") == "-1.0"
|
||||
with pytest.raises(ToolExecutionError):
|
||||
log("-1", "0.5")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
log("0", "10")
|
||||
|
||||
|
||||
def test_power():
|
||||
assert power("-8", "2") == "64"
|
||||
assert power("0", "10") == "0"
|
||||
assert power("2", "0.5") == "1.414213562373095048801688724"
|
||||
assert power("2", "3") == "8"
|
||||
assert power("2.", "-0.5") == "0.7071067811865475244008443621"
|
||||
assert power("2.1234", "0.6") == "1.571155202490495156807227175"
|
||||
assert power("2.1234", "1") == "2.1234"
|
||||
assert power("2.1234", "3") == "9.574044440904"
|
||||
assert power("8", "-2") == "0.015625"
|
||||
assert power("8", "2") == "64"
|
||||
with pytest.raises(ToolExecutionError):
|
||||
power("-1", "0.5")
|
||||
63
toolkits/math/tests/test_miscellaneous.py
Normal file
63
toolkits/math/tests/test_miscellaneous.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import pytest
|
||||
from arcade.sdk.errors import ToolExecutionError
|
||||
|
||||
from arcade_math.tools.miscellaneous import (
|
||||
abs_val,
|
||||
factorial,
|
||||
sqrt,
|
||||
)
|
||||
|
||||
|
||||
def test_abs_val():
|
||||
assert abs_val("2") == "2"
|
||||
assert abs_val("-1") == "1"
|
||||
assert abs_val("-1.12341234") == "1.12341234"
|
||||
|
||||
|
||||
def test_factorial():
|
||||
assert factorial("1") == "1"
|
||||
assert factorial("0") == "1"
|
||||
assert factorial("-0") == "1"
|
||||
assert factorial("23") == "25852016738884976640000"
|
||||
assert factorial("24") == "620448401733239439360000"
|
||||
assert factorial("10") == "3628800"
|
||||
with pytest.raises(ToolExecutionError):
|
||||
factorial("-1")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
factorial("-10")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
factorial("0.0000")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
factorial("-0.0")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
factorial("1.0")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
factorial("-1.0")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
factorial("23.0")
|
||||
|
||||
|
||||
def test_sqrt():
|
||||
assert sqrt("1") == "1"
|
||||
assert sqrt("0") == "0"
|
||||
assert sqrt("-0") == "-0"
|
||||
assert sqrt("23") == "4.795831523312719541597438064"
|
||||
assert sqrt("24") == "4.898979485566356196394568149"
|
||||
assert sqrt("10") == "3.162277660168379331998893544"
|
||||
assert sqrt("0.0") == "0.0"
|
||||
assert sqrt("0.0000") == "0.00"
|
||||
assert sqrt("-0.0") == "-0.0"
|
||||
assert sqrt("1.0") == "1.0"
|
||||
assert sqrt("3.14") == "1.772004514666935040199112510"
|
||||
assert sqrt("0.4") == "0.6324555320336758663997787089"
|
||||
assert sqrt("10.0") == "3.162277660168379331998893544"
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sqrt("-1")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sqrt("-10")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sqrt("-1.0")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sqrt("-1.3")
|
||||
with pytest.raises(ToolExecutionError):
|
||||
sqrt("-10.0")
|
||||
31
toolkits/math/tests/test_rational.py
Normal file
31
toolkits/math/tests/test_rational.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import pytest
|
||||
from arcade.sdk.errors import ToolExecutionError
|
||||
|
||||
from arcade_math.tools.rational import (
|
||||
gcd,
|
||||
lcm,
|
||||
)
|
||||
|
||||
|
||||
def test_gcd():
|
||||
assert gcd("-15", "-5") == "5"
|
||||
assert gcd("15", "0") == "15"
|
||||
assert gcd("15", "-2") == "1"
|
||||
assert gcd("15", "-0") == "15"
|
||||
assert gcd("15", "5") == "5"
|
||||
assert gcd("7", "13") == "1"
|
||||
assert gcd("-13", "13") == "13"
|
||||
with pytest.raises(ToolExecutionError):
|
||||
gcd("15.0", "5.0")
|
||||
|
||||
|
||||
def test_lcm():
|
||||
assert lcm("-15", "-5") == "15"
|
||||
assert lcm("15", "0") == "0"
|
||||
assert lcm("15", "-2") == "30"
|
||||
assert lcm("15", "-0") == "0"
|
||||
assert lcm("15", "5") == "15"
|
||||
assert lcm("7", "13") == "91"
|
||||
assert lcm("-13", "13") == "13"
|
||||
with pytest.raises(ToolExecutionError):
|
||||
lcm("15.0", "5.0")
|
||||
54
toolkits/math/tests/test_rounding.py
Normal file
54
toolkits/math/tests/test_rounding.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
from arcade_math.tools.rounding import (
|
||||
ceil,
|
||||
floor,
|
||||
round_num,
|
||||
)
|
||||
|
||||
|
||||
def test_ceil():
|
||||
assert ceil("1") == "1"
|
||||
assert ceil("-1") == "-1"
|
||||
assert ceil("0") == "0"
|
||||
assert ceil("-0") == "0"
|
||||
assert ceil("0.0") == "0"
|
||||
assert ceil("0.0000") == "0"
|
||||
assert ceil("-0.0") == "0"
|
||||
assert ceil("1.0") == "1"
|
||||
assert ceil("-1.0") == "-1"
|
||||
assert ceil("3.14") == "4"
|
||||
assert ceil("0.4") == "1"
|
||||
assert ceil("-1.3") == "-1"
|
||||
|
||||
|
||||
def test_floor():
|
||||
assert floor("1") == "1"
|
||||
assert floor("-1") == "-1"
|
||||
assert floor("0") == "0"
|
||||
assert floor("-0") == "0"
|
||||
assert floor("10") == "10"
|
||||
assert floor("0.0") == "0"
|
||||
assert floor("0.0000") == "0"
|
||||
assert floor("-0.0") == "0"
|
||||
assert floor("1.0") == "1"
|
||||
assert floor("-1.0") == "-1"
|
||||
assert floor("3.14") == "3"
|
||||
assert floor("0.4") == "0"
|
||||
assert floor("-1.3") == "-2"
|
||||
|
||||
|
||||
def test_round_num():
|
||||
# TODO(mateo): ok with scientific notatin? ok with negative round digits?
|
||||
assert round_num("1.2345", "-2") == "0"
|
||||
assert round_num("1.2345", "-1") == "0"
|
||||
assert round_num("1.2345", "0") == "1"
|
||||
assert round_num("1.2345", "1") == "1.2"
|
||||
assert round_num("1.2345", "2") == "1.23"
|
||||
assert round_num("1.2345", "3") == "1.234"
|
||||
assert round_num("1.2345", "8") == "1.23450000"
|
||||
assert round_num("1.654321", "-2") == "0"
|
||||
assert round_num("1.654321", "-1") == "0"
|
||||
assert round_num("1.654321", "0") == "2"
|
||||
assert round_num("1.654321", "1") == "1.7"
|
||||
assert round_num("1.654321", "2") == "1.65"
|
||||
assert round_num("1.654321", "3") == "1.654"
|
||||
assert round_num("1.654321", "8") == "1.65432100"
|
||||
18
toolkits/math/tests/test_statistics.py
Normal file
18
toolkits/math/tests/test_statistics.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
from arcade_math.tools.statistics import (
|
||||
avg,
|
||||
median,
|
||||
)
|
||||
|
||||
|
||||
def test_avg():
|
||||
assert avg(["1", "2", "3", "4", "5", "6"]) == "3.5"
|
||||
assert avg([]) == "0.0"
|
||||
assert avg(["-1", "-2", "-3", "-4", "-5", "-6"]) == "-3.5"
|
||||
assert avg(["0.1", "0.2", "0.3", "0.3", "0.5", "0.7"]) == "0.35"
|
||||
|
||||
|
||||
def test_median():
|
||||
assert median(["1", "2", "3", "4", "5", "6"]) == "3.5"
|
||||
assert median([]) == "0.0"
|
||||
assert median(["-1", "-2", "-3", "-4", "-5", "-6"]) == "-3.5"
|
||||
assert median(["0.1", "0.2", "0.3", "0.3", "0.5", "0.7"]) == "0.3"
|
||||
45
toolkits/math/tests/test_trigonometry.py
Normal file
45
toolkits/math/tests/test_trigonometry.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
from arcade_math.tools.trigonometry import (
|
||||
deg_to_rad,
|
||||
rad_to_deg,
|
||||
)
|
||||
|
||||
|
||||
def test_deg_to_rad():
|
||||
assert deg_to_rad("1") == "0.017453292519943295"
|
||||
assert deg_to_rad("-1") == "-0.017453292519943295"
|
||||
assert deg_to_rad("0") == "0.0"
|
||||
assert deg_to_rad("-0") == "-0.0"
|
||||
assert deg_to_rad("23") == "0.4014257279586958"
|
||||
assert deg_to_rad("24") == "0.4188790204786391"
|
||||
assert deg_to_rad("-10") == "-0.17453292519943295"
|
||||
assert deg_to_rad("10") == "0.17453292519943295"
|
||||
assert deg_to_rad("180") == "3.141592653589793"
|
||||
assert deg_to_rad("0.0") == "0.0"
|
||||
assert deg_to_rad("0.0000") == "0.0"
|
||||
assert deg_to_rad("-0.0") == "-0.0"
|
||||
assert deg_to_rad("1.0") == "0.017453292519943295"
|
||||
assert deg_to_rad("-1.0") == "-0.017453292519943295"
|
||||
assert deg_to_rad("23.0") == "0.4014257279586958"
|
||||
assert deg_to_rad("0.4") == "0.006981317007977318"
|
||||
assert deg_to_rad("-10.0") == "-0.17453292519943295"
|
||||
assert deg_to_rad("10.0") == "0.17453292519943295"
|
||||
|
||||
|
||||
def test_rad_to_deg():
|
||||
assert rad_to_deg("1") == "57.29577951308232"
|
||||
assert rad_to_deg("-1") == "-57.29577951308232"
|
||||
assert rad_to_deg("0") == "0.0"
|
||||
assert rad_to_deg("-0") == "-0.0"
|
||||
assert rad_to_deg("23") == "1317.8029288008934"
|
||||
assert rad_to_deg("24") == "1375.0987083139757"
|
||||
assert rad_to_deg("-10") == "-572.9577951308232"
|
||||
assert rad_to_deg("10") == "572.9577951308232"
|
||||
assert rad_to_deg("0.0") == "0.0"
|
||||
assert rad_to_deg("0.0000") == "0.0"
|
||||
assert rad_to_deg("-0.0") == "-0.0"
|
||||
assert rad_to_deg("1.0") == "57.29577951308232"
|
||||
assert rad_to_deg("-1.0") == "-57.29577951308232"
|
||||
assert rad_to_deg("3.14") == "179.9087476710785"
|
||||
assert rad_to_deg("0.4") == "22.918311805232932"
|
||||
assert rad_to_deg("-10.0") == "-572.9577951308232"
|
||||
assert rad_to_deg("10.0") == "572.9577951308232"
|
||||
Loading…
Reference in a new issue