File: isqrt_constexpr.cpp

package info (click to toggle)
primecount 8.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,640 kB
  • sloc: cpp: 21,835; ansic: 121; sh: 99; makefile: 89
file content (137 lines) | stat: -rw-r--r-- 6,575 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
///
/// @file   isqrt_constexpr.cpp
/// @brief  Test compile time square root function.
///
/// Copyright (C) 2025 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <isqrt.hpp>
#include <int128_t.hpp>

#include <stdint.h>
#include <iostream>

using namespace primecount;

#if defined(BAD_ISQRT)

/// The following compile time integer square root function
/// has a recursion depth of O(sqrt(n)). This is very bad, the
/// stack will explose if you try to compute the square root
/// of a number > 10^9. Furthermore constexpr recursion depth
/// is limited by the compiler even more e.g. both GCC and
/// Clang currently limit constexpr recursion depth to 512.
///
/// The nasty thing is that GCC and Clang start calculating
/// constexpr functions at compile time but once the function
/// reaches a recursion depth > 512, GCC and Clang will stop
/// the compile time calculation and instead compute the
/// function at run-time.
///
/// Because of this you cannot easily tell if your constexpr
/// function was evaluated at compile time or at run-time.
/// But you can use static_assert to test whether your constexpr
/// function is calculated at compile time or at runtime.
/// The code below will not compile with the compiler error
/// message telling you the maximum recursion depth has been
/// exceeded.

template <typename T>
constexpr T bad_isqrt_helper(T sq, T dlt, T value)
{
  return sq > value ? (dlt >> 1) - 1
      : bad_isqrt_helper(sq + dlt, dlt + 2, value);
}

template <typename T>
constexpr T bad_isqrt(T value)
{
  return bad_isqrt_helper<T>(1, 3, value);
}

static_assert(bad_isqrt(100000000) == 10000, "bad_isqrt(10^8) failed!");

#endif

int main()
{
  static_assert(ct_sqrt(0) == 0, "ct_sqrt(0) failed!");
  static_assert(ct_sqrt(1) == 1, "ct_sqrt(1) failed!");
  static_assert(ct_sqrt(2) == 1, "ct_sqrt(2) failed!");
  static_assert(ct_sqrt(3) == 1, "ct_sqrt(3) failed!");
  static_assert(ct_sqrt(4) == 2, "ct_sqrt(4) failed!");
  static_assert(ct_sqrt(5) == 2, "ct_sqrt(5) failed!");
  static_assert(ct_sqrt(6) == 2, "ct_sqrt(6) failed!");
  static_assert(ct_sqrt(7) == 2, "ct_sqrt(7) failed!");
  static_assert(ct_sqrt(8) == 2, "ct_sqrt(8) failed!");
  static_assert(ct_sqrt(9) == 3, "ct_sqrt(9) failed!");
  static_assert(ct_sqrt(10) == 3, "ct_sqrt(10) failed!");
  static_assert(ct_sqrt(11) == 3, "ct_sqrt(11) failed!");
  static_assert(ct_sqrt(12) == 3, "ct_sqrt(12) failed!");
  static_assert(ct_sqrt(13) == 3, "ct_sqrt(13) failed!");
  static_assert(ct_sqrt(14) == 3, "ct_sqrt(14) failed!");
  static_assert(ct_sqrt(15) == 3, "ct_sqrt(15) failed!");
  static_assert(ct_sqrt(16) == 4, "ct_sqrt(16) failed!");
  static_assert(ct_sqrt(17) == 4, "ct_sqrt(17) failed!");
  static_assert(ct_sqrt(18) == 4, "ct_sqrt(18) failed!");
  static_assert(ct_sqrt(19) == 4, "ct_sqrt(19) failed!");
  static_assert(ct_sqrt(20) == 4, "ct_sqrt(20) failed!");
  static_assert(ct_sqrt(21) == 4, "ct_sqrt(21) failed!");
  static_assert(ct_sqrt(22) == 4, "ct_sqrt(22) failed!");
  static_assert(ct_sqrt(23) == 4, "ct_sqrt(23) failed!");
  static_assert(ct_sqrt(24) == 4, "ct_sqrt(24) failed!");
  static_assert(ct_sqrt(25) == 5, "ct_sqrt(25) failed!");
  static_assert(ct_sqrt(26) == 5, "ct_sqrt(26) failed!");
  static_assert(ct_sqrt(27) == 5, "ct_sqrt(27) failed!");
  static_assert(ct_sqrt(28) == 5, "ct_sqrt(28) failed!");
  static_assert(ct_sqrt(29) == 5, "ct_sqrt(29) failed!");
  static_assert(ct_sqrt(30) == 5, "ct_sqrt(30) failed!");
  static_assert(ct_sqrt(31) == 5, "ct_sqrt(31) failed!");
  static_assert(ct_sqrt(32) == 5, "ct_sqrt(32) failed!");
  static_assert(ct_sqrt(33) == 5, "ct_sqrt(33) failed!");
  static_assert(ct_sqrt(34) == 5, "ct_sqrt(34) failed!");
  static_assert(ct_sqrt(35) == 5, "ct_sqrt(35) failed!");
  static_assert(ct_sqrt(36) == 6, "ct_sqrt(36) failed!");
  static_assert(ct_sqrt(37) == 6, "ct_sqrt(37) failed!");
  static_assert(ct_sqrt(38) == 6, "ct_sqrt(38) failed!");
  static_assert(ct_sqrt(39) == 6, "ct_sqrt(39) failed!");

  static_assert(ct_sqrt(9223372037000249999ull) == 3037000499ull, "ct_sqrt(3037000500^2-1) failed!");
  static_assert(ct_sqrt(9223372037000250000ull) == 3037000500ull, "ct_sqrt(3037000500^2) failed!");
  static_assert(ct_sqrt(9223372037000250001ull) == 3037000500ull, "ct_sqrt(3037000500^2+1) failed!");

  static_assert(ct_sqrt(pstd::numeric_limits<int8_t>::max()) == 11, "ct_sqrt(2^7-1) failed!");
  static_assert(ct_sqrt(pstd::numeric_limits<uint8_t>::max()) == 15, "ct_sqrt(2^8-1) failed!");
  static_assert(ct_sqrt(pstd::numeric_limits<int16_t>::max()) == 181, "ct_sqrt(2^15-1) failed!");
  static_assert(ct_sqrt(pstd::numeric_limits<uint16_t>::max()) == 255, "ct_sqrt(2^16-1) failed!");
  static_assert(ct_sqrt(pstd::numeric_limits<int32_t>::max()) == 46340, "ct_sqrt(2^31-1) failed!");
  static_assert(ct_sqrt(pstd::numeric_limits<uint32_t>::max()) == 65535, "ct_sqrt(2^32-1) failed!");
  static_assert(ct_sqrt(pstd::numeric_limits<int64_t>::max()) == 3037000499ll, "ct_sqrt(2^63-1) failed!");
  static_assert(ct_sqrt(pstd::numeric_limits<uint64_t>::max()) == 4294967295ull, "ct_sqrt(2^64-1) failed!");

#if defined(HAVE_INT128_T)

  static_assert(ct_sqrt(pstd::numeric_limits<int128_t>::max()) == 13043817825332782212ull, "ct_sqrt(2^127-1) failed!");
  static_assert(ct_sqrt(pstd::numeric_limits<uint128_t>::max()) == 18446744073709551615ull, "ct_sqrt(2^128-1) failed!");

  // here std::sqrt((double) 443075998594972078030832658571409090) is 1 too small
  static_assert(ct_sqrt((((int128_t) 24019198012642651) << 64) | 15864680554123835074ull) == 665639541039271553ll, "ct_sqrt(443075998594972078030832658571409090) failed!");

  // here std::sqrt((double) 443075998594972075382716071791084150) is 1 too large
  static_assert(ct_sqrt((((int128_t) 24019198012642651) << 64) | 13216563967343510134ull) == 665639541039271551ll, "ct_sqrt(443075998594972075382716071791084150) failed!");

  // here std::sqrt((double) 443075998594971958032420320541208365) is 38 too small
  static_assert(ct_sqrt((((int128_t) 24019198012642645) << 64) | 6546732658350944045ull) == 665639541039271462ll, "ct_sqrt(443075998594971958032420320541208365) failed!");

  // here std::sqrt((double) 443075998594971969939937761777907585) is 81 too large
  static_assert(ct_sqrt((((int128_t) 24019198012642646) << 64) | 7506025878091649ull) == 665639541039271471ll, "ct_sqrt(443075998594971969939937761777907585) failed!");

#endif

  std::cout << "All tests passed successfully!" << std::endl;

  return 0;
}