@@ -59,6 +59,7 @@ typedef __m128d bruun_v2;
5959# define V2_ADD (a, b ) _mm_add_pd((a), (b))
6060# define V2_SUB (a, b ) _mm_sub_pd((a), (b))
6161# define V2_MUL (a, b ) _mm_mul_pd((a), (b))
62+ # define V2_DIV (a, b ) _mm_div_pd((a), (b))
6263# define V2_MADD (a, b, c ) V2_ADD ((a), V2_MUL ((b), (c)))
6364# define V2_MSUB (a, b, c ) V2_SUB ((a), V2_MUL ((b), (c)))
6465# define V2_SET1 (x ) _mm_set1_pd(x)
@@ -68,13 +69,16 @@ typedef __m128d bruun_v2;
6869# define V2_DUP0 (a ) _mm_unpacklo_pd((a), (a))
6970# define V2_DUP1 (a ) _mm_unpackhi_pd((a), (a))
7071# define V2_NEGHI (a ) _mm_xor_pd((a), _mm_set_pd(-0.0 , 0.0 ))
72+ # define V2_CMPGT (a, b ) _mm_cmpgt_pd((a), (b))
73+ # define V2_SELECT (m, t, f ) _mm_or_pd(_mm_and_pd((m), (t)), _mm_andnot_pd((m), (f)))
7174#elif defined(BRUUN_NEON_128)
7275typedef float64x2_t bruun_v2;
7376# define V2_LD (p ) vld1q_f64(p)
7477# define V2_ST (p, a ) vst1q_f64((p), (a))
7578# define V2_ADD (a, b ) vaddq_f64((a), (b))
7679# define V2_SUB (a, b ) vsubq_f64((a), (b))
7780# define V2_MUL (a, b ) vmulq_f64((a), (b))
81+ # define V2_DIV (a, b ) vdivq_f64((a), (b))
7882# define V2_MADD (a, b, c ) vfmaq_f64((a), (b), (c))
7983# define V2_MSUB (a, b, c ) vfmsq_f64((a), (b), (c))
8084# define V2_SET1 (x ) vdupq_n_f64(x)
@@ -88,6 +92,14 @@ static inline float64x2_t bruun_neghi(float64x2_t a) {
8892 return vreinterpretq_f64_u64 (veorq_u64 (vreinterpretq_u64_f64 (a), m));
8993}
9094# define V2_NEGHI (a ) bruun_neghi(a)
95+ static inline float64x2_t bruun_v2_cmpgt (float64x2_t a, float64x2_t b) {
96+ return vreinterpretq_f64_u64 (vcgtq_f64 (a, b));
97+ }
98+ static inline float64x2_t bruun_v2_select (float64x2_t m, float64x2_t t, float64x2_t f) {
99+ return vbslq_f64 (vreinterpretq_u64_f64 (m), t, f);
100+ }
101+ # define V2_CMPGT (a, b ) bruun_v2_cmpgt((a), (b))
102+ # define V2_SELECT (m, t, f ) bruun_v2_select((m), (t), (f))
91103#endif
92104
93105// 4-lane float vector primitive for internal float32 helper loops.
@@ -151,6 +163,12 @@ namespace bruun {
151163#define BRUUN_ASSUME_ALIGNED (ptr, bytes ) (ptr)
152164#endif
153165
166+ #if defined(__GNUC__) || defined(__clang__)
167+ #define BRUUN_ALWAYS_INLINE inline __attribute__ ((always_inline))
168+ #else
169+ #define BRUUN_ALWAYS_INLINE inline
170+ #endif
171+
154172static constexpr std::size_t bruun_cache_alignment = 64 ;
155173
156174template <typename T>
@@ -463,6 +481,11 @@ static inline double bruun_atan_leaf_odd(double h) {
463481 return h * p;
464482}
465483
484+ static BRUUN_ALWAYS_INLINE double bruun_atan_leaf_cubic (double h) {
485+ const double s = h * h;
486+ return h * (1.0 - s * (1.0 / 3.0 ));
487+ }
488+
466489static inline int bruun_phase_child_index (int idx, int base, double major, double minor) {
467490 const bruun_phase_node node = bruun_phase_nodes[base + idx];
468491 const double side = bruun_fused_msub (minor, node.c , major, node.s );
@@ -473,7 +496,7 @@ static inline int bruun_phase_child_index(int idx, int base, double major, doubl
473496 return idx;
474497}
475498
476- static inline double bruun_phase_first_octant (double major, double minor, double mag) {
499+ static inline double bruun_phase_first_octant_tree32_degree7 (double major, double minor, double mag) {
477500 int idx = 0 ;
478501 idx = bruun_phase_child_index (idx, 0 , major, minor);
479502 idx = bruun_phase_child_index (idx, 1 , major, minor);
@@ -489,7 +512,74 @@ static inline double bruun_phase_first_octant(double major, double minor, double
489512 return leaf.center + delta;
490513}
491514
492- static inline double bruun_phase_atan2_mag (double y, double x, double mag) {
515+ static BRUUN_ALWAYS_INLINE double bruun_phase_first_octant_vec5_cubic (double major, double minor, double mag) {
516+ double theta = M_PI / 8.0 ;
517+ double c = 0.92387953251128674 ;
518+ double s = 0.38268343236508978 ;
519+
520+ const double side0 = bruun_fused_msub (minor, c, major, s);
521+ const double negative0 = static_cast <double >(side0 <= 0.0 );
522+ const double sign0 = 1.0 - 2.0 * negative0;
523+ theta = bruun_fused_madd (sign0, 0.19634954084936207 , theta);
524+ const double signed_sstep0 = sign0 * 0.19509032201612825 ;
525+ const double nc0 = bruun_fused_madd (-signed_sstep0, s, c * 0.98078528040323043 );
526+ const double ns0 = bruun_fused_madd (signed_sstep0, c, s * 0.98078528040323043 );
527+ c = nc0;
528+ s = ns0;
529+
530+ const double side1 = bruun_fused_msub (minor, c, major, s);
531+ const double negative1 = static_cast <double >(side1 <= 0.0 );
532+ const double sign1 = 1.0 - 2.0 * negative1;
533+ theta = bruun_fused_madd (sign1, 0.09817477042468103 , theta);
534+ const double signed_sstep1 = sign1 * 0.098017140329560604 ;
535+ const double nc1 = bruun_fused_madd (-signed_sstep1, s, c * 0.99518472667219693 );
536+ const double ns1 = bruun_fused_madd (signed_sstep1, c, s * 0.99518472667219693 );
537+ c = nc1;
538+ s = ns1;
539+
540+ const double side2 = bruun_fused_msub (minor, c, major, s);
541+ const double negative2 = static_cast <double >(side2 <= 0.0 );
542+ const double sign2 = 1.0 - 2.0 * negative2;
543+ theta = bruun_fused_madd (sign2, 0.04908738521234052 , theta);
544+ const double signed_sstep2 = sign2 * 0.049067674327418015 ;
545+ const double nc2 = bruun_fused_madd (-signed_sstep2, s, c * 0.99879545620517241 );
546+ const double ns2 = bruun_fused_madd (signed_sstep2, c, s * 0.99879545620517241 );
547+ c = nc2;
548+ s = ns2;
549+
550+ const double side3 = bruun_fused_msub (minor, c, major, s);
551+ const double negative3 = static_cast <double >(side3 <= 0.0 );
552+ const double sign3 = 1.0 - 2.0 * negative3;
553+ theta = bruun_fused_madd (sign3, 0.02454369260617026 , theta);
554+ const double signed_sstep3 = sign3 * 0.024541228522912288 ;
555+ const double nc3 = bruun_fused_madd (-signed_sstep3, s, c * 0.99969881869620425 );
556+ const double ns3 = bruun_fused_madd (signed_sstep3, c, s * 0.99969881869620425 );
557+ c = nc3;
558+ s = ns3;
559+
560+ const double side4 = bruun_fused_msub (minor, c, major, s);
561+ const double negative4 = static_cast <double >(side4 <= 0.0 );
562+ const double sign4 = 1.0 - 2.0 * negative4;
563+ theta = bruun_fused_madd (sign4, 0.01227184630308513 , theta);
564+ const double signed_sstep4 = sign4 * 0.012271538285719925 ;
565+ const double nc4 = bruun_fused_madd (-signed_sstep4, s, c * 0.9999247018391445 );
566+ const double ns4 = bruun_fused_madd (signed_sstep4, c, s * 0.9999247018391445 );
567+ c = nc4;
568+ s = ns4;
569+
570+ const double dot = bruun_fused_madd (major, c, minor * s);
571+ const double cross = bruun_fused_msub (minor, c, major, s);
572+ const double h = cross / (mag + dot);
573+ const double delta = 2.0 * bruun_atan_leaf_cubic (h);
574+ return theta + delta;
575+ }
576+
577+ static BRUUN_ALWAYS_INLINE double bruun_phase_first_octant (double major, double minor, double mag) {
578+ return bruun_phase_first_octant_vec5_cubic (major, minor, mag);
579+ }
580+
581+ template <class FirstOctant >
582+ static inline double bruun_phase_atan2_core (double y, double x, double mag, FirstOctant first_octant) {
493583 const double ax = std::fabs (x);
494584 const double ay = std::fabs (y);
495585 if (ay == 0.0 ) {
@@ -510,9 +600,9 @@ static inline double bruun_phase_atan2_mag(double y, double x, double mag) {
510600
511601 double alpha = 0.0 ;
512602 if (ax >= ay) {
513- alpha = bruun_phase_first_octant (ax, ay, mag);
603+ alpha = first_octant (ax, ay, mag);
514604 } else {
515- alpha = bruun_pio2 - bruun_phase_first_octant (ay, ax, mag);
605+ alpha = bruun_pio2 - first_octant (ay, ax, mag);
516606 }
517607
518608 if (x < 0.0 ) {
@@ -527,6 +617,121 @@ static inline double bruun_phase_atan2_mag(double y, double x, double mag) {
527617 return alpha;
528618}
529619
620+
621+ #if BRUUN_LEVEL >= 1
622+ static BRUUN_ALWAYS_INLINE bruun_v2 bruun_phase_first_octant_vec5_cubic_v2 (bruun_v2 major,
623+ bruun_v2 minor,
624+ bruun_v2 mag) {
625+ bruun_v2 theta = V2_SET1 (M_PI / 8.0 );
626+ bruun_v2 c = V2_SET1 (0.92387953251128674 );
627+ bruun_v2 s = V2_SET1 (0.38268343236508978 );
628+ const bruun_v2 one = V2_SET1 (1.0 );
629+ const bruun_v2 neg_one = V2_SET1 (-1.0 );
630+
631+ #define BRUUN_VEC5_V2_STEP (STEP, CSTEP, SSTEP ) \
632+ do { \
633+ const bruun_v2 side = V2_MSUB (V2_MUL (minor, c), major, s); \
634+ const bruun_v2 sign = V2_SELECT (V2_CMPGT (side, V2_SET1 (0.0 )), one, neg_one); \
635+ theta = V2_ADD (theta, V2_MUL (sign, V2_SET1 (STEP ))); \
636+ const bruun_v2 signed_sstep = V2_MUL (sign, V2_SET1 (SSTEP )); \
637+ const bruun_v2 nc = V2_SUB (V2_MUL (c, V2_SET1 (CSTEP )), V2_MUL (s, signed_sstep)); \
638+ const bruun_v2 ns = V2_ADD (V2_MUL (c, signed_sstep), V2_MUL (s, V2_SET1 (CSTEP ))); \
639+ c = nc; \
640+ s = ns; \
641+ } while (false )
642+
643+ BRUUN_VEC5_V2_STEP (0.19634954084936207 , 0.98078528040323043 , 0.19509032201612825 );
644+ BRUUN_VEC5_V2_STEP (0.09817477042468103 , 0.99518472667219693 , 0.098017140329560604 );
645+ BRUUN_VEC5_V2_STEP (0.04908738521234052 , 0.99879545620517241 , 0.049067674327418015 );
646+ BRUUN_VEC5_V2_STEP (0.02454369260617026 , 0.99969881869620425 , 0.024541228522912288 );
647+ BRUUN_VEC5_V2_STEP (0.01227184630308513 , 0.9999247018391445 , 0.012271538285719925 );
648+ #undef BRUUN_VEC5_V2_STEP
649+
650+ const bruun_v2 dot = V2_ADD (V2_MUL (major, c), V2_MUL (minor, s));
651+ const bruun_v2 cross = V2_SUB (V2_MUL (minor, c), V2_MUL (major, s));
652+ const bruun_v2 h = V2_DIV (cross, V2_ADD (mag, dot));
653+ const bruun_v2 h2 = V2_MUL (h, h);
654+ const bruun_v2 cubic = V2_MUL (h, V2_SUB (one, V2_MUL (h2, V2_SET1 (1.0 / 3.0 ))));
655+ return V2_ADD (theta, V2_MUL (V2_SET1 (2.0 ), cubic));
656+ }
657+ #endif
658+
659+
660+ #if BRUUN_LEVEL >= 1
661+ static BRUUN_ALWAYS_INLINE void bruun_phase_atan2_mag_pair (double y0, double x0, double mag0,
662+ double y1, double x1, double mag1,
663+ double * out0, double * out1) {
664+ const double ax0 = std::fabs (x0);
665+ const double ay0 = std::fabs (y0);
666+ const double ax1 = std::fabs (x1);
667+ const double ay1 = std::fabs (y1);
668+ if (ax0 == 0.0 || ay0 == 0.0 || ax1 == 0.0 || ay1 == 0.0 ) {
669+ *out0 = bruun_phase_atan2_core (y0, x0, mag0, bruun_phase_first_octant);
670+ *out1 = bruun_phase_atan2_core (y1, x1, mag1, bruun_phase_first_octant);
671+ return ;
672+ }
673+
674+ double major0 = ax0;
675+ double minor0 = ay0;
676+ bool swap0 = false ;
677+ if (ay0 > ax0) {
678+ major0 = ay0;
679+ minor0 = ax0;
680+ swap0 = true ;
681+ }
682+
683+ double major1 = ax1;
684+ double minor1 = ay1;
685+ bool swap1 = false ;
686+ if (ay1 > ax1) {
687+ major1 = ay1;
688+ minor1 = ax1;
689+ swap1 = true ;
690+ }
691+
692+ const bruun_v2 major = V2_SETLH (major0, major1);
693+ const bruun_v2 minor = V2_SETLH (minor0, minor1);
694+ const bruun_v2 mag = V2_SETLH (mag0, mag1);
695+ double phase_pair[2 ];
696+ V2_ST (phase_pair, bruun_phase_first_octant_vec5_cubic_v2 (major, minor, mag));
697+
698+ double alpha0 = phase_pair[0 ];
699+ if (swap0) {
700+ alpha0 = bruun_pio2 - alpha0;
701+ }
702+ if (x0 < 0.0 ) {
703+ alpha0 = M_PI - alpha0;
704+ }
705+ if (y0 < 0.0 ) {
706+ alpha0 = -alpha0;
707+ }
708+ if (alpha0 < 0.0 ) {
709+ alpha0 += bruun_tau;
710+ }
711+
712+ double alpha1 = phase_pair[1 ];
713+ if (swap1) {
714+ alpha1 = bruun_pio2 - alpha1;
715+ }
716+ if (x1 < 0.0 ) {
717+ alpha1 = M_PI - alpha1;
718+ }
719+ if (y1 < 0.0 ) {
720+ alpha1 = -alpha1;
721+ }
722+ if (alpha1 < 0.0 ) {
723+ alpha1 += bruun_tau;
724+ }
725+
726+ *out0 = alpha0;
727+ *out1 = alpha1;
728+ }
729+ #endif
730+
731+ static BRUUN_ALWAYS_INLINE double bruun_phase_atan2_mag (double y, double x, double mag) {
732+ return bruun_phase_atan2_core (y, x, mag, bruun_phase_first_octant);
733+ }
734+
530735static inline float bruun_phase_atan2_mag_f32 (float y, float x, float mag) {
531736 const double phase = bruun_phase_atan2_mag (static_cast <double >(y),
532737 static_cast <double >(x),
@@ -2531,7 +2736,27 @@ class RFFT {
25312736 }
25322737
25332738 const int * RESTRICT kin = KINV .data ();
2534- for (int k = 1 ; k < N / 2 ; ++k) {
2739+ int k = 1 ;
2740+ #if BRUUN_LEVEL >= 1
2741+ for (; k + 1 < N / 2 ; k += 2 ) {
2742+ const int m0 = kin[k];
2743+ const int m1 = kin[k + 1 ];
2744+ const double re0 = work[2 *m0];
2745+ const double im0 = -work[2 *m0 + 1 ];
2746+ const double re1 = work[2 *m1];
2747+ const double im1 = -work[2 *m1 + 1 ];
2748+ const double mag0 = std::sqrt (re0 * re0 + im0 * im0);
2749+ const double mag1 = std::sqrt (re1 * re1 + im1 * im1);
2750+ double phase0;
2751+ double phase1;
2752+ bruun_phase_atan2_mag_pair (im0, re0, mag0, im1, re1, mag1, &phase0, &phase1);
2753+ output[k].re = mag0;
2754+ output[k].im = phase0;
2755+ output[k + 1 ].re = mag1;
2756+ output[k + 1 ].im = phase1;
2757+ }
2758+ #endif
2759+ for (; k < N / 2 ; ++k) {
25352760 const int m = kin[k];
25362761 const double re = work[2 *m];
25372762 const double im = -work[2 *m + 1 ];
0 commit comments