66#include < algorithm>
77#include < limits>
88#include < numeric>
9+ #include < optional>
910
1011#include " array.hpp"
1112extern " C" {
@@ -14,11 +15,12 @@ extern "C" {
1415
1516#define CVCASE (a, b ) (((a) << 3 ) + (b)) // The main cases fit in low 8 bits of mask
1617
17- // FIEQ are used in bcvt, where FUZZ may be set to 0 to ensure only exact values are demoted to lower precision
18- #define FIEQ (u, v, fuzz ) \
19- (ABS((u) - (v)) <= \
20- fuzz * \
21- ABS (v)) // used when v is known to be exact integer. It's close enough, maybe ULP too small on the high end
18+ // fuzzy_equal() is used in bcvt, where FUZZ may be set to 0 to ensure only exact values are demoted to lower precision
19+ // used when v is known to be exact integer. It's close enough, maybe ULP too small on the high end
20+ [[nodiscard]] static auto
21+ fuzzy_equal (double u, double v, double fuzz) -> bool {
22+ return std::abs (u - v) <= fuzz * std::abs (v);
23+ }
2224
2325template <typename T, typename V>
2426[[nodiscard]] constexpr auto
@@ -32,88 +34,84 @@ in_range() -> bool {
3234 return in_range<T>(std::numeric_limits<V>::min ()) && in_range<T>(std::numeric_limits<V>::max ());
3335}
3436
35- template <typename From, typename To>
37+ template <typename T>
38+ struct is_optional : std::false_type {};
39+
40+ template <typename T>
41+ struct is_optional <std::optional<T>> : std::true_type {};
42+
43+ template <typename T>
3644[[nodiscard]] auto
37- convert (J jt, array w, void *yv) -> bool {
45+ value_if (bool cond, T value) -> std::optional<T> {
46+ return cond ? std::optional (value) : std::nullopt ;
47+ }
48+
49+ template <typename From, typename To, typename Transform>
50+ [[nodiscard]] auto
51+ convert (J jt, array w, void *yv, Transform t) -> bool {
3852 auto *v = pointer_to_values<From>(w);
39- if constexpr (!in_range<To, From>()) {
40- // TODO: replace with short circuiting solution
41- auto *out = static_cast <To *>(yv);
42- return out + AN (w) == std::copy_if (v, v + AN (w), out, [](auto v) { return in_range<To>(v); });
53+ auto *result = static_cast <To *>(yv);
54+ if constexpr (is_optional<decltype (t (*v))>::value) {
55+ for (int64_t i = 0 ; i < AN (w); ++i) {
56+ auto opt = t (v[i]);
57+ if (!opt) return false ;
58+ result[i] = opt.value ();
59+ }
60+ } else {
61+ std::transform (v, v + AN (w), result, t);
4362 }
44- std::copy (v, v + AN (w), static_cast <To *>(yv));
4563 return true ;
4664}
4765
48- template <typename From, typename To, typename Transform >
66+ template <typename From, typename To>
4967[[nodiscard]] auto
50- convert (J jt, array w, void *yv, Transform t) -> bool {
68+ convert (J jt, array w, void *yv) -> bool {
69+ if constexpr (!in_range<To, From>()) {
70+ return convert<From, To>(jt, w, yv, [](auto v) { return value_if (in_range<To>(v), v); });
71+ }
5172 auto *v = pointer_to_values<From>(w);
52- std::transform (v, v + AN (w), static_cast <To *>(yv), t );
73+ std::copy (v, v + AN (w), static_cast <To *>(yv));
5374 return true ;
5475}
5576
5677template <>
5778[[nodiscard]] auto
5879convert<D, bool >(J jt, array w, void *yv, D fuzz) -> bool {
59- auto n = AN (w);
60- auto *v = pointer_to_values<double >(w);
61- auto *x = static_cast <B *>(yv);
62- DQ (n, auto p = *v++; if (p < -2 || 2 < p) return false ; // handle infinities
63- I val = 2 ;
64- val = (p == 0 ) ? 0 : val;
65- val = FIEQ (p, 1.0 , fuzz) ? 1 : val;
66- if (val == 2 ) return false ;
67- *x++ = (B)val;)
68- return true ;
80+ auto const infinity = [](auto p) { return p < -2 || 2 < p; };
81+ return convert<D, bool >(jt, w, yv, [&](auto p) {
82+ return value_if (!infinity (p) && (p == 0.0 || fuzzy_equal (p, 1.0 , fuzz)), p != 0.0 );
83+ });
6984}
7085
7186template <>
7287[[nodiscard]] auto
7388convert<D, I>(J jt, array w, void *yv, D fuzz) -> bool {
74- auto n = AN (w);
75- auto *v = pointer_to_values<double >(w);
76- auto *x = static_cast <I *>(yv);
77- for (int64_t i = 0 ; i < n; ++i) {
78- auto const p = v[i];
89+ return convert<D, I>(jt, w, yv, [&](auto p) -> std::optional<I> {
7990 auto const q = jround (p);
80- I rq = static_cast <I>(q);
81- if (!(p == q || FIEQ (p, q, fuzz))) {
82- return 0 ; // must equal int, possibly out of range
91+ if (!(p == q || fuzzy_equal (p, q, fuzz))) {
92+ return std::nullopt ; // must equal int, possibly out of range
8393 }
8494 // out-of-range values don't convert, handle separately
8595 if (p < static_cast <D> IMIN) {
86- if (!(p >= IMIN * (1 + fuzz))) return false ;
87- rq = IMIN;
96+ return value_if (p >= IMIN * (1 + fuzz), IMIN);
8897 } // if tolerantly < IMIN, error; else take IMIN
8998 else if (p >= FLIMAX) {
90- if (!(p <= -static_cast <D> IMIN * (1 + fuzz))) return false ;
91- rq = IMAX;
99+ return value_if (p <= -static_cast <D> IMIN * (1 + fuzz), IMAX);
92100 } // if tolerantly > IMAX, error; else take IMAX
93- *x++ = rq;
94- }
95- return true ;
101+ return q;
102+ });
96103}
97104
98105template <>
99106[[nodiscard]] auto
100107convert<Z, D>(J jt, array w, void *yv, D fuzz) -> bool {
101- auto const n = AN (w);
102- auto const *v = pointer_to_values<Z>(w);
103- auto *x = static_cast <D *>(yv);
104- if (fuzz != 0.0 )
105- DQ (
106- n, auto d = std::abs (v->im ); if (d != inf && d <= fuzz * std::abs (v->re )) {
107- *x++ = v->re ;
108- v++;
109- } else return false ;)
110- else
111- DQ (
112- n, if (!v->im ) {
113- *x++ = v->re ;
114- v++;
115- } else return false ;);
116- return true ;
108+ if (fuzz != 0.0 ) {
109+ return convert<Z, D>(jt, w, yv, [&](auto const &v) {
110+ auto const d = std::abs (v.im );
111+ return value_if (d != inf && d <= fuzz * std::abs (v.re ), v.re );
112+ });
113+ }
114+ return convert<Z, D>(jt, w, yv, [](auto v) { return value_if (!v.im , v.re ); });
117115}
118116
119117template <>
@@ -206,10 +204,10 @@ convert<D, X>(J jt, array w, void *yv, I mode) -> bool {
206204template <>
207205[[nodiscard]] auto
208206convert<X, bool >(J jt, array w, void *yv) -> bool {
209- auto *v = pointer_to_values<X>(w);
210- auto *x = static_cast <B *>(yv) ;
211- DO ( AN (w), array q = v[i]; I e = pointer_to_values< int64_t >(q)[ 0 ]; if ((AN (q) ^ 1 ) | (e & -2 )) return false ; x[i] = (B)e; );
212- return true ;
207+ return convert<X, bool >(jt, w, yv, []( auto q) {
208+ auto const e = pointer_to_values< int64_t >(q)[ 0 ] ;
209+ return value_if (! ((AN (q) ^ 1 ) | (e & -2 )), e );
210+ }) ;
213211}
214212
215213template <typename T>
@@ -316,7 +314,10 @@ convert<Q, D>(J jt, array w, void *yv) -> bool {
316314 auto const add_digits = [&](auto n, auto v) {
317315 auto f = 1.0 ;
318316 auto d = 0.0 ;
319- DO (n, d += f * v[i]; f *= xb;);
317+ std::for_each (v, v + n, [&](auto i) {
318+ d += f * i;
319+ f *= xb;
320+ });
320321 return d;
321322 };
322323
@@ -357,10 +358,7 @@ convert<Q, D>(J jt, array w, void *yv) -> bool {
357358template <>
358359[[nodiscard]] auto
359360convert<Q, X>(J jt, array w, void *yv) -> bool {
360- auto *v = pointer_to_values<Q>(w);
361- auto *x = static_cast <X *>(yv);
362- DQ (AN (w), if (!(jtequ (jt, iv1, v->d ))) return 0 ; *x++ = v->n ; ++v;);
363- return !jt->jerr ;
361+ return convert<Q, X>(jt, w, yv, [&](auto v) { return value_if (jtequ (jt, iv1, v.d ), v.n ); }) && !jt->jerr ;
364362}
365363
366364template <typename T>
@@ -641,14 +639,17 @@ jtxco1(J jt, array w) -> array {
641639auto
642640jtxco2 (J jt, array a, array w) -> array {
643641 ASSERT (AT (w) & DENSE, EVNONCE);
644- I j = 0 ;
645- RE (j = jti0 (jt, a)) ;
642+ I j = jti0 (jt, a) ;
643+ if (jt-> jerr != 0 ) return nullptr ;
646644 switch (j) {
647645 case -2 : return jtaslash1 (jt, CDIV, w);
648646 case -1 : return jtbcvt (jt, 1 , w);
649647 case 1 : return jtxco1 (jt, w);
650648 case 2 :
651- if ((AT (w) & RAT) == 0 ) RZ (w = jtcvt (jt, RAT, w));
649+ if ((AT (w) & RAT) == 0 ) {
650+ w = jtcvt (jt, RAT, w);
651+ if (!w) return nullptr ;
652+ }
652653 {
653654 auto const n = AN (w);
654655 auto const r = AR (w);
0 commit comments