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:
Mateo Torres 2025-03-14 09:47:04 -03:00 committed by GitHub
parent 6cafadac9c
commit f04087b389
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 722 additions and 103 deletions

View file

@ -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))

View 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))

View 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())

View file

@ -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

View 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))

View 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)))

View 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"

View 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)))

View file

@ -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

View file

@ -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"

View 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")

View 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")

View 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")

View 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"

View 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"

View 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"