From f6af29cb3a6a81761f5e99a71bdec65682160c01 Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Mon, 3 Aug 2026 21:45:37 +0200 Subject: [PATCH 1/5] update Eigen to 5.0.1 Use the latest Eigen release while preserving the existing focused test set. Verify the official archive checksum and keep the benchmark recipe otherwise unchanged. --- README.md | 2 +- use_on_realworld_projects/eigen_speed_report.md | 16 ++++++++-------- use_on_realworld_projects/realworld_projects.py | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ccbda9d..5712178 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It is not complete. It is not fully compliant. But it is good enough to quickly | [cmake](https://cmake.org/) | [3.04x](use_on_realworld_projects/cmake_speed_report.md) | Uses a compiler wrapper to build. | | [cppcheck](https://github.com/cppcheck-opensource/cppcheck) | [2.04x](use_on_realworld_projects/cppcheck_speed_report.md)| | | [CTRE](https://github.com/hanickadot/compile-time-regular-expressions) | [1.12x](use_on_realworld_projects/ctre_speed_report.md) | Builds every upstream compile-time test. | -| [eigen](https://gitlab.com/libeigen/eigen) | [1.89x](use_on_realworld_projects/eigen_speed_report.md) | | +| [eigen](https://gitlab.com/libeigen/eigen) | [1.82x](use_on_realworld_projects/eigen_speed_report.md) | | | [Electron](https://www.electronjs.org/) | [3.51x](use_on_realworld_projects/electron_speed_report.md) | Builds and tests a focused slice of Electron's startup and command-line handling without a Chromium checkout. | | [FlatBuffers](https://flatbuffers.dev/) | [2.37x](use_on_realworld_projects/flatbuffers_speed_report.md) | Builds the compiler, library, samples, and C++ test suite. | | [fmt](https://github.com/fmtlib/fmt) | [1.62x](use_on_realworld_projects/fmt_speed_report.md) | | diff --git a/use_on_realworld_projects/eigen_speed_report.md b/use_on_realworld_projects/eigen_speed_report.md index 0a8142d..4135313 100644 --- a/use_on_realworld_projects/eigen_speed_report.md +++ b/use_on_realworld_projects/eigen_speed_report.md @@ -1,10 +1,10 @@ # Real-world project speed comparison -Compiler: `c++ (Debian 14.2.0-19) 14.2.0`. Each project is built 9 time(s) per side (system libstdc++, psychicstd); `system (s)`/`psychicstd (s)` are the *median* build time of those repetitions, in seconds -- the median is used instead of the mean so one repetition disturbed by another process on the machine doesn't skew the result. `speedup` = system median / psychicstd median (>1x means psychicstd is faster); its bracketed range is a 95% confidence interval on that *same ratio* (obtained by resampling the raw per-repetition timings, not just the two medians, 2000 times) -- so it reflects how much the repetitions varied, not a different unit. ๐ŸŸข the whole CI is above 1x (reliably faster) ยท ๐Ÿ”ด the whole CI is below 1x (reliably slower) ยท ๐ŸŸก the CI straddles 1x (not distinguishable from run-to-run noise). +Compiler: `g++-14 (Debian 14.2.0-19) 14.2.0`. Each project is built 9 time(s) per side (system libstdc++, psychicstd); `system (s)`/`psychicstd (s)` are the *median* build time of those repetitions, in seconds -- the median is used instead of the mean so one repetition disturbed by another process on the machine doesn't skew the result. `speedup` = system median / psychicstd median (>1x means psychicstd is faster); its bracketed range is a 95% confidence interval on that *same ratio* (obtained by resampling the raw per-repetition timings, not just the two medians, 2000 times) -- so it reflects how much the repetitions varied, not a different unit. ๐ŸŸข the whole CI is above 1x (reliably faster) ยท ๐Ÿ”ด the whole CI is below 1x (reliably slower) ยท ๐ŸŸก the CI straddles 1x (not distinguishable from run-to-run noise). -Parallelism: **8 jobs** (20 logical CPUs available; the memory estimate permits 21 jobs at 1.5 GiB/job). ccache was disabled. +Parallelism: **8 jobs** (16 logical CPUs available; the memory estimate permits 17 jobs at 1.5 GiB/job). ccache was disabled. -## eigen (3.4.0) +## eigen (5.0.1) eigen has no configure step; a fixed subset of its test suite is compiled and run individually, with times summed. @@ -12,16 +12,16 @@ eigen has no configure step; a fixed subset of its test suite is compiled and ru | step | system (s) | psychicstd (s) | speedup | comment | | --- | ---: | ---: | ---: | --- | -| compile | 19.46 | 10.29 | ๐ŸŸข 1.89x [1.89x, 1.89x] | | -| run tests | 0.03 | 0.03 | ๐ŸŸข 1.29x [1.26x, 1.32x] | | +| compile | 25.08 | 13.75 | ๐ŸŸข 1.82x [1.76x, 1.87x] | | +| run tests | 0.24 | 0.16 | ๐ŸŸข 1.45x [1.41x, 1.47x] | | ### Release | step | system (s) | psychicstd (s) | speedup | comment | | --- | ---: | ---: | ---: | --- | -| compile | 19.45 | 10.31 | ๐ŸŸข 1.89x [1.88x, 1.89x] | | -| run tests | 0.03 | 0.03 | ๐ŸŸข 1.29x [1.27x, 1.32x] | | +| compile | 25.13 | 13.86 | ๐ŸŸข 1.81x [1.80x, 1.83x] | | +| run tests | 0.24 | 0.16 | ๐ŸŸข 1.46x [1.44x, 1.46x] | | ______________________________________________________________________ -Reproduce this on your machine: `scripts/benchmark_realworld.py --compiler c++ --build-type both --reps 9 --jobs 8` +Reproduce this on your machine: `scripts/benchmark_realworld.py --compiler g++-14 --build-type both --reps 9 --jobs 8` diff --git a/use_on_realworld_projects/realworld_projects.py b/use_on_realworld_projects/realworld_projects.py index 7fa870a..c124da5 100755 --- a/use_on_realworld_projects/realworld_projects.py +++ b/use_on_realworld_projects/realworld_projects.py @@ -1066,11 +1066,11 @@ def build(tc: Toolchain) -> dict[str, float]: def _eigen() -> Project: - version = "3.4.0" + version = "5.0.1" url = ( f"https://gitlab.com/libeigen/eigen/-/archive/{version}/eigen-{version}.tar.gz" ) - checksum = "8586084f71f9bde545ee7fa6d00288b264a2b7ac3607b974e54d13e7162c1c72" + checksum = "e9c326dc8c05cd1e044c71f30f1b2e34a6161a3b6ecf445d56b53ff1669e3dec" def build(tc: Toolchain) -> dict[str, float]: tarball = RW_DIR / f"eigen-{version}.tar.gz" From 169cdd68f828ed9a3a50637078ff605b97bea2cf Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Tue, 4 Aug 2026 12:23:40 +0200 Subject: [PATCH 2/5] complete cmath arithmetic overloads Provide correctly typed unary and binary overloads for floating-point and integral arguments while retaining the C names from . Match GCC's builtin-backed constant evaluation without requiring unsupported constexpr math from Clang. --- include/cmath | 175 +++++++++++++++++++++++++++------- tests/test_cmath.cpp | 4 + tests/test_cmath_surface.cpp | 87 +++++++++++++++++ tests/test_math_h_surface.cpp | 27 ++++++ 4 files changed, 261 insertions(+), 32 deletions(-) create mode 100644 tests/test_cmath_surface.cpp create mode 100644 tests/test_math_h_surface.cpp diff --git a/include/cmath b/include/cmath index 2b292f0..25751ff 100644 --- a/include/cmath +++ b/include/cmath @@ -33,6 +33,12 @@ #undef fpclassify #endif +#if defined(__GNUC__) && !defined(__clang__) +#define _PSYCHICSTD_CMATH_CONSTEXPR constexpr __attribute__((__always_inline__)) +#else +#define _PSYCHICSTD_CMATH_CONSTEXPR __attribute__((__always_inline__)) +#endif + namespace std { using ::acos; using ::acosh; @@ -137,6 +143,14 @@ using ::rint; using ::scalbln; using ::scalbn; +namespace __cmath_detail { +template +concept integral = __builtin_classify_type(*(T*)nullptr) == 1 && !__is_enum(T); + +template +concept arithmetic = integral || __builtin_classify_type(*(T*)nullptr) == 8; +} // namespace __cmath_detail + // float overloads using ::ceilf; using ::cosf; @@ -159,18 +173,72 @@ using ::tgammaf; inline float abs(float x) { return ::fabsf(x); } inline double abs(double x) { return ::fabs(x); } inline long double abs(long double x) { return ::fabsl(x); } -inline float sqrt(float x) { return ::sqrtf(x); } -inline long double sqrt(long double x) { return ::sqrtl(x); } -inline float exp(float x) { return ::expf(x); } -inline long double exp(long double x) { return ::expl(x); } -inline float log(float x) { return ::logf(x); } -inline long double log(long double x) { return ::logl(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR float sqrt(float x) { return ::sqrtf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double sqrt(long double x) { + return ::sqrtl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float exp(float x) { return ::expf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double exp(long double x) { + return ::expl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float log(float x) { return ::logf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double log(long double x) { + return ::logl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float log10(float x) { return ::log10f(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double log10(long double x) { + return ::log10l(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float sin(float x) { return ::sinf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double sin(long double x) { + return ::sinl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float cos(float x) { return ::cosf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double cos(long double x) { + return ::cosl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float tan(float x) { return ::tanf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double tan(long double x) { + return ::tanl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float sinh(float x) { return ::sinhf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double sinh(long double x) { + return ::sinhl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float cosh(float x) { return ::coshf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double cosh(long double x) { + return ::coshl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float tanh(float x) { return ::tanhf(x); } +inline _PSYCHICSTD_CMATH_CONSTEXPR long double tanh(long double x) { + return ::tanhl(x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float atan2(float y, float x) { + return ::atan2f(y, x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR long double atan2(long double y, + long double x) { + return ::atan2l(y, x); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float hypot(float x, float y) { + return ::hypotf(x, y); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR long double hypot(long double x, + long double y) { + return ::hypotl(x, y); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR float pow(float base, float exponent) { + return ::powf(base, exponent); +} +inline _PSYCHICSTD_CMATH_CONSTEXPR long double pow(long double base, + long double exponent) { + return ::powl(base, exponent); +} inline float ldexp(float x, int exponent) { return ::ldexpf(x, exponent); } inline long double ldexp(long double x, int exponent) { return ::ldexpl(x, exponent); } -template - requires(__builtin_classify_type(*(Integer*)nullptr) == 1) +template <__cmath_detail::integral Integer> inline double ldexp(Integer x, int exponent) { return ::ldexp(static_cast(x), exponent); } @@ -186,39 +254,80 @@ inline float fma(float x, float y, float z) { return ::fmaf(x, y, z); } inline long double fma(long double x, long double y, long double z) { return ::fmal(x, y, z); } -#if _PSYCHICSTD_COMPATIBILITY_LEVEL >= _PSYCHICSTD_COMPAT_DROPIN -template - requires(is_integral_v) -inline double exp(T x) { +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double exp(T x) { return ::exp(static_cast(x)); } -template - requires(is_integral_v) -inline double log(T x) { +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double log(T x) { return ::log(static_cast(x)); } -template - requires(is_integral_v) -constexpr double sqrt(T x) { +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double sqrt(T x) { return __builtin_sqrt(static_cast(x)); } -#else -template - requires(__builtin_classify_type(*(T*)nullptr) == 1) -inline double exp(T x) { - return ::exp(static_cast(x)); +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double log10(T x) { + return ::log10(static_cast(x)); } -template - requires(__builtin_classify_type(*(T*)nullptr) == 1) -inline double log(T x) { - return ::log(static_cast(x)); +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double sin(T x) { + return ::sin(static_cast(x)); } -template - requires(__builtin_classify_type(*(T*)nullptr) == 1) -constexpr double sqrt(T x) { - return __builtin_sqrt(static_cast(x)); +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double cos(T x) { + return ::cos(static_cast(x)); +} +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double tan(T x) { + return ::tan(static_cast(x)); +} +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double sinh(T x) { + return ::sinh(static_cast(x)); +} +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double cosh(T x) { + return ::cosh(static_cast(x)); +} +template <__cmath_detail::integral T> +inline _PSYCHICSTD_CMATH_CONSTEXPR double tanh(T x) { + return ::tanh(static_cast(x)); +} + +template + requires(__cmath_detail::arithmetic && __cmath_detail::arithmetic) +inline _PSYCHICSTD_CMATH_CONSTEXPR auto atan2(T y, U x) { + if constexpr (__is_same(T, long double) || __is_same(U, long double)) + return ::atan2l(static_cast(y), static_cast(x)); + else if constexpr (__is_same(T, float) && __is_same(U, float)) + return ::atan2f(y, x); + else + return ::atan2(static_cast(y), static_cast(x)); +} + +template + requires(__cmath_detail::arithmetic && __cmath_detail::arithmetic) +inline _PSYCHICSTD_CMATH_CONSTEXPR auto hypot(T x, U y) { + if constexpr (__is_same(T, long double) || __is_same(U, long double)) + return ::hypotl(static_cast(x), static_cast(y)); + else if constexpr (__is_same(T, float) && __is_same(U, float)) + return ::hypotf(x, y); + else + return ::hypot(static_cast(x), static_cast(y)); +} + +template + requires(__cmath_detail::arithmetic && __cmath_detail::arithmetic) +inline _PSYCHICSTD_CMATH_CONSTEXPR auto pow(T base, U exponent) { + if constexpr (__is_same(T, long double) || __is_same(U, long double)) + return ::powl(static_cast(base), + static_cast(exponent)); + else if constexpr (__is_same(T, float) && __is_same(U, float)) + return ::powf(base, exponent); + else + return ::pow(static_cast(base), static_cast(exponent)); } -#endif // integer abs overloads come from <__psychicstd_abs> inline float nextafter(float x, float y) { return ::nextafterf(x, y); } using ::nextafter; @@ -255,3 +364,5 @@ inline long double expm1(long double x) { return ::expm1l(x); } inline double expm1(double x) { return ::expm1(x); } inline float expm1(float x) { return ::expm1f(x); } } // namespace std + +#undef _PSYCHICSTD_CMATH_CONSTEXPR diff --git a/tests/test_cmath.cpp b/tests/test_cmath.cpp index a2dd0ea..759e745 100644 --- a/tests/test_cmath.cpp +++ b/tests/test_cmath.cpp @@ -21,6 +21,10 @@ int main() { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); diff --git a/tests/test_cmath_surface.cpp b/tests/test_cmath_surface.cpp new file mode 100644 index 0000000..b22975b --- /dev/null +++ b/tests/test_cmath_surface.cpp @@ -0,0 +1,87 @@ +#include + +#define CHECK_UNARY_OVERLOADS(function) \ + static_assert(__is_same(decltype(std::function(1.0F)), float)); \ + static_assert(__is_same(decltype(std::function(1.0)), double)); \ + static_assert(__is_same(decltype(std::function(1.0L)), long double)); \ + static_assert(__is_same(decltype(std::function(1)), double)) + +#define CHECK_BINARY_OVERLOADS(function) \ + static_assert(__is_same(decltype(std::function(1.0F, 2.0F)), float)); \ + static_assert(__is_same(decltype(std::function(1.0, 2.0)), double)); \ + static_assert(__is_same(decltype(std::function(1.0L, 2.0L)), long double)); \ + static_assert(__is_same(decltype(std::function(1, 2)), double)); \ + static_assert(__is_same(decltype(std::function(1.0F, 2.0)), double)); \ + static_assert(__is_same(decltype(std::function(1.0, 2.0L)), long double)) + +CHECK_UNARY_OVERLOADS(sqrt); +CHECK_UNARY_OVERLOADS(exp); +CHECK_UNARY_OVERLOADS(log); +CHECK_UNARY_OVERLOADS(log10); +CHECK_UNARY_OVERLOADS(sin); +CHECK_UNARY_OVERLOADS(cos); +CHECK_UNARY_OVERLOADS(tan); +CHECK_UNARY_OVERLOADS(sinh); +CHECK_UNARY_OVERLOADS(cosh); +CHECK_UNARY_OVERLOADS(tanh); +CHECK_BINARY_OVERLOADS(atan2); +CHECK_BINARY_OVERLOADS(hypot); +CHECK_BINARY_OVERLOADS(pow); + +enum class not_arithmetic {}; + +#define CHECK_REJECTS_ENUM_UNARY(function) \ + template \ + concept has_std_##function = requires(T value) { std::function(value); }; \ + static_assert(!has_std_##function) + +#define CHECK_REJECTS_ENUM_BINARY(function) \ + template \ + concept has_std_##function = \ + requires(T value) { std::function(value, value); }; \ + static_assert(!has_std_##function) + +CHECK_REJECTS_ENUM_UNARY(sqrt); +CHECK_REJECTS_ENUM_UNARY(exp); +CHECK_REJECTS_ENUM_UNARY(log); +CHECK_REJECTS_ENUM_UNARY(log10); +CHECK_REJECTS_ENUM_UNARY(sin); +CHECK_REJECTS_ENUM_UNARY(cos); +CHECK_REJECTS_ENUM_UNARY(tan); +CHECK_REJECTS_ENUM_UNARY(sinh); +CHECK_REJECTS_ENUM_UNARY(cosh); +CHECK_REJECTS_ENUM_UNARY(tanh); +CHECK_REJECTS_ENUM_BINARY(atan2); +CHECK_REJECTS_ENUM_BINARY(hypot); +CHECK_REJECTS_ENUM_BINARY(pow); + +#if defined(__GNUC__) && !defined(__clang__) +#define CHECK_CONSTEXPR_UNARY(function, input, expected) \ + static_assert(std::function(input##F) == expected##F); \ + static_assert(std::function(input) == expected); \ + static_assert(std::function(input##L) == expected##L); \ + static_assert(std::function(static_cast(input)) == expected) + +#define CHECK_CONSTEXPR_BINARY(function, left, right, expected) \ + static_assert(std::function(left##F, right##F) == expected##F); \ + static_assert(std::function(left, right) == expected); \ + static_assert(std::function(left##L, right##L) == expected##L); \ + static_assert(std::function(static_cast(left), \ + static_cast(right)) == expected) + +CHECK_CONSTEXPR_UNARY(sqrt, 4.0, 2.0); +CHECK_CONSTEXPR_UNARY(exp, 0.0, 1.0); +CHECK_CONSTEXPR_UNARY(log, 1.0, 0.0); +CHECK_CONSTEXPR_UNARY(log10, 1.0, 0.0); +CHECK_CONSTEXPR_UNARY(sin, 0.0, 0.0); +CHECK_CONSTEXPR_UNARY(cos, 0.0, 1.0); +CHECK_CONSTEXPR_UNARY(tan, 0.0, 0.0); +CHECK_CONSTEXPR_UNARY(sinh, 0.0, 0.0); +CHECK_CONSTEXPR_UNARY(cosh, 0.0, 1.0); +CHECK_CONSTEXPR_UNARY(tanh, 0.0, 0.0); +CHECK_CONSTEXPR_BINARY(atan2, 0.0, 1.0, 0.0); +CHECK_CONSTEXPR_BINARY(hypot, 3.0, 4.0, 5.0); +CHECK_CONSTEXPR_BINARY(pow, 2.0, 3.0, 8.0); +#endif + +int main() {} diff --git a/tests/test_math_h_surface.cpp b/tests/test_math_h_surface.cpp new file mode 100644 index 0000000..053fea2 --- /dev/null +++ b/tests/test_math_h_surface.cpp @@ -0,0 +1,27 @@ +#include + +#define CHECK_C_UNARY(function) \ + static_assert(__is_same(decltype(::function##f(1.0F)), float)); \ + static_assert(__is_same(decltype(::function(1.0)), double)); \ + static_assert(__is_same(decltype(::function##l(1.0L)), long double)) + +#define CHECK_C_BINARY(function) \ + static_assert(__is_same(decltype(::function##f(1.0F, 2.0F)), float)); \ + static_assert(__is_same(decltype(::function(1.0, 2.0)), double)); \ + static_assert(__is_same(decltype(::function##l(1.0L, 2.0L)), long double)) + +CHECK_C_UNARY(sqrt); +CHECK_C_UNARY(exp); +CHECK_C_UNARY(log); +CHECK_C_UNARY(log10); +CHECK_C_UNARY(sin); +CHECK_C_UNARY(cos); +CHECK_C_UNARY(tan); +CHECK_C_UNARY(sinh); +CHECK_C_UNARY(cosh); +CHECK_C_UNARY(tanh); +CHECK_C_BINARY(atan2); +CHECK_C_BINARY(hypot); +CHECK_C_BINARY(pow); + +int main() {} From 3bc3aeaedd96597defd9c7c525f70b9cc6eae09a Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Tue, 4 Aug 2026 12:23:46 +0200 Subject: [PATCH 3/5] expand complex math support Add the standard operations needed by Eigen and use compiler complex builtins for overflow-safe magnitude, square root, and exponential edge cases. Keep stream insertion lightweight while consuming Eigen field widths correctly. --- cmake/psychicstd-runtime-sources.txt | 1 + include/complex | 234 ++++++++++++++++++++++++++- src/complex.cpp | 30 ++++ tests/CMakeLists.txt | 3 + tests/test_complex.cpp | 68 ++++++++ tests/test_complex_math_surface.cpp | 39 +++++ 6 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 src/complex.cpp create mode 100644 tests/test_complex_math_surface.cpp diff --git a/cmake/psychicstd-runtime-sources.txt b/cmake/psychicstd-runtime-sources.txt index 46fa9d1..eee00d8 100644 --- a/cmake/psychicstd-runtime-sources.txt +++ b/cmake/psychicstd-runtime-sources.txt @@ -3,6 +3,7 @@ src/atomic.cpp src/cerr.cpp src/cin.cpp src/clog.cpp +src/complex.cpp src/condition_variable.cpp src/cout.cpp src/filesystem.cpp diff --git a/include/complex b/include/complex index 9a783be..8b8f116 100644 --- a/include/complex +++ b/include/complex @@ -58,12 +58,82 @@ inline float sqrt(float value) { return __builtin_sqrtf(value); } inline double sqrt(double value) { return __builtin_sqrt(value); } inline long double sqrt(long double value) { return __builtin_sqrtl(value); } +template T hypot(T x, T y) { + return static_cast(__builtin_hypotl(static_cast(x), + static_cast(y))); +} +inline float hypot(float x, float y) { return __builtin_hypotf(x, y); } +inline double hypot(double x, double y) { return __builtin_hypot(x, y); } +inline long double hypot(long double x, long double y) { + return __builtin_hypotl(x, y); +} + +template T exp(T value) { + return static_cast(__builtin_expl(static_cast(value))); +} +inline float exp(float value) { return __builtin_expf(value); } +inline double exp(double value) { return __builtin_exp(value); } +inline long double exp(long double value) { return __builtin_expl(value); } + +template T log(T value) { + return static_cast(__builtin_logl(static_cast(value))); +} +inline float log(float value) { return __builtin_logf(value); } +inline double log(double value) { return __builtin_log(value); } +inline long double log(long double value) { return __builtin_logl(value); } + +template T sin(T value) { + return static_cast(__builtin_sinl(static_cast(value))); +} +inline float sin(float value) { return __builtin_sinf(value); } +inline double sin(double value) { return __builtin_sin(value); } +inline long double sin(long double value) { return __builtin_sinl(value); } + +template T cos(T value) { + return static_cast(__builtin_cosl(static_cast(value))); +} +inline float cos(float value) { return __builtin_cosf(value); } +inline double cos(double value) { return __builtin_cos(value); } +inline long double cos(long double value) { return __builtin_cosl(value); } + +template T sinh(T value) { + return static_cast(__builtin_sinhl(static_cast(value))); +} +inline float sinh(float value) { return __builtin_sinhf(value); } +inline double sinh(double value) { return __builtin_sinh(value); } +inline long double sinh(long double value) { return __builtin_sinhl(value); } + +template T cosh(T value) { + return static_cast(__builtin_coshl(static_cast(value))); +} +inline float cosh(float value) { return __builtin_coshf(value); } +inline double cosh(double value) { return __builtin_cosh(value); } +inline long double cosh(long double value) { return __builtin_coshl(value); } + template inline constexpr int floating_rank = __is_same(T, float) ? 1 : __is_same(T, double) ? 2 : __is_same(T, long double) ? 3 : 0; +template inline constexpr bool is_integral = false; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; +template <> inline constexpr bool is_integral = true; + } // namespace __complex_detail template class complex { @@ -251,18 +321,178 @@ template constexpr bool operator!=(T a, const complex& b) { template T arg(const complex& c) { return __complex_detail::atan2(c.imag(), c.real()); } +inline float arg(float value) { return __complex_detail::atan2(0.0F, value); } +inline double arg(double value) { return __complex_detail::atan2(0.0, value); } +inline long double arg(long double value) { + return __complex_detail::atan2(0.0L, value); +} +template + requires(__complex_detail::is_integral) +double arg(T value) { + return arg(static_cast(value)); +} template T norm(const complex& c) { return c.real() * c.real() + c.imag() * c.imag(); } template T abs(const complex& c) { - return __complex_detail::sqrt(norm(c)); + return __complex_detail::hypot(c.real(), c.imag()); +} +template complex polar(const T& rho, const T& theta = T()) { + return complex(rho * __complex_detail::cos(theta), + rho * __complex_detail::sin(theta)); +} +template complex sqrt(const complex& c) { + if constexpr (__complex_detail::floating_rank == 1) { + __complex__ float value; + __real__ value = c.real(); + __imag__ value = c.imag(); + value = __builtin_csqrtf(value); + return complex(__real__ value, __imag__ value); + } else if constexpr (__complex_detail::floating_rank == 2) { + __complex__ double value; + __real__ value = c.real(); + __imag__ value = c.imag(); + value = __builtin_csqrt(value); + return complex(__real__ value, __imag__ value); + } else if constexpr (__complex_detail::floating_rank == 3) { + __complex__ long double value; + __real__ value = c.real(); + __imag__ value = c.imag(); + value = __builtin_csqrtl(value); + return complex(__real__ value, __imag__ value); + } else { + return polar(__complex_detail::sqrt(abs(c)), arg(c) / T(2)); + } } +template complex exp(const complex& c) { + if constexpr (__complex_detail::floating_rank == 1) { + __complex__ float value; + __real__ value = c.real(); + __imag__ value = c.imag(); + value = __builtin_cexpf(value); + return complex(__real__ value, __imag__ value); + } else if constexpr (__complex_detail::floating_rank == 2) { + __complex__ double value; + __real__ value = c.real(); + __imag__ value = c.imag(); + value = __builtin_cexp(value); + return complex(__real__ value, __imag__ value); + } else if constexpr (__complex_detail::floating_rank == 3) { + __complex__ long double value; + __real__ value = c.real(); + __imag__ value = c.imag(); + value = __builtin_cexpl(value); + return complex(__real__ value, __imag__ value); + } else { + return polar(__complex_detail::exp(c.real()), c.imag()); + } +} +template complex log(const complex& c) { + return complex(__complex_detail::log(abs(c)), arg(c)); +} +template complex log10(const complex& c) { + return log(c) / __complex_detail::log(T(10)); +} +template +complex pow(const complex& base, const complex& exponent) { + return exp(exponent * log(base)); +} +// [cmplx.over] treats integral exponents as double. Do not preserve float as +// libstdc++'s legacy complex overload does. +template + requires(__complex_detail::is_integral) +complex pow(const complex& base, + Integer exponent) { + using R = decltype(T() + double()); + complex factor(base); + complex result(R(1)); + const bool reciprocal = exponent < Integer(); + while (exponent != Integer()) { + if (exponent % Integer(2) != Integer()) + result *= factor; + factor *= factor; + exponent /= Integer(2); + } + return reciprocal ? complex(R(1)) / result : result; +} +template +complex pow(const complex& base, const T& exponent) { + return exp(complex(exponent) * log(base)); +} +template +complex pow(const T& base, const complex& exponent) { + return exp(exponent * log(complex(base))); +} + +// Compiler complex builtins preserve special values and avoid intermediate +// overflow in the elementary formulas. +#define _PSYCHICSTD_COMPLEX_BUILTIN(name, input) \ + if constexpr (__complex_detail::floating_rank == 1) { \ + __complex__ float value; \ + __real__ value = input.real(); \ + __imag__ value = input.imag(); \ + value = __builtin_##name##f(value); \ + return complex(__real__ value, __imag__ value); \ + } else if constexpr (__complex_detail::floating_rank == 2) { \ + __complex__ double value; \ + __real__ value = input.real(); \ + __imag__ value = input.imag(); \ + value = __builtin_##name(value); \ + return complex(__real__ value, __imag__ value); \ + } else if constexpr (__complex_detail::floating_rank == 3) { \ + __complex__ long double value; \ + __real__ value = input.real(); \ + __imag__ value = input.imag(); \ + value = __builtin_##name##l(value); \ + return complex(__real__ value, __imag__ value); \ + } else + +template complex sin(const complex& c) { + _PSYCHICSTD_COMPLEX_BUILTIN(csin, c) + return complex( + __complex_detail::sin(c.real()) * __complex_detail::cosh(c.imag()), + __complex_detail::cos(c.real()) * __complex_detail::sinh(c.imag())); +} +template complex cos(const complex& c) { + _PSYCHICSTD_COMPLEX_BUILTIN(ccos, c) + return complex( + __complex_detail::cos(c.real()) * __complex_detail::cosh(c.imag()), + -__complex_detail::sin(c.real()) * __complex_detail::sinh(c.imag())); +} +template complex tan(const complex& c) { + _PSYCHICSTD_COMPLEX_BUILTIN(ctan, c) + return sin(c) / cos(c); +} +template complex sinh(const complex& c) { + _PSYCHICSTD_COMPLEX_BUILTIN(csinh, c) + return complex( + __complex_detail::sinh(c.real()) * __complex_detail::cos(c.imag()), + __complex_detail::cosh(c.real()) * __complex_detail::sin(c.imag())); +} +template complex cosh(const complex& c) { + _PSYCHICSTD_COMPLEX_BUILTIN(ccosh, c) + return complex( + __complex_detail::cosh(c.real()) * __complex_detail::cos(c.imag()), + __complex_detail::sinh(c.real()) * __complex_detail::sin(c.imag())); +} +template complex tanh(const complex& c) { + _PSYCHICSTD_COMPLEX_BUILTIN(ctanh, c) + return sinh(c) / cosh(c); +} + +#undef _PSYCHICSTD_COMPLEX_BUILTIN // I/O +namespace __complex_detail { +template +basic_ostream& write_complex(basic_ostream&, + T real, T imag); +} // namespace __complex_detail + template basic_ostream& operator<<(basic_ostream& os, const complex& c) { - return os << '(' << c.real() << ',' << c.imag() << ')'; + return __complex_detail::write_complex(os, c.real(), c.imag()); } } // namespace std diff --git a/src/complex.cpp b/src/complex.cpp new file mode 100644 index 0000000..65bec14 --- /dev/null +++ b/src/complex.cpp @@ -0,0 +1,30 @@ +#include +#include + +namespace std::__complex_detail { + +template +basic_ostream& write_complex(basic_ostream& out, + T real, T imag) { + basic_ostringstream field; + field.flags(out.flags()); + field.imbue(out.getloc()); + field.precision(out.precision()); + field << '(' << real << ',' << imag << ')'; + return out << field.str(); +} + +#define _PSYCHICSTD_INSTANTIATE_COMPLEX_OUTPUT(CharT, T) \ + template basic_ostream>& write_complex( \ + basic_ostream>&, T, T) + +_PSYCHICSTD_INSTANTIATE_COMPLEX_OUTPUT(char, float); +_PSYCHICSTD_INSTANTIATE_COMPLEX_OUTPUT(char, double); +_PSYCHICSTD_INSTANTIATE_COMPLEX_OUTPUT(char, long double); +_PSYCHICSTD_INSTANTIATE_COMPLEX_OUTPUT(wchar_t, float); +_PSYCHICSTD_INSTANTIATE_COMPLEX_OUTPUT(wchar_t, double); +_PSYCHICSTD_INSTANTIATE_COMPLEX_OUTPUT(wchar_t, long double); + +#undef _PSYCHICSTD_INSTANTIATE_COMPLEX_OUTPUT + +} // namespace std::__complex_detail diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6eb2990..5acc0c9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,8 +13,10 @@ set(TESTS test_charconv test_chrono test_cmath + test_cmath_surface test_compare test_complex + test_complex_math_surface test_concepts test_coroutine test_csetjmp @@ -41,6 +43,7 @@ set(TESTS test_list test_locale test_map + test_math_h_surface test_memory test_memory_include_order test_mutex diff --git a/tests/test_complex.cpp b/tests/test_complex.cpp index 7b35911..eb41694 100644 --- a/tests/test_complex.cpp +++ b/tests/test_complex.cpp @@ -1,7 +1,19 @@ #include "psyassert.h" #include +#include +#include +#include #include +namespace { +struct unrelated_expression {}; + +template +concept has_std_arg = requires(T value) { std::arg(value); }; + +static_assert(!has_std_arg); +} // namespace + int main() { static_assert( std::is_convertible_v, std::complex>); @@ -17,9 +29,17 @@ int main() { psyassert(c.real() == 3.0); psyassert(std::abs(c) == 5.0); psyassert(std::arg(std::complex(1.0, 0.0)) == 0.0); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + psyassert(std::arg(1.0F) == 0.0F); + psyassert(std::arg(-1) > 3.0); std::complex wide(5.0L, 12.0L); psyassert(std::abs(wide) == 13.0L); + const double maximum = std::numeric_limits::max(); + psyassert(std::abs(std::complex(maximum, maximum)) == + std::numeric_limits::infinity()); + psyassert(std::abs(std::complex(maximum, 1.0)) == maximum); std::complex narrow(1.5F, -2.5F); std::complex converted = narrow; psyassert(converted.real() == 1.5L); @@ -30,4 +50,52 @@ int main() { c *= std::complex(2.0, -1.0); psyassert(c == std::complex(10.0, 5.0)); + psyassert(std::sqrt(std::complex(-4.0, 0.0)).imag() == 2.0); + psyassert(std::pow(std::complex(0.0, 1.0), 2.0).real() < 0.0); + const auto integer_power = std::pow(std::complex(0.0F, 1.0F), 2); + const auto real_error = integer_power.real() + 1.0; + psyassert(real_error > -1e-12 && real_error < 1e-12); + psyassert(integer_power.imag() > -1e-12 && integer_power.imag() < 1e-12); +#if defined(PSYCHICSTD_TEST_PSYCHICSTD) + const auto negative_integer_power = + std::pow(std::complex(0.0F, 2.0F), -2); + psyassert(negative_integer_power.real() == -0.25); + psyassert(negative_integer_power.imag() == 0.0); + const auto minimum_integer_power = std::pow(std::complex(1.0F, 0.0F), + std::numeric_limits::min()); + psyassert(minimum_integer_power.real() == 1.0); + psyassert(minimum_integer_power.imag() == 0.0); +#endif + psyassert(std::sin(std::complex(0.0, 0.0)) == std::complex()); + const auto large_tangent = std::tan(std::complex(0.0, 1000.0)); + psyassert(large_tangent.real() == 0.0); + psyassert(large_tangent.imag() == 1.0); + const auto large_hyperbolic_tangent = + std::tanh(std::complex(1000.0, 0.0)); + psyassert(large_hyperbolic_tangent.real() == 1.0); + psyassert(large_hyperbolic_tangent.imag() == 0.0); + const auto infinite_sine = std::sin( + std::complex(0.0, std::numeric_limits::infinity())); + psyassert(infinite_sine.real() == 0.0); + psyassert(infinite_sine.imag() == std::numeric_limits::infinity()); + const auto logarithm = std::log10(std::complex(100.0, 0.0)); + psyassert(logarithm.real() > 1.99 && logarithm.real() < 2.01); + psyassert(logarithm.imag() == 0.0); + + const auto infinite_root = std::sqrt( + std::complex(std::numeric_limits::infinity(), 0.0)); + psyassert(infinite_root.real() == std::numeric_limits::infinity()); + psyassert(infinite_root.imag() == 0.0); + const auto infinite_exp = std::exp( + std::complex(std::numeric_limits::infinity(), 0.0F)); + psyassert(infinite_exp.real() == std::numeric_limits::infinity()); + psyassert(infinite_exp.imag() == 0.0F); + + std::ostringstream stream; + stream << std::setw(9) << std::complex(12.0, 34.0) << 'x'; + psyassert(stream.str() == " (12,34)x"); + + std::wostringstream wide_stream; + wide_stream << std::setw(9) << std::complex(12.0, 34.0); + psyassert(wide_stream.str() == L" (12,34)"); } diff --git a/tests/test_complex_math_surface.cpp b/tests/test_complex_math_surface.cpp new file mode 100644 index 0000000..eb757a0 --- /dev/null +++ b/tests/test_complex_math_surface.cpp @@ -0,0 +1,39 @@ +#include + +template constexpr void check_complex_math_surface() { + using C = std::complex; + static_assert(__is_same(decltype(std::abs(C())), T)); + static_assert(__is_same(decltype(std::arg(C())), T)); + static_assert(__is_same(decltype(std::norm(C())), T)); + static_assert(__is_same(decltype(std::polar(T(), T())), C)); + static_assert(__is_same(decltype(std::sqrt(C())), C)); + static_assert(__is_same(decltype(std::exp(C())), C)); + static_assert(__is_same(decltype(std::log(C())), C)); + static_assert(__is_same(decltype(std::log10(C())), C)); + static_assert(__is_same(decltype(std::pow(C(), C())), C)); + static_assert(__is_same(decltype(std::pow(C(), T())), C)); + static_assert(__is_same(decltype(std::pow(T(), C())), C)); +#if defined(PSYCHICSTD_TEST_PSYCHICSTD) + using Promoted = std::complex; + static_assert(__is_same(decltype(std::pow(C(), 1)), Promoted)); + static_assert(__is_same(decltype(std::pow(C(), 1L)), Promoted)); + static_assert(__is_same(decltype(std::pow(C(), 1ULL)), Promoted)); +#endif + static_assert(__is_same(decltype(std::sin(C())), C)); + static_assert(__is_same(decltype(std::cos(C())), C)); + static_assert(__is_same(decltype(std::tan(C())), C)); + static_assert(__is_same(decltype(std::sinh(C())), C)); + static_assert(__is_same(decltype(std::cosh(C())), C)); + static_assert(__is_same(decltype(std::tanh(C())), C)); +} + +static_assert(__is_same(decltype(std::arg(1.0F)), float)); +static_assert(__is_same(decltype(std::arg(1.0)), double)); +static_assert(__is_same(decltype(std::arg(1.0L)), long double)); +static_assert(__is_same(decltype(std::arg(1)), double)); + +int main() { + check_complex_math_surface(); + check_complex_math_surface(); + check_complex_math_surface(); +} From a51ef2c1653f1523f37f25b7e9adbc0791658c61 Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Tue, 4 Aug 2026 12:23:57 +0200 Subject: [PATCH 4/5] implement list resize Support shrinking and both forms of list growth while preserving existing nodes and iterators. Eigen exercises these operations through its STL tests. --- include/list | 12 ++++++++++++ tests/test_list.cpp | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/include/list b/include/list index 7088679..2726d10 100644 --- a/include/list +++ b/include/list @@ -212,6 +212,18 @@ public: unlink(n); delete n; } + void resize(size_type count) { + while (size_ > count) + pop_back(); + while (size_ < count) + emplace_back(); + } + void resize(size_type count, const value_type& value) { + while (size_ > count) + pop_back(); + while (size_ < count) + push_back(value); + } template iterator emplace_back(Args&&... args) { Node* n = new Node(static_cast(args)...); link_back(n); diff --git a/tests/test_list.cpp b/tests/test_list.cpp index dd89db2..7f4c1a2 100644 --- a/tests/test_list.cpp +++ b/tests/test_list.cpp @@ -52,6 +52,14 @@ int main() { stable_end.push_back(7); psyassert(*--old_end == 7); + std::list resized = {1, 2}; + resized.resize(4, 7); + psyassert((resized == std::list{1, 2, 7, 7})); + resized.resize(5); + psyassert(resized.back() == 0); + resized.resize(1); + psyassert((resized == std::list{1})); + std::list recycled = {8}; std::list reactor_queue; auto queue_end = reactor_queue.end(); From 11ed7e492febd859d4a0833363873ee7715f384e Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Tue, 4 Aug 2026 18:13:38 +0200 Subject: [PATCH 5/5] add complete Eigen benchmark Build Eigen's complete official test target as a separate real-world project. Cap compilation at two jobs because the heaviest translation units consume several GiB. Allow tests to use the requested parallelism. --- .github/workflows/full-projects-nightly.yml | 2 +- .../realworld_projects.py | 115 ++++++++++++++++-- 2 files changed, 105 insertions(+), 12 deletions(-) diff --git a/.github/workflows/full-projects-nightly.yml b/.github/workflows/full-projects-nightly.yml index 5b15538..9bc388b 100644 --- a/.github/workflows/full-projects-nightly.yml +++ b/.github/workflows/full-projects-nightly.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - project: [abseil-full, boost-test-full] + project: [abseil-full, boost-test-full, eigen-full] runs-on: ubuntu-latest timeout-minutes: 60 steps: diff --git a/use_on_realworld_projects/realworld_projects.py b/use_on_realworld_projects/realworld_projects.py index c124da5..2b54a1e 100755 --- a/use_on_realworld_projects/realworld_projects.py +++ b/use_on_realworld_projects/realworld_projects.py @@ -1065,7 +1065,7 @@ def build(tc: Toolchain) -> dict[str, float]: ) -def _eigen() -> Project: +def _eigen(full: bool) -> Project: version = "5.0.1" url = ( f"https://gitlab.com/libeigen/eigen/-/archive/{version}/eigen-{version}.tar.gz" @@ -1077,7 +1077,8 @@ def build(tc: Toolchain) -> dict[str, float]: _fetch(url, tarball, checksum) with tempfile.TemporaryDirectory( - prefix="rw-eigen-", ignore_cleanup_errors=True + prefix="rw-eigen-full-" if full else "rw-eigen-", + ignore_cleanup_errors=True, ) as work_dir: work = Path(work_dir) with tarfile.open(tarball) as t: @@ -1088,17 +1089,100 @@ def build(tc: Toolchain) -> dict[str, float]: # main.h's FORBIDDEN_IDENTIFIER macros clash with psychicstd's names. main_h = test_dir / "main.h" text = main_h.read_text() - for name in ( - "FORBIDDEN_IDENTIFIER", - "B0 FORBIDDEN_IDENTIFIER", - "I FORBIDDEN_IDENTIFIER", + for declaration in ( + ( + "#define FORBIDDEN_IDENTIFIER \\\n" + " (this_identifier_is_forbidden_to_avoid_clashes) " + "this_identifier_is_forbidden_to_avoid_clashes\n" + ), + "#define B0 FORBIDDEN_IDENTIFIER\n", + "#define I FORBIDDEN_IDENTIFIER\n", + "#define _res FORBIDDEN_IDENTIFIER\n", ): - text = text.replace(f"#define {name}", f"// DISABLED: #define {name}") + if text.count(declaration) != 1: + raise RuntimeError("unexpected Eigen forbidden-identifier block") + text = text.replace(declaration, "") main_h.write_text(text) env = _env(tc) # Keep randomized tests reproducible across benchmark variants. env["EIGEN_SEED"] = "1" + if full: + # Some Eigen test translation units consume several GiB while + # compiling. Keep memory-constrained nightly runners stable. + compile_jobs = f"-j{min(tc.jobs, 2)}" + test_jobs = f"-j{tc.jobs}" + configure = [ + "cmake", + "-S", + ".", + "-B", + "build", + "-GNinja", + "-DCMAKE_BUILD_TYPE=" + tc.build_type.capitalize(), + "-DCMAKE_CXX_COMPILER=" + tc.cxx, + "-DCMAKE_CXX_FLAGS=" + tc.cxxflags, + "-DCMAKE_EXE_LINKER_FLAGS=" + tc.ldflags, + "-DCMAKE_CXX_STANDARD_LIBRARIES=" + tc.libs, + "-DCMAKE_CXX_STANDARD=20", + "-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY", + # The overlay supplies libm explicitly in tc.libs, but + # Eigen's probe clears the required library list. + "-Dstandard_math_library_linked_to_automatically=ON", + "-DBUILD_TESTING=ON", + "-DEIGEN_BUILD_TESTING=ON", + "-DEIGEN_BUILD_BLAS=OFF", + "-DEIGEN_BUILD_CMAKE_PACKAGE=OFF", + "-DEIGEN_BUILD_DEMOS=OFF", + "-DEIGEN_BUILD_DOC=OFF", + "-DEIGEN_BUILD_LAPACK=OFF", + "-DEIGEN_BUILD_PKGCONFIG=OFF", + "-DEIGEN_TEST_EXTERNAL_BLAS=OFF", + "-DEIGEN_TEST_MAX_SIZE=320", + "-DEIGEN_TEST_NOQT=ON", + "-DEIGEN_TEST_OPENMP=OFF", + "-DCMAKE_DISABLE_FIND_PACKAGE_Accelerate=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_BLAS=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_Boost=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_CHOLMOD=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_CUDA=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_HIP=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_KLU=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_LAPACK=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_PASTIX=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_SPQR=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_SuperLU=ON", + "-DCMAKE_DISABLE_FIND_PACKAGE_UMFPACK=ON", + ] + return { + "configure": _timed(configure, src, env), + "compile": _timed( + [ + "cmake", + "--build", + "build", + "--target", + "BuildOfficial", + compile_jobs, + ], + src, + env, + ), + "run tests": _timed( + [ + "ctest", + "--test-dir", + "build", + "--output-on-failure", + "-L", + "Official", + test_jobs, + ], + src, + env, + ), + } + cxxflags = [ *tc.cxxflags.split(), "-I", @@ -1127,10 +1211,18 @@ def build(tc: Toolchain) -> dict[str, float]: return Project( version=version, build=build, - expected_seconds={"debug": 15, "release": 15}, + expected_seconds=( + {"debug": 900, "release": 900} if full else {"debug": 15, "release": 15} + ), + expected_jobs=2 if full else 20, phases=("compile", "run tests"), - comment="eigen has no configure step; a fixed subset of its test " - "suite is compiled and run individually, with times summed.", + comment=( + "Builds and runs Eigen's complete upstream official test target; " + "optional third-party numerical backends are disabled." + if full + else "eigen has no configure step; a fixed subset of its test " + "suite is compiled and run individually, with times summed." + ), ) @@ -3639,7 +3731,8 @@ def build(tc: Toolchain) -> dict[str, float]: "cmake": _cmake(), "cppcheck": _cppcheck(), "ctre": _ctre(), - "eigen": _eigen(), + "eigen": _eigen(full=False), + "eigen-full": _eigen(full=True), "electron": _electron(), "flatbuffers": _flatbuffers(), "fmt": _fmt(),