diff --git a/toolkits/math/arcade_math/tools/arithmetic.py b/toolkits/math/arcade_math/tools/arithmetic.py index 14ef148a..1bb128f7 100644 --- a/toolkits/math/arcade_math/tools/arithmetic.py +++ b/toolkits/math/arcade_math/tools/arithmetic.py @@ -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)) diff --git a/toolkits/math/arcade_math/tools/exponents.py b/toolkits/math/arcade_math/tools/exponents.py new file mode 100644 index 00000000..a7362131 --- /dev/null +++ b/toolkits/math/arcade_math/tools/exponents.py @@ -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)) diff --git a/toolkits/math/arcade_math/tools/miscellaneous.py b/toolkits/math/arcade_math/tools/miscellaneous.py new file mode 100644 index 00000000..f3d624cf --- /dev/null +++ b/toolkits/math/arcade_math/tools/miscellaneous.py @@ -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()) diff --git a/toolkits/math/arcade_math/tools/random.py b/toolkits/math/arcade_math/tools/random.py index e09ac2f6..2df19ce3 100644 --- a/toolkits/math/arcade_math/tools/random.py +++ b/toolkits/math/arcade_math/tools/random.py @@ -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 diff --git a/toolkits/math/arcade_math/tools/rational.py b/toolkits/math/arcade_math/tools/rational.py new file mode 100644 index 00000000..fdd16a92 --- /dev/null +++ b/toolkits/math/arcade_math/tools/rational.py @@ -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)) diff --git a/toolkits/math/arcade_math/tools/rounding.py b/toolkits/math/arcade_math/tools/rounding.py new file mode 100644 index 00000000..59b7becd --- /dev/null +++ b/toolkits/math/arcade_math/tools/rounding.py @@ -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))) diff --git a/toolkits/math/arcade_math/tools/statistics.py b/toolkits/math/arcade_math/tools/statistics.py new file mode 100644 index 00000000..b197a7e2 --- /dev/null +++ b/toolkits/math/arcade_math/tools/statistics.py @@ -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" diff --git a/toolkits/math/arcade_math/tools/trigonometry.py b/toolkits/math/arcade_math/tools/trigonometry.py new file mode 100644 index 00000000..e7cecb3f --- /dev/null +++ b/toolkits/math/arcade_math/tools/trigonometry.py @@ -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))) diff --git a/toolkits/math/evals/eval_math_tools.py b/toolkits/math/evals/eval_math_tools.py index 07b55d5f..9bc8a58f 100644 --- a/toolkits/math/evals/eval_math_tools.py +++ b/toolkits/math/evals/eval_math_tools.py @@ -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 diff --git a/toolkits/math/tests/test_arithmetic.py b/toolkits/math/tests/test_arithmetic.py index 5f9b8807..2404f80b 100644 --- a/toolkits/math/tests/test_arithmetic.py +++ b/toolkits/math/tests/test_arithmetic.py @@ -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" diff --git a/toolkits/math/tests/test_exponents.py b/toolkits/math/tests/test_exponents.py new file mode 100644 index 00000000..f5825a85 --- /dev/null +++ b/toolkits/math/tests/test_exponents.py @@ -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") diff --git a/toolkits/math/tests/test_miscellaneous.py b/toolkits/math/tests/test_miscellaneous.py new file mode 100644 index 00000000..8946e88a --- /dev/null +++ b/toolkits/math/tests/test_miscellaneous.py @@ -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") diff --git a/toolkits/math/tests/test_rational.py b/toolkits/math/tests/test_rational.py new file mode 100644 index 00000000..fa854621 --- /dev/null +++ b/toolkits/math/tests/test_rational.py @@ -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") diff --git a/toolkits/math/tests/test_rounding.py b/toolkits/math/tests/test_rounding.py new file mode 100644 index 00000000..22e252a5 --- /dev/null +++ b/toolkits/math/tests/test_rounding.py @@ -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" diff --git a/toolkits/math/tests/test_statistics.py b/toolkits/math/tests/test_statistics.py new file mode 100644 index 00000000..90a542af --- /dev/null +++ b/toolkits/math/tests/test_statistics.py @@ -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" diff --git a/toolkits/math/tests/test_trigonometry.py b/toolkits/math/tests/test_trigonometry.py new file mode 100644 index 00000000..98e1f8fe --- /dev/null +++ b/toolkits/math/tests/test_trigonometry.py @@ -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"