Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Math.gcd new function #8331

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ports_qemu-arm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install packages
run: source tools/ci.sh && ci_qemu_arm_setup
- name: Build and run test suite
Expand Down
50 changes: 47 additions & 3 deletions .github/workflows/ports_unix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Build
run: source tools/ci.sh && ci_unix_standard_build
- name: Run main test suite
Expand All @@ -53,6 +56,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Build
run: source tools/ci.sh && ci_unix_dev_build
- name: Run main test suite
Expand All @@ -65,8 +71,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install packages
run: source tools/ci.sh && ci_unix_coverage_setup
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install python dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools pyelftools
- name: Print environment version
run: source tools/ci.sh && ci_unix_coverage_print_env
- name: Build
run: source tools/ci.sh && ci_unix_coverage_build
- name: Run main test suite
Expand All @@ -92,6 +105,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install python dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools pyelftools
- name: Install packages
run: source tools/ci.sh && ci_unix_32bit_setup
- name: Build
Expand All @@ -110,6 +130,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install packages
run: source tools/ci.sh && ci_unix_32bit_setup
- name: Build
Expand All @@ -124,6 +147,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Build
run: source tools/ci.sh && ci_unix_float_build
- name: Run main test suite
Expand All @@ -136,6 +162,9 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install packages
run: source tools/ci.sh && ci_unix_clang_setup
- name: Build
Expand All @@ -150,6 +179,9 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install packages
run: source tools/ci.sh && ci_unix_clang_setup
- name: Build
Expand All @@ -164,6 +196,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Build
run: source tools/ci.sh && ci_unix_settrace_build
- name: Run main test suite
Expand All @@ -176,6 +211,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Build
run: source tools/ci.sh && ci_unix_settrace_stackless_build
- name: Run main test suite
Expand All @@ -190,7 +228,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.8'
python-version: '3.9'
- name: Build
run: source tools/ci.sh && ci_unix_macos_build
- name: Run tests
Expand All @@ -203,6 +241,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install packages
run: source tools/ci.sh && ci_unix_qemu_mips_setup
- name: Build
Expand All @@ -217,6 +258,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install packages
run: source tools/ci.sh && ci_unix_qemu_arm_setup
- name: Build
Expand Down
2 changes: 1 addition & 1 deletion docs/differences/python_35.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Changes to built-in modules:
+-----------------------------------------------------------------------------------------------------------+---------------+
| A new function *isclose()* provides a way to test for approximate equality. | |
+-----------------------------------------------------------------------------------------------------------+---------------+
| A new *gcd()* function has been added. The *fractions.gcd()* function is now deprecated. | |
| A new *gcd()* function has been added. The *fractions.gcd()* function is now deprecated. | Completed |
+-----------------------------------------------------------------------------------------------------------+---------------+
| `os <https://docs.python.org/3/whatsnew/3.5.html#os>`_ |
+-----------------------------------------------------------------------------------------------------------+---------------+
Expand Down
2 changes: 1 addition & 1 deletion docs/differences/python_39.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Changes to built-in modules:
+---------------------------------------------------------------------------------------------------------------+---------------+
| `math` |
+---------------------------------------------------------------------------------------------------------------+---------------+
| Expanded the *math.gcd()* function to handle multiple arguments. Formerly, it only supported two arguments | |
| Expanded the *math.gcd()* function to handle multiple arguments. Formerly, it only supported two arguments | Completed |
+---------------------------------------------------------------------------------------------------------------+---------------+
| Added *math.lcm()*: return the least common multiple of specified arguments | |
+---------------------------------------------------------------------------------------------------------------+---------------+
Expand Down
4 changes: 4 additions & 0 deletions docs/library/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ Functions

Return the gamma function of ``x``.

.. function:: gcd(x, y, ...)

Return the greatest common divisor of any amount of numbers with minimum of 2 arguments.

.. function:: isfinite(x)

Return ``True`` if ``x`` is finite.
Expand Down
4 changes: 2 additions & 2 deletions ports/windows/.appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
image: Visual Studio 2017
image: Visual Studio 2019
clone_depth: 1
skip_tags: true

environment:
# Python version used
MICROPY_CPYTHON3: c:/python38/python.exe
MICROPY_CPYTHON3: c:/python39/python.exe
# The variants.
matrix:
- PyVariant: dev
Expand Down
57 changes: 56 additions & 1 deletion py/modmath.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,61 @@ MATH_FUN_1(erfc, erfc)
MATH_FUN_1(gamma, tgamma)
// lgamma(x): return the natural logarithm of the gamma function of x
MATH_FUN_1(lgamma, lgamma)
#endif

