Index: External/CUDA/CMakeLists.txt =================================================================== --- External/CUDA/CMakeLists.txt +++ External/CUDA/CMakeLists.txt @@ -47,6 +47,7 @@ create_one_local_test(axpy axpy.cu) create_one_local_test(algorithm algorithm.cu) create_one_local_test(cmath cmath.cu) + create_one_local_test(complex complex.cu) create_one_local_test(math_h math_h.cu) create_one_local_test(empty empty.cu) create_one_local_test(printf printf.cu) @@ -250,7 +251,7 @@ set(_Std_LDFLAGS -std=${_Std}) foreach(_GccPath IN LISTS GCC_PATHS) get_version(_GccVersion ${_GccPath}) - # Our tests don't work with libstdc++ earlier than 5.0. + # libstdc++ seems not to support C++14 before version 5.0. if (${_Std} STREQUAL "c++14" AND ${_GccVersion} VERSION_LESS "5.0") continue() endif() Index: External/CUDA/complex.cu =================================================================== --- /dev/null +++ External/CUDA/complex.cu @@ -0,0 +1,440 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +// These are loosely adapted from libc++'s tests. In general, we don't care a +// ton about verifying the return types or results we get, on the assumption +// that our standard library is correct. But we care deeply about calling every +// overload of every function (so that we verify that everything compiles). +// +// We do care about the results of complex multiplication / division, since +// these use code we've written. + +// These tests are pretty annoying to write without C++11. +#if __cplusplus >= 201103L + +#include + +template +__device__ double promote( + T, typename std::enable_if::value>::type* = 0); +__device__ float promote(float); +__device__ double promote(double); + +__device__ void is_about(float x, float y) { + assert(std::abs((x - y) / (x + y)) < 1.e-6); +} +__device__ void is_about(double x, double y) { + assert(std::abs((x - y) / (x + y)) < 1.e-14); +} + +template +__device__ void test_promotion_impl(T x) { + assert(std::imag(x) == 0); + assert(std::real(x) == x); + + using Promoted = decltype(promote(x)); + assert(std::arg(x) == arg(std::complex(x, 0))); + assert(std::conj(x) == conj(std::complex(x, 0))); + assert(std::norm(x) == norm(std::complex(x, 0))); +#ifndef __GLIBCXX__ + // libstdc++'s implementation of proj is completely broken, see + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61761. + assert(std::proj(x) == proj(std::complex(x, 0))); +#endif +} + +__device__ void test_promotion() { + int vals[] = {0, 1, 10}; + for (int i : vals) { + test_promotion_impl(i); + test_promotion_impl(i); + test_promotion_impl(i); + test_promotion_impl(i); + test_promotion_impl(i); + } +} + +__device__ void test_literals() { +#if __cplusplus >= 201402L + using namespace std::literals::complex_literals; + + { + std::complex c1 = 3.0i; + assert(c1 == std::complex(0, 3.0)); + auto c2 = 3i; + assert(c1 == c2); + } + + { + std::complex c1 = 3.0if; + assert(c1 == std::complex(0, 3.0)); + auto c2 = 3if; + assert(c1 == c2); + } +#endif +} + +template +__device__ void test_assignment_real() { + std::complex c; + c = 1.5; + assert(c.real() == 1.5); + assert(c.imag() == 0); +} + +template +__device__ void test_assignment_complex() { + std::complex c; + std::complex c2(1.5, 2.5); + c = c2; + assert(c.real() == 1.5); + assert(c.imag() == 2.5); +} + +template +__device__ void test_plus_equals() { + { + std::complex c; + c += 1.5; + assert(c.real() == 1.5); + assert(c.imag() == 0); + } + + { + std::complex c; + const std::complex c2(1.5, 2.5); + c += c2; + c += c2; + assert(c.real() == 3); + assert(c.imag() == 5); + + std::complex c3; + + c3 = c; + std::complex ic(1, 1); + c3 += ic; + assert(c3.real() == 4); + assert(c3.imag() == 6); + + c3 = c; + std::complex fc(1, 1); + c3 += fc; + assert(c3.real() == 4); + assert(c3.imag() == 6); + } +} + +template +__device__ void test_minus_equals() { + { + std::complex c; + c -= 1.5; + assert(c.real() == -1.5); + assert(c.imag() == 0); + } + + { + std::complex c; + const std::complex c2(1.5, 2.5); + assert(c.real() == 0); + assert(c.imag() == 0); + c -= c2; + assert(c.real() == -1.5); + assert(c.imag() == -2.5); + c -= c2; + assert(c.real() == -3); + assert(c.imag() == -5); + + std::complex c3; + + c3 = c; + std::complex ic (1,1); + c3 -= ic; + assert(c3.real() == -4); + assert(c3.imag() == -6); + + c3 = c; + std::complex fc (1,1); + c3 -= fc; + assert(c3.real() == -4); + assert(c3.imag() == -6); + } +} + +template +__device__ void test_times_equals() { + { + std::complex c(1); + c *= 1.5; + c *= 1.5; + c *= -1.5; + c.imag(2); + c *= 1.5; + assert(c.real() == -5.0625); + assert(c.imag() == 3); + } + + { + std::complex c(1); + const std::complex c2(1.5, 2.5); + c *= c2; + c *= c2; + assert(c.real() == -4); + assert(c.imag() == 7.5); + + std::complex c3; + + c3 = c; + std::complex ic (1,1); + c3 *= ic; + assert(c3.real() == -11.5); + assert(c3.imag() == 3.5); + + c3 = c; + std::complex fc (1,1); + c3 *= fc; + assert(c3.real() == -11.5); + assert(c3.imag() == 3.5); + } +} + +template +__device__ void test_divide_equals() { + { + std::complex c(1); + c /= 0.5; + c /= 0.5; + c /= -0.5; + c.imag(2); + c /= 0.5; + assert(c.real() == -16); + assert(c.imag() == 4); + } + + { + std::complex c(-4, 7.5); + const std::complex c2(1.5, 2.5); + assert(c.real() == -4); + assert(c.imag() == 7.5); + c /= c2; + assert(c.real() == 1.5); + assert(c.imag() == 2.5); + c /= c2; + assert(c.real() == 1); + assert(c.imag() == 0); + + std::complex c3; + + c3 = c; + std::complex ic (1,1); + c3 /= ic; + assert(c3.real() == 0.5); + assert(c3.imag() == -0.5); + + c3 = c; + std::complex fc (1,1); + c3 /= fc; + assert(c3.real() == 0.5); + assert(c3.imag() == -0.5); + } +} + +template +__device__ void test_construct() { + { + const std::complex c; + assert(c.real() == 0); + assert(c.imag() == 0); + } + { + const std::complex c = 7.5; + assert(c.real() == 7.5); + assert(c.imag() == 0); + } + { + const std::complex c(8.5); + assert(c.real() == 8.5); + assert(c.imag() == 0); + } + { + const std::complex c(10.5, -9.5); + assert(c.real() == 10.5); + assert(c.imag() == -9.5); + } +#if __cplusplus >= 201103L + { + constexpr std::complex c; + static_assert(c.real() == 0, ""); + static_assert(c.imag() == 0, ""); + } + { + constexpr std::complex c = 7.5; + static_assert(c.real() == 7.5, ""); + static_assert(c.imag() == 0, ""); + } + { + constexpr std::complex c(8.5); + static_assert(c.real() == 8.5, ""); + static_assert(c.imag() == 0, ""); + } + { + constexpr std::complex c(10.5, -9.5); + static_assert(c.real() == 10.5, ""); + static_assert(c.imag() == -9.5, ""); + } +#endif +} + +template +__device__ void test_construct_integral() { +#if __cplusplus >= 201402L + constexpr std::complex c1; + static_assert(c1.real() == 0, ""); + static_assert(c1.imag() == 0, ""); + constexpr std::complex c2(3); + static_assert(c2.real() == 3, ""); + static_assert(c2.imag() == 0, ""); + constexpr std::complex c3(3, 4); + static_assert(c3.real() == 3, ""); + static_assert(c3.imag() == 4, ""); +#endif +} + +template +__device__ void test_set_real_imag() { + std::complex c; + c.real(3.5); + assert(c.real() == 3.5); + assert(c.imag() == 0); + c.imag(4.5); + assert(c.real() == 3.5); + assert(c.imag() == 4.5); +} + +template +__device__ void test_transcendentals_etc() { + assert(sin(std::complex(0, 0)) == std::complex(0, 0)); + assert(sinh(std::complex(0, 0)) == std::complex(0, 0)); + assert(asin(std::complex(0, 0)) == std::complex(0, 0)); + assert(asinh(std::complex(0, 0)) == std::complex(0, 0)); + + assert(cos(std::complex(0, 0)) == std::complex(1, 0)); + assert(cosh(std::complex(0, 0)) == std::complex(1, 0)); + { + std::complex c = acos(std::complex(0, 0)); + is_about(real(c), T(M_PI_2)); + assert(std::abs(imag(c)) < 1.e-6); + } + { + std::complex c = acosh(std::complex(0, 0)); + assert(std::abs(real(c)) < 1.e-6); + is_about(imag(c), T(M_PI_2)); + } + + assert(tan(std::complex(0, 0)) == std::complex(0, 0)); + assert(tanh(std::complex(0, 0)) == std::complex(0, 0)); + assert(atan(std::complex(0, 0)) == std::complex(0, 0)); + assert(atanh(std::complex(0, 0)) == std::complex(0, 0)); + + assert(exp(std::complex(0, 0)) == std::complex(1, 0)); + assert(log10(std::complex(0, 0)) == std::complex(-INFINITY, 0)); + assert(log(std::complex(0, 0)) == std::complex(-INFINITY, 0)); + + { + std::complex c = pow(std::complex(2, 3), std::complex(2, 0)); + is_about(real(c), -5); + is_about(imag(c), 12); + } + { + std::complex c = pow(std::complex(2, 3), T(2)); + is_about(real(c), -5); + is_about(imag(c), 12); + } + { + std::complex c = pow(T(2), std::complex(2)); + is_about(real(c), 4); + assert(std::abs(imag(c)) < 1.e-6); + } + { + std::complex c = sqrt(std::complex(64, 0)); + is_about(real(c), 8); + assert(std::abs(imag(c)) < 1.e-6); + } + + // "etc." + assert(abs(std::complex(3, 4)) == 5); + assert(norm(std::complex(3, 4)) == 25); + assert(arg(std::complex(1, 0)) == 0); + assert(conj(std::complex(1, 2)) == std::complex(1, -2)); + + assert(std::polar(T(0)) == std::complex(0, 0)); + assert(std::polar(T(1)) == std::complex(1, 0)); + assert(std::polar(T(100)) == std::complex(100, 0)); + assert(std::polar(T(0), T(0)) == std::complex(0, 0)); + assert(std::polar(T(1), T(0)) == std::complex(1, 0)); + assert(std::polar(T(100), T(0)) == std::complex(100, 0)); + +#ifndef __GLIBCXX__ + // libstdc++'s implementation of proj is completely broken, see + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61761. + assert(std::proj(std::complex(1, 2)) == std::complex(1, 2)); + assert(std::proj(std::complex(-1, 2)) == std::complex(-1, 2)); + assert(std::proj(std::complex(1, -2)) == std::complex(1, -2)); + assert(std::proj(std::complex(-1, -2)) == std::complex(-1, -2)); +#endif +} + +__global__ void tests() { + test_promotion(); + test_literals(); + test_assignment_real(); + test_assignment_real(); + + test_assignment_complex(); + test_assignment_complex(); + test_assignment_complex(); + test_assignment_complex(); + + test_plus_equals(); + test_plus_equals(); + test_minus_equals(); + test_minus_equals(); + test_times_equals(); + test_times_equals(); + test_divide_equals(); + test_divide_equals(); + + test_construct(); + test_construct(); + test_construct_integral(); + + test_set_real_imag(); + test_set_real_imag(); + + test_transcendentals_etc(); + test_transcendentals_etc(); +} +#else +__global__ void tests() {} +#endif + +int main() { + tests<<<1, 1>>>(); + cudaError_t err = cudaDeviceSynchronize(); + if (err != cudaSuccess) { + printf("CUDA error %d\n", (int)err); + return 1; + } + printf("Success!\n"); + return 0; +} Index: External/CUDA/complex.reference_output =================================================================== --- /dev/null +++ External/CUDA/complex.reference_output @@ -0,0 +1,2 @@ +Success! +exit 0