//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // UNSUPPORTED: c++03, c++11, c++14 // // template // constexpr common_type_t<_M,_N> gcd(_M __m, _N __n) #include #include #include #include #include #include #include #include "test_macros.h" constexpr struct { int x; int y; int expect; } Cases[] = {{0, 0, 0}, {1, 0, 1}, {0, 1, 1}, {1, 1, 1}, {2, 3, 1}, {2, 4, 2}, {11, 9, 1}, {36, 17, 1}, {36, 18, 18}}; template constexpr bool test0(int in1, int in2, int out) { auto value1 = static_cast(in1); auto value2 = static_cast(in2); static_assert(std::is_same_v, ""); static_assert(std::is_same_v, ""); assert(static_cast(out) == std::gcd(value1, value2)); return true; } template T basic_gcd_(T m, T n) { return n == 0 ? m : basic_gcd_(n, m % n); } template T basic_gcd(T m, T n) { using Tp = std::make_unsigned_t; if constexpr (std::is_signed_v) { if (m < 0 && m != std::numeric_limits::min()) m = -m; if (n < 0 && n != std::numeric_limits::min()) n = -n; } return basic_gcd_(static_cast(m), static_cast(n)); } template void do_fuzzy_tests() { std::mt19937 gen(1938); using DistIntType = std::conditional_t; // See N4981 [rand.req.genl]/1.5 constexpr Input max_input = std::numeric_limits::max(); std::uniform_int_distribution distrib(0, max_input); constexpr int nb_rounds = 10000; for (int i = 0; i < nb_rounds; ++i) { Input n = static_cast(distrib(gen)); Input m = static_cast(distrib(gen)); assert(std::gcd(n, m) == basic_gcd(n, m)); } } template void do_limit_tests() { Input inputs[] = { // The behavior of std::gcd is undefined if the absolute value of one of its // operand is not representable in the result type. std::numeric_limits::min() + (std::is_signed::value ? 3 : 0), std::numeric_limits::min() + 1, std::numeric_limits::min() + 2, std::numeric_limits::max(), std::numeric_limits::max() - 1, std::numeric_limits::max() - 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, (Input)-1, (Input)-2, (Input)-3, (Input)-4, (Input)-5, (Input)-6, (Input)-7, (Input)-8, (Input)-9, (Input)-10, }; for (auto n : inputs) { for (auto m : inputs) { assert(std::gcd(n, m) == basic_gcd(n, m)); } } } template constexpr bool do_test(int = 0) { using S1 = std::make_signed_t; using S2 = std::make_signed_t; using U1 = std::make_unsigned_t; using U2 = std::make_unsigned_t; bool accumulate = true; for (auto TC : Cases) { { // Test with two signed types using Output = std::common_type_t; accumulate &= test0(TC.x, TC.y, TC.expect); accumulate &= test0(-TC.x, TC.y, TC.expect); accumulate &= test0(TC.x, -TC.y, TC.expect); accumulate &= test0(-TC.x, -TC.y, TC.expect); accumulate &= test0(TC.x, TC.y, TC.expect); accumulate &= test0(-TC.x, TC.y, TC.expect); accumulate &= test0(TC.x, -TC.y, TC.expect); accumulate &= test0(-TC.x, -TC.y, TC.expect); } { // test with two unsigned types using Output = std::common_type_t; accumulate &= test0(TC.x, TC.y, TC.expect); accumulate &= test0(TC.x, TC.y, TC.expect); } { // Test with mixed signs using Output = std::common_type_t; accumulate &= test0(TC.x, TC.y, TC.expect); accumulate &= test0(TC.x, TC.y, TC.expect); accumulate &= test0(-TC.x, TC.y, TC.expect); accumulate &= test0(TC.x, -TC.y, TC.expect); } { // Test with mixed signs using Output = std::common_type_t; accumulate &= test0(TC.x, TC.y, TC.expect); accumulate &= test0(TC.x, TC.y, TC.expect); accumulate &= test0(-TC.x, TC.y, TC.expect); accumulate &= test0(TC.x, -TC.y, TC.expect); } } return accumulate; } int main(int argc, char**) { int non_cce = argc; // a value that can't possibly be constexpr static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); assert(do_test(non_cce)); assert(do_test(non_cce)); assert(do_test(non_cce)); assert(do_test(non_cce)); assert(do_test(non_cce)); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); assert(do_test(non_cce)); assert(do_test(non_cce)); assert(do_test(non_cce)); assert(do_test(non_cce)); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); static_assert(do_test(), ""); assert((do_test(non_cce))); assert((do_test(non_cce))); assert((do_test(non_cce))); assert((do_test(non_cce))); assert((do_test(non_cce))); assert((do_test(non_cce))); assert((do_test(non_cce))); assert((do_test(non_cce))); // LWG#2837 { auto res = std::gcd(static_cast(1234), INT32_MIN); static_assert(std::is_same_v, ""); assert(res == 2); } do_fuzzy_tests(); do_fuzzy_tests(); do_fuzzy_tests(); do_fuzzy_tests(); do_fuzzy_tests(); do_fuzzy_tests(); do_fuzzy_tests(); do_fuzzy_tests(); do_limit_tests(); do_limit_tests(); do_limit_tests(); do_limit_tests(); do_limit_tests(); do_limit_tests(); do_limit_tests(); do_limit_tests(); return 0; }