/* gcd(x, y): return the greatest common divisor
GCD Pseudocode
* gcd(x,y)
* if x < y;
* x, y = y, x
*
* while y != 0:
* x,y = y, x mod y
* return x
*/
STATIC mp_obj_t gcd_func(mp_obj_t x, mp_obj_t y) {
mp_obj_t temp;
if (mp_binary_op(MP_BINARY_OP_LESS, x, y) == mp_const_true) {
temp = x;
x = y;
y = temp;
}

mp_obj_t zero = mp_obj_new_int(0);
while (mp_binary_op(MP_BINARY_OP_NOT_EQUAL, y, zero) == mp_const_true) {
temp = y;
y = mp_binary_op(MP_BINARY_OP_MODULO, x, y);
x = temp;
}
return x;
}

STATIC mp_obj_t gcd_preprocess_arg(mp_obj_t presumed_integer) {
if (!mp_obj_is_int(presumed_integer)) {
mp_raise_msg_varg(&mp_type_TypeError,
MP_ERROR_TEXT("can't convert %s to int"), mp_obj_get_type_str(presumed_integer));
}
return mp_unary_op(MP_UNARY_OP_ABS, presumed_integer);
}

STATIC mp_obj_t mp_math_gcd(size_t n_args, const mp_obj_t *args) {
mp_obj_t e = gcd_preprocess_arg(args[--n_args]);
mp_obj_t d = gcd_preprocess_arg(args[--n_args]);

mp_obj_t ans = gcd_func(d, e);
if (n_args == 0) {
return ans;
}

// gcd(a, gcd(b, gcd(c, gcd(d, e)))))
do {
mp_obj_t next_variable = gcd_preprocess_arg(args[--n_args]);
ans = gcd_func(next_variable, ans);
} while (n_args > 0);
return ans;
}
MP_DEFINE_CONST_FUN_OBJ_VAR(mp_math_gcd_obj, 2, mp_math_gcd);

#endif // MICROPY_PY_MATH_SPECIAL_FUNCTIONS
// TODO: fsum

#if MICROPY_PY_MATH_ISCLOSE
Expand Down Expand Up @@ -425,6 +479,7 @@ STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&mp_math_erfc_obj) },
{ MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&mp_math_gamma_obj) },
{ MP_ROM_QSTR(MP_QSTR_lgamma), MP_ROM_PTR(&mp_math_lgamma_obj) },
{ MP_ROM_QSTR(MP_QSTR_gcd), MP_ROM_PTR(&mp_math_gcd_obj) },
#endif
};

Expand Down
54 changes: 54 additions & 0 deletions tests/float/math_fun_special.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,57 @@
print("{:.4g}".format(function(value)))
except ValueError as e:
print(str(e))


# Tests for gcd mostly duplicated from
# https://github.com/python/cpython/blob/main/Lib/test/test_math.py


def assertRaises(exception, func, *args):
try:
print(func(*args))
assert False
except exception:
pass


print(gcd(0, 0))
print(gcd(1, 0))
print(gcd(-1, 0))
print(gcd(0, 1))
print(gcd(0, -1))
print(gcd(7, 1))
print(gcd(7, -1))
print(gcd(-23, 15))
print(gcd(120, 84))
print(gcd(84, -120))
print(gcd(1216342683557601535506311712, 436522681849110124616458784))

x = 434610456570399902378880679233098819019853229470286994367836600566
y = 1064502245825115327754847244914921553977
for c in (652560, 576559230871654959816130551884856912003141446781646602790216406874):
a = x * c
b = y * c
print(gcd(a, b))
print(gcd(b, a))
print(gcd(-a, b))
print(gcd(b, -a))
print(gcd(a, -b))
print(gcd(-b, a))
print(gcd(-a, -b))
print(gcd(-b, -a))

# In MP minimum argument count is 2
# self.assertEqual(gcd(), 0)
# self.assertEqual(gcd(120), 120)
# self.assertEqual(gcd(-120), 120)

print(gcd(120, 84, 102))
print(gcd(120, 1, 84))
print(gcd(6, 30, 40, -60, 20, 40))
print(gcd(12345678912345678912345678911561561658135153135135135, 123456))

assertRaises(TypeError, gcd, 120.0)
assertRaises(TypeError, gcd, 120.0, 84)
assertRaises(TypeError, gcd, 120, 84.0)
assertRaises(TypeError, gcd, 120, 1, 84.0)
6 changes: 1 addition & 5 deletions tools/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -434,9 +434,7 @@ function ci_unix_dev_run_tests {
ci_unix_run_tests_helper VARIANT=dev
}

function ci_unix_coverage_setup {
sudo pip3 install setuptools
sudo pip3 install pyelftools
function ci_unix_coverage_print_env {
gcc --version
python3 --version
}
Expand All @@ -459,8 +457,6 @@ function ci_unix_32bit_setup {
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386
sudo pip3 install setuptools
sudo pip3 install pyelftools
gcc --version
python2 --version
python3 --version
Expand Down