1#pragma once
2
3#include <cmath>
4
5// Android NDK platform < 21 with libstdc++ has spotty C++11 support.
6// Various hacks in this header allow the rest of the codebase to use
7// standard APIs.
8#if (defined(__ANDROID__) && __ANDROID_API__ < 21 && defined(__GLIBCXX__)) || \
9 defined(__NEWLIB__)
10#include <stdexcept>
11
12namespace std {
13// Import double versions of these functions from the global namespace.
14using ::acosh;
15using ::asinh;
16using ::atanh;
17using ::erf;
18using ::erfc;
19using ::expm1;
20using ::lgamma;
21using ::log1p;
22using ::nearbyint;
23using ::round;
24using ::tgamma;
25using ::trunc;
26using ::truncf;
27
28// Define float versions the same way as more recent libstdc++
29inline float acosh(float x) {
30 return __builtin_acoshf(x);
31}
32inline float asinh(float x) {
33 return __builtin_asinhf(x);
34}
35inline float atanh(float x) {
36 return __builtin_atanhf(x);
37}
38inline float copysign(float x, float y) {
39 return __builtin_copysignf(x, y);
40}
41inline float erf(float x) {
42 return __builtin_erff(x);
43}
44inline float erfc(float x) {
45 return __builtin_erfcf(x);
46}
47inline float expm1(float x) {
48 return __builtin_expm1f(x);
49}
50inline float fmax(float x, float y) {
51 return __builtin_fmaxf(x, y);
52}
53inline float fmin(float x, float y) {
54 return __builtin_fminf(x, y);
55}
56inline float lgamma(float x) {
57 return __builtin_lgammaf(x);
58}
59inline float log1p(float x) {
60 return __builtin_log1pf(x);
61}
62inline float nearbyint(float x) {
63 return __builtin_nearbyintf(x);
64}
65inline float remainder(float x, float y) {
66 return __builtin_remainderf(x, y);
67}
68inline float round(float x) {
69 return __builtin_roundf(x);
70}
71inline float tgamma(float x) {
72 return __builtin_tgammaf(x);
73}
74inline float trunc(float x) {
75 return __builtin_truncf(x);
76}
77
78// __builtin_nexttoward isn't doesn't work. It appears to try to
79// link against the global nexttoward function, which is not present
80// prior to API 18. Just bail for now.
81inline float nexttoward(float x, long double y) {
82 throw std::runtime_error("std::nexttoward is not present on older Android");
83}
84inline double nexttoward(double x, long double y) {
85 throw std::runtime_error("std::nexttoward is not present on older Android");
86}
87
88#if !defined(__NEWLIB__)
89// TODO: this function needs to be implemented and tested. Currently just throw
90// an error.
91inline float hypot(float x, float y) {
92 throw std::runtime_error("std::hypot is not implemented on older Android");
93}
94inline double hypot(double x, double y) {
95 throw std::runtime_error("std::hypot is not implemented on older Android");
96}
97#else
98inline float hypot(float x, float y) {
99 return hypot((double)x, (double)y);
100}
101#endif
102
103// TODO: this function needs to be implemented and tested. Currently just throw
104// an error.
105inline float igamma(float x, float y) {
106 throw std::runtime_error("igamma is not implemented on older Android");
107}
108inline double igamma(double x, double y) {
109 throw std::runtime_error("igamma is not implemented on older Android");
110}
111inline float igammac(float x, float y) {
112 throw std::runtime_error("igammac is not implemented on older Android");
113}
114inline double igammac(double x, double y) {
115 throw std::runtime_error("igammac is not implemented on older Android");
116}
117
118// Note: std::signbit returns true for negative zero (-0), but this
119// implementation returns false.
120inline bool signbit(float x) {
121 return x < 0;
122}
123inline bool signbit(double x) {
124 return x < 0;
125}
126inline bool signbit(long double x) {
127 return x < 0;
128}
129
130#if !defined(__NEWLIB__)
131// TODO: this function needs to be implemented and tested. Currently just throw
132// an error.
133inline float nextafter(float x, float y) {
134 throw std::runtime_error(
135 "std::nextafter is not implemented on older Android");
136}
137inline double nextafter(double x, double y) {
138 throw std::runtime_error(
139 "std::nextafter is not implemented on older Android");
140}
141#else
142inline float nextafter(float x, float y) {
143 return nextafter((double)x, (double)y);
144}
145#endif
146
147#if !defined(__NEWLIB__)
148// TODO: this function needs to be implemented and tested. Currently just throw
149// an error.
150inline float exp2(float x) {
151 throw std::runtime_error("std::exp2 is not implemented on older Android");
152}
153inline double exp2(double x) {
154 throw std::runtime_error("std::exp2 is not implemented on older Android");
155}
156#else
157inline float exp2(float x) {
158 return exp2((double)x);
159}
160#endif
161
162// Define integral versions the same way as more recent libstdc++
163template <typename T>
164typename std::enable_if<std::is_integral<T>::value, double>::type acosh(T x) {
165 return __builtin_acosh(x);
166}
167template <typename T>
168typename std::enable_if<std::is_integral<T>::value, double>::type asinh(T x) {
169 return __builtin_asinh(x);
170}
171template <typename T>
172typename std::enable_if<std::is_integral<T>::value, double>::type atanh(T x) {
173 return __builtin_atanh(x);
174}
175template <typename T>
176typename std::enable_if<std::is_integral<T>::value, double>::type erf(T x) {
177 return __builtin_erf(x);
178}
179template <typename T>
180typename std::enable_if<std::is_integral<T>::value, double>::type erfc(T x) {
181 return __builtin_erfc(x);
182}
183template <typename T>
184typename std::enable_if<std::is_integral<T>::value, double>::type expm1(T x) {
185 return __builtin_expm1(x);
186}
187template <typename T>
188typename std::enable_if<std::is_integral<T>::value, double>::type lgamma(T x) {
189 return __builtin_lgamma(x);
190}
191template <typename T>
192typename std::enable_if<std::is_integral<T>::value, double>::type log1p(T x) {
193 return __builtin_log1p(x);
194}
195template <typename T>
196typename std::enable_if<std::is_integral<T>::value, double>::type nearbyint(
197 T x) {
198 return __builtin_nearbyint(x);
199}
200template <typename T>
201typename std::enable_if<std::is_integral<T>::value, double>::type round(T x) {
202 return __builtin_round(x);
203}
204template <typename T>
205typename std::enable_if<std::is_integral<T>::value, double>::type tgamma(T x) {
206 return __builtin_tgamma(x);
207}
208template <typename T>
209typename std::enable_if<std::is_integral<T>::value, double>::type trunc(T x) {
210 return __builtin_trunc(x);
211}
212
213// Convoluted definition of these binary functions for overloads other than
214// (float,float) and (double,double). Using a template from __gnu_cxx
215// is dirty, but this code is only enabled on a dead platform, so there
216// shouldn't be any risk of it breaking due to updates.
217template <typename T, typename U>
218typename __gnu_cxx::__promote_2<T, U>::__type fmax(T x, U y) {
219 typedef typename __gnu_cxx::__promote_2<T, U>::__type type;
220 return fmax(type(x), type(y));
221}
222template <typename T, typename U>
223typename __gnu_cxx::__promote_2<T, U>::__type fmin(T x, U y) {
224 typedef typename __gnu_cxx::__promote_2<T, U>::__type type;
225 return fmin(type(x), type(y));
226}
227template <typename T, typename U>
228typename __gnu_cxx::__promote_2<T, U>::__type copysign(T x, U y) {
229 typedef typename __gnu_cxx::__promote_2<T, U>::__type type;
230 return copysign(type(x), type(y));
231}
232template <typename T, typename U>
233typename __gnu_cxx::__promote_2<T, U>::__type remainder(T x, U y) {
234 typedef typename __gnu_cxx::__promote_2<T, U>::__type type;
235 return remainder(type(x), type(y));
236}
237
238// log2 is a macro on Android API < 21, so we need to define it ourselves.
239inline float log2(float arg) {
240 return ::log(arg) / ::log(2.0);
241}
242#if !defined(__NEWLIB__)
243inline double log2(double arg) {
244 return ::log(arg) / ::log(2.0);
245}
246#endif
247inline long double log2(long double arg) {
248 return ::log(arg) / ::log(2.0);
249}
250template <typename T>
251typename std::enable_if<std::is_integral<T>::value, double>::type log2(T x) {
252 return ::log(x) / ::log(2.0);
253}
254} // namespace std
255
256#endif
257