GeographicLib: Math.hpp Source File

GeographicLib 2.5.2

Loading...
Searching...
No Matches
Math.hpp
Go to the documentation of this file.
1/**
2 * \file Math.hpp
3 * \brief Header for GeographicLib::Math class
4 *
5 * Copyright (c) Charles Karney (2008-2024) <karney@alum.mit.edu> and licensed
6 * under the MIT/X11 License. For more information, see
7 * https://geographiclib.sourceforge.io/
8 **********************************************************************/
9
10// Constants.hpp includes Math.hpp. Place this include outside Math.hpp's
11// include guard to enforce this ordering.
12#include <GeographicLib/Constants.hpp>
13
14#if !defined(GEOGRAPHICLIB_MATH_HPP)
15#define GEOGRAPHICLIB_MATH_HPP 1
16
17#if !defined(GEOGRAPHICLIB_WORDS_BIGENDIAN)
18# define GEOGRAPHICLIB_WORDS_BIGENDIAN 0
19#endif
20
21#if !defined(GEOGRAPHICLIB_HAVE_LONG_DOUBLE)
22# define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 0
23#endif
24
25#if !defined(GEOGRAPHICLIB_PRECISION)
26/**
27 * The precision of floating point numbers used in %GeographicLib. 1 means
28 * float (single precision); 2 (the default) means double; 3 means long double;
29 * 4 is reserved for quadruple precision. Nearly all the testing has been
30 * carried out with doubles and that's the recommended configuration. In order
31 * for long double to be used, GEOGRAPHICLIB_HAVE_LONG_DOUBLE needs to be
32 * defined. Note that with Microsoft Visual Studio, long double is the same as
33 * double.
34 **********************************************************************/
35# define GEOGRAPHICLIB_PRECISION 2
36#endif
37
38#include <cmath>
39#include <algorithm>
40#include <limits>
41
42#if GEOGRAPHICLIB_PRECISION == 4
43# include <memory>
44# include <boost/version.hpp>
45# include <boost/multiprecision/float128.hpp>
46# include <boost/math/special_functions.hpp>
47#elif GEOGRAPHICLIB_PRECISION >= 5
48# if GEOGRAPHICLIB_PRECISION > 5
49# define MPREAL_FIXED_PRECISION GEOGRAPHICLIB_PRECISION
50# else
51# define MPREAL_FIXED_PRECISION 0
52# endif
53# include <mpreal.h>
54#endif
55
56#if GEOGRAPHICLIB_PRECISION > 3
57// volatile keyword makes no sense for multiprec types
58# define GEOGRAPHICLIB_VOLATILE
59// Signal a convergence failure with multiprec types by throwing an exception
60// at loop exit.
61# define GEOGRAPHICLIB_PANIC(msg) \
62 (throw GeographicLib::GeographicErr(msg), false)
63#else
64# define GEOGRAPHICLIB_VOLATILE volatile
65// Ignore convergence failures with standard floating points types by allowing
66// loop to exit cleanly.
67# define GEOGRAPHICLIB_PANIC(msg) false
68#endif
69
70namespace GeographicLib {
71
72 /**
73 * \brief Mathematical functions needed by %GeographicLib
74 *
75 * Define mathematical functions in order to localize system dependencies and
76 * to provide generic versions of the functions. In addition define a real
77 * type to be used by %GeographicLib.
78 *
79 * Example of use:
80 * \include example-Math.cpp
81 **********************************************************************/
82 class GEOGRAPHICLIB_EXPORT Math {
83 private:
84 void dummy(); // Static check for GEOGRAPHICLIB_PRECISION
85 Math() = delete; // Disable constructor
86 public:
87
88#if GEOGRAPHICLIB_HAVE_LONG_DOUBLE
89 /**
90 * The extended precision type for real numbers, used for some testing.
91 * This is long double on computers with this type; otherwise it is double.
92 **********************************************************************/
93 typedef long double extended;
94#else
95 typedef double extended;
96#endif
97
98#if GEOGRAPHICLIB_PRECISION == 2
99 /**
100 * The real type for %GeographicLib. Nearly all the testing has been done
101 * with \e real = double. However, the algorithms should also work with
102 * float and long double (where available). (<b>CAUTION</b>: reasonable
103 * accuracy typically cannot be obtained using floats.)
104 **********************************************************************/
105 typedef double real;
106#elif GEOGRAPHICLIB_PRECISION == 1
107 typedef float real;
108#elif GEOGRAPHICLIB_PRECISION == 3
109 typedef extended real;
110#elif GEOGRAPHICLIB_PRECISION == 4
111 typedef boost::multiprecision::float128 real;
112#elif GEOGRAPHICLIB_PRECISION >= 5
113 typedef mpfr::mpreal real;
114#else
115 typedef double real;
116#endif
117
118 /**
119 * The constants defining the standard (Babylonian) meanings of degrees,
120 * minutes, and seconds, for angles. Read the constants as follows (for
121 * example): \e ms = 60 is the ratio 1 minute / 1 second. The
122 * abbreviations are
123 * - \e t a whole turn (360&deg;)
124 * - \e h a half turn (180&deg;)
125 * - \e q a quarter turn (a right angle = 90&deg;)
126 * - \e d a degree
127 * - \e m a minute
128 * - \e s a second
129 * .
130 * Note that degree() is ratio 1 degree / 1 radian, thus, for example,
131 * Math::degree() * Math::qd is the ratio 1 quarter turn / 1 radian =
132 * &pi;/2.
133 *
134 * Defining all these in one place would mean that it's simple to convert
135 * to the centesimal system for measuring angles. The DMS class assumes
136 * that Math::dm and Math::ms are less than or equal to 100 (so that two
137 * digits suffice for the integer parts of the minutes and degrees
138 * components of an angle). Switching to the centesimal convention will
139 * break most of the tests. Also the normal definition of degree is baked
140 * into some classes, e.g., UTMUPS, MGRS, Georef, Geohash, etc.
141 **********************************************************************/
142#if __cplusplus >= 201703L
143 static inline constexpr int qd = 90; ///< degrees per quarter turn
144 static inline constexpr int dm = 60; ///< minutes per degree
145 static inline constexpr int ms = 60; ///< seconds per minute
146 static inline constexpr int hd = 2 * qd; ///< degrees per half turn
147 static inline constexpr int td = 2 * hd; ///< degrees per turn
148 static inline constexpr int ds = dm * ms; ///< seconds per degree
149#elif GEOGRAPHICLIB_PRECISION < 4
150 static constexpr int qd = 90; ///< degrees per quarter turn
151 static constexpr int dm = 60; ///< minutes per degree
152 static constexpr int ms = 60; ///< seconds per minute
153 static constexpr int hd = 2 * qd; ///< degrees per half turn
154 static constexpr int td = 2 * hd; ///< degrees per turn
155 static constexpr int ds = dm * ms; ///< seconds per degree
156#else
157 enum dms {
158 qd = 90, ///< degrees per quarter turn
159 dm = 60, ///< minutes per degree
160 ms = 60, ///< seconds per minute
161 hd = 2 * qd, ///< degrees per half turn
162 td = 2 * hd, ///< degrees per turn
163 ds = dm * ms ///< seconds per degree
164 };
165#endif
166
167 /**
168 * @return the number of bits of precision in a real number.
169 **********************************************************************/
170 static int digits();
171
172 /**
173 * Set the binary precision of a real number.
174 *
175 * @param[in] ndigits the number of bits of precision.
176 * @return the resulting number of bits of precision.
177 *
178 * This only has an effect when GEOGRAPHICLIB_PRECISION >= 5. See also
179 * Utility::set_digits for caveats about when this routine should be
180 * called. If GEOGRAPHICLIB_PRECISION > 5, the precision is set to the
181 * compile-time value of GEOGRAPHICLIB_PRECISION and \e ndigits is ignored.
182 **********************************************************************/
183 static int set_digits(int ndigits);
184
185 /**
186 * @return the number of decimal digits of precision in a real number.
187 **********************************************************************/
188 static int digits10();
189
190 /**
191 * Number of additional decimal digits of precision for real relative to
192 * double (0 for float).
193 **********************************************************************/
194 static int extra_digits();
195
196 /**
197 * true if the machine is big-endian.
198 **********************************************************************/
199 static const bool bigendian = GEOGRAPHICLIB_WORDS_BIGENDIAN;
200
201 /**
202 * @tparam T the type of the returned value.
203 * @return &pi;.
204 **********************************************************************/
205 template<typename T = real> static T pi() {
206 using std::atan2;
207 static const T pi = atan2(T(0), T(-1));
208 return pi;
209 }
210
211 /**
212 * @tparam T the type of the returned value.
213 * @return the number of radians in a degree.
214 **********************************************************************/
215 template<typename T = real> static T degree() {
216 static const T degree = pi<T>() / T(hd);
217 return degree;
218 }
219
220 /**
221 * Square a number.
222 *
223 * @tparam T the type of the argument and the returned value.
224 * @param[in] x
225 * @return <i>x</i><sup>2</sup>.
226 **********************************************************************/
227 template<typename T> static T sq(T x)
228 { return x * x; }
229
230 /**
231 * Normalize a two-vector.
232 *
233 * @tparam T the type of the argument and the returned value.
234 * @param[in,out] x on output set to <i>x</i>/hypot(<i>x</i>, <i>y</i>).
235 * @param[in,out] y on output set to <i>y</i>/hypot(<i>x</i>, <i>y</i>).
236 **********************************************************************/
237 template<typename T> static void norm(T& x, T& y) {
238#if defined(_MSC_VER) && defined(_M_IX86)
239 // hypot for Visual Studio (A=win32) fails monotonicity, e.g., with
240 // x = 0.6102683302836215
241 // y1 = 0.7906090004346522
242 // y2 = y1 + 1e-16
243 // the test
244 // hypot(x, y2) >= hypot(x, y1)
245 // fails. Reported 2021-03-14:
246 // https://developercommunity.visualstudio.com/t/1369259
247 // See also:
248 // https://bugs.python.org/issue43088
249 using std::sqrt; T h = sqrt(x * x + y * y);
250#else
251 using std::hypot; T h = hypot(x, y);
252#endif
253 x /= h; y /= h;
254 }
255
256 /**
257 * The error-free sum of two numbers.
258 *
259 * @tparam T the type of the argument and the returned value.
260 * @param[in] u
261 * @param[in] v
262 * @param[out] t the exact error given by (\e u + \e v) - \e s.
263 * @return \e s = round(\e u + \e v).
264 *
265 * See D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B.
266 *
267 * \note \e t can be the same as one of the first two arguments.
268 **********************************************************************/
269 template<typename T> static T sum(T u, T v, T& t);
270
271 /**
272 * Evaluate a polynomial.
273 *
274 * @tparam T the type of the arguments and returned value.
275 * @param[in] N the order of the polynomial.
276 * @param[in] p the coefficient array (of size \e N + 1) with
277 * <i>p</i><sub>0</sub> being coefficient of <i>x</i><sup><i>N</i></sup>.
278 * @param[in] x the variable.
279 * @return the value of the polynomial.
280 *
281 * Evaluate &sum;<sub><i>n</i>=0..<i>N</i></sub>
282 * <i>p</i><sub><i>n</i></sub> <i>x</i><sup><i>N</i>&minus;<i>n</i></sup>.
283 * Return 0 if \e N &lt; 0. Return <i>p</i><sub>0</sub>, if \e N = 0 (even
284 * if \e x is infinite or a nan). The evaluation uses Horner's method.
285 **********************************************************************/
286 template<typename T> static T polyval(int N, const T p[], T x) {
287 // This used to employ Math::fma; but that's too slow and it seemed not
288 // to improve the accuracy noticeably. This might change when there's
289 // direct hardware support for fma.
290 T y = N < 0 ? 0 : *p++;
291 while (--N >= 0) y = y * x + *p++;
292 return y;
293 }
294
295 /**
296 * Normalize an angle.
297 *
298 * @tparam T the type of the argument and returned value.
299 * @param[in] x the angle in degrees.
300 * @return the angle reduced to the range [&minus;180&deg;, 180&deg;].
301 *
302 * The range of \e x is unrestricted. If the result is &plusmn;0&deg; or
303 * &plusmn;180&deg; then the sign is the sign of \e x.
304 **********************************************************************/
305 template<typename T> static T AngNormalize(T x);
306
307 /**
308 * Normalize a latitude.
309 *
310 * @tparam T the type of the argument and returned value.
311 * @param[in] x the angle in degrees.
312 * @return x if it is in the range [&minus;90&deg;, 90&deg;], otherwise
313 * return NaN.
314 **********************************************************************/
315 template<typename T> static T LatFix(T x)
316 { using std::fabs; return fabs(x) > T(qd) ? NaN<T>() : x; }
317
318 /**
319 * The exact difference of two angles reduced to
320 * [&minus;180&deg;, 180&deg;].
321 *
322 * @tparam T the type of the arguments and returned value.
323 * @param[in] x the first angle in degrees.
324 * @param[in] y the second angle in degrees.
325 * @param[out] e the error term in degrees.
326 * @return \e d, the truncated value of \e y &minus; \e x.
327 *
328 * This computes \e z = \e y &minus; \e x exactly, reduced to
329 * [&minus;180&deg;, 180&deg;]; and then sets \e z = \e d + \e e where \e d
330 * is the nearest representable number to \e z and \e e is the truncation
331 * error. If \e z = &plusmn;0&deg; or &plusmn;180&deg;, then the sign of
332 * \e d is given by the sign of \e y &minus; \e x. The maximum absolute
333 * value of \e e is 2<sup>&minus;26</sup> (for doubles).
334 **********************************************************************/
335 template<typename T> static T AngDiff(T x, T y, T& e);
336
337 /**
338 * Difference of two angles reduced to [&minus;180&deg;, 180&deg;]
339 *
340 * @tparam T the type of the arguments and returned value.
341 * @param[in] x the first angle in degrees.
342 * @param[in] y the second angle in degrees.
343 * @return \e y &minus; \e x, reduced to the range [&minus;180&deg;,
344 * 180&deg;].
345 *
346 * The result is equivalent to computing the difference exactly, reducing
347 * it to [&minus;180&deg;, 180&deg;] and rounding the result.
348 **********************************************************************/
349 template<typename T> static T AngDiff(T x, T y)
350 { T e; return AngDiff(x, y, e); }
351
352 /**
353 * Coarsen a value close to zero.
354 *
355 * @tparam T the type of the argument and returned value.
356 * @param[in] x
357 * @return the coarsened value.
358 *
359 * The makes the smallest gap in \e x = 1/16 &minus; nextafter(1/16, 0) =
360 * 1/2<sup>57</sup> for doubles = 0.8 pm on the earth if \e x is an angle
361 * in degrees. (This is about 2000 times more resolution than we get with
362 * angles around 90&deg;.) We use this to avoid having to deal with near
363 * singular cases when \e x is non-zero but tiny (e.g.,
364 * 10<sup>&minus;200</sup>). This sign of &plusmn;0 is preserved.
365 **********************************************************************/
366 template<typename T> static T AngRound(T x);
367
368 /**
369 * Evaluate the sine and cosine function with the argument in degrees
370 *
371 * @tparam T the type of the arguments.
372 * @param[in] x in degrees.
373 * @param[out] sinx sin(<i>x</i>).
374 * @param[out] cosx cos(<i>x</i>).
375 *
376 * The results obey exactly the elementary properties of the trigonometric
377 * functions, e.g., sin 9&deg; = cos 81&deg; = &minus; sin 123456789&deg;.
378 * If x = &minus;0 or a negative multiple of 180&deg;, then \e sinx =
379 * &minus;0; this is the only case where &minus;0 is returned.
380 **********************************************************************/
381 template<typename T> static void sincosd(T x, T& sinx, T& cosx);
382
383 /**
384 * Evaluate the sine and cosine with reduced argument plus correction
385 *
386 * @tparam T the type of the arguments.
387 * @param[in] x reduced angle in degrees.
388 * @param[in] t correction in degrees.
389 * @param[out] sinx sin(<i>x</i> + <i>t</i>).
390 * @param[out] cosx cos(<i>x</i> + <i>t</i>).
391 *
392 * This is a variant of Math::sincosd allowing a correction to the angle to
393 * be supplied. \e x must be in [&minus;180&deg;, 180&deg;] and \e t is
394 * assumed to be a <i>small</i> correction. Math::AngRound is applied to
395 * the reduced angle to prevent problems with \e x + \e t being extremely
396 * close but not exactly equal to one of the four cardinal directions.
397 **********************************************************************/
398 template<typename T> static void sincosde(T x, T t, T& sinx, T& cosx);
399
400 /**
401 * Evaluate the sine function with the argument in degrees
402 *
403 * @tparam T the type of the argument and the returned value.
404 * @param[in] x in degrees.
405 * @return sin(<i>x</i>).
406 *
407 * The result is +0 for \e x = +0 and positive multiples of 180&deg;. The
408 * result is &minus;0 for \e x = -0 and negative multiples of 180&deg;.
409 **********************************************************************/
410 template<typename T> static T sind(T x);
411
412 /**
413 * Evaluate the cosine function with the argument in degrees
414 *
415 * @tparam T the type of the argument and the returned value.
416 * @param[in] x in degrees.
417 * @return cos(<i>x</i>).
418 *
419 * The result is +0 for \e x an odd multiple of 90&deg;.
420 **********************************************************************/
421 template<typename T> static T cosd(T x);
422
423 /**
424 * Evaluate the tangent function with the argument in degrees
425 *
426 * @tparam T the type of the argument and the returned value.
427 * @param[in] x in degrees.
428 * @return tan(<i>x</i>).
429 *
430 * If \e x is an odd multiple of 90&deg;, then a suitably large (but
431 * finite) value is returned.
432 **********************************************************************/
433 template<typename T> static T tand(T x);
434
435 /**
436 * Evaluate the atan2 function with the result in degrees
437 *
438 * @tparam T the type of the arguments and the returned value.
439 * @param[in] y
440 * @param[in] x
441 * @return atan2(<i>y</i>, <i>x</i>) in degrees.
442 *
443 * The result is in the range [&minus;180&deg; 180&deg;]. N.B.,
444 * atan2d(&plusmn;0, &minus;1) = &plusmn;180&deg;.
445 **********************************************************************/
446 template<typename T> static T atan2d(T y, T x);
447
448 /**
449 * Evaluate the atan function with the result in degrees
450 *
451 * @tparam T the type of the argument and the returned value.
452 * @param[in] x
453 * @return atan(<i>x</i>) in degrees.
454 **********************************************************************/
455 template<typename T> static T atand(T x);
456
457 /**
458 * Evaluate <i>e</i> atanh(<i>e x</i>)
459 *
460 * @tparam T the type of the argument and the returned value.
461 * @param[in] x
462 * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
463 * sqrt(|<i>e</i><sup>2</sup>|)
464 * @return <i>e</i> atanh(<i>e x</i>)
465 *
466 * If <i>e</i><sup>2</sup> is negative (<i>e</i> is imaginary), the
467 * expression is evaluated in terms of atan.
468 **********************************************************************/
469 template<typename T> static T eatanhe(T x, T es);
470
471 /**
472 * tan&chi; in terms of tan&phi;
473 *
474 * @tparam T the type of the argument and the returned value.
475 * @param[in] tau &tau; = tan&phi;
476 * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
477 * sqrt(|<i>e</i><sup>2</sup>|)
478 * @return &tau;&prime; = tan&chi;
479 *
480 * See Eqs. (7--9) of
481 * C. F. F. Karney,
482 * <a href="https://doi.org/10.1007/s00190-011-0445-3">
483 * Transverse Mercator with an accuracy of a few nanometers,</a>
484 * J. Geodesy 85(8), 475--485 (Aug. 2011)
485 * (preprint
486 * <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>).
487 **********************************************************************/
488 template<typename T> static T taupf(T tau, T es);
489
490 /**
491 * tan&phi; in terms of tan&chi;
492 *
493 * @tparam T the type of the argument and the returned value.
494 * @param[in] taup &tau;&prime; = tan&chi;
495 * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
496 * sqrt(|<i>e</i><sup>2</sup>|)
497 * @return &tau; = tan&phi;
498 *
499 * See Eqs. (19--21) of
500 * C. F. F. Karney,
501 * <a href="https://doi.org/10.1007/s00190-011-0445-3">
502 * Transverse Mercator with an accuracy of a few nanometers,</a>
503 * J. Geodesy 85(8), 475--485 (Aug. 2011)
504 * (preprint
505 * <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>).
506 **********************************************************************/
507 template<typename T> static T tauf(T taup, T es);
508
509 /**
510 * Implement hypot with 3 parameters
511 *
512 * @tparam T the type of the argument and the returned value.
513 * @param[in] x
514 * @param[in] y
515 * @param[in] z
516 * @return sqrt(<i>x</i><sup>2</sup> + <i>y</i><sup>2</sup> +
517 * <i>z</i><sup>2</sup>).
518 **********************************************************************/
519 template<typename T> static T hypot3(T x, T y, T z);
520
521 /**
522 * The NaN (not a number)
523 *
524 * @tparam T the type of the returned value.
525 * @return NaN if available, otherwise return the max real of type T.
526 **********************************************************************/
527 template<typename T = real> static T NaN();
528
529 /**
530 * Infinity
531 *
532 * @tparam T the type of the returned value.
533 * @return infinity if available, otherwise return the max real.
534 **********************************************************************/
535 template<typename T = real> static T infinity();
536
537 /**
538 * Swap the bytes of a quantity
539 *
540 * @tparam T the type of the argument and the returned value.
541 * @param[in] x
542 * @return x with its bytes swapped.
543 **********************************************************************/
544 template<typename T> static T swab(T x) {
545 union {
546 T r;
547 unsigned char c[sizeof(T)];
548 } b;
549 b.r = x;
550 for (int i = sizeof(T)/2; i--; )
551 std::swap(b.c[i], b.c[sizeof(T) - 1 - i]);
552 return b.r;
553 }
554
555 };
556
557} // namespace GeographicLib
558
559#endif // GEOGRAPHICLIB_MATH_HPP
Constants.hpp
Header for GeographicLib::Constants class.
GEOGRAPHICLIB_EXPORT
#define GEOGRAPHICLIB_EXPORT
Definition Constants.hpp:67
GEOGRAPHICLIB_WORDS_BIGENDIAN
#define GEOGRAPHICLIB_WORDS_BIGENDIAN
Definition Math.hpp:18
GeographicLib::Math::degree
static T degree()
Definition Math.hpp:215
GeographicLib::Math::dm
static constexpr int dm
minutes per degree
Definition Math.hpp:151
GeographicLib::Math::LatFix
static T LatFix(T x)
Definition Math.hpp:315
GeographicLib::Math::ds
static constexpr int ds
seconds per degree
Definition Math.hpp:155
GeographicLib::Math::extended
double extended
Definition Math.hpp:95
GeographicLib::Math::norm
static void norm(T &x, T &y)
Definition Math.hpp:237
GeographicLib::Math::sq
static T sq(T x)
Definition Math.hpp:227
GeographicLib::Math::qd
static constexpr int qd
degrees per quarter turn
Definition Math.hpp:150
GeographicLib::Math::bigendian
static const bool bigendian
Definition Math.hpp:199
GeographicLib::Math::td
static constexpr int td
degrees per turn
Definition Math.hpp:154
GeographicLib::Math::pi
static T pi()
Definition Math.hpp:205
GeographicLib::Math::NaN
static T NaN()
Definition Math.cpp:283
GeographicLib::Math::polyval
static T polyval(int N, const T p[], T x)
Definition Math.hpp:286
GeographicLib::Math::AngDiff
static T AngDiff(T x, T y, T &e)
Definition Math.cpp:80
GeographicLib::Math::hd
static constexpr int hd
degrees per half turn
Definition Math.hpp:153
GeographicLib::Math::AngDiff
static T AngDiff(T x, T y)
Definition Math.hpp:349
GeographicLib::Math::swab
static T swab(T x)
Definition Math.hpp:544
GeographicLib::Math::real
double real
Definition Math.hpp:115
GeographicLib::Math::ms
static constexpr int ms
seconds per minute
Definition Math.hpp:152
GeographicLib
Namespace for GeographicLib.
Definition Accumulator.cpp:12
std::swap
void swap(GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &a, GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &b)
Definition NearestNeighbor.hpp:819