/*
References:
- saffah_codex_6a_agg3, https://duck.ac/submission/121443 : reused both
global level-0 Montgomery passes and the validated reduction helper.
- saffah_cc_v41_agg1, https://duck.ac/submission/111674 : retained the
mixed-radix engine, radix-five transforms and original notices below.
Idea:
Also store the two radix-five geometric root tables in Montgomery form.
Build w^2, w^3 and w^4 with Montgomery products, retaining the same form,
then multiply ordinary butterfly data by these roots directly. This removes
all transient Shoup reciprocal construction for those roots and powers,
without adding root tables or changing normal-form data and DFT constants.
Scalar boundary paths decode the root once before their existing arithmetic.
Purpose: Test the repeated radix-five twiddle calculations after the largest
binary forward/inverse layers benefited from the same representation.
*/
/*
References:
- saffah_codex_6a_agg3, https://duck.ac/submission/121420 : reused the
validated inverse global level-0 Montgomery roots and reduction.
- saffah_cc_v41_agg1, https://duck.ac/submission/111674 : inherited mixed
radix transforms and forward radix-16 blocking, with notices below.
Idea:
Extend the prepared Montgomery root representation to forward global
level 0. Its fused radix-16 first pass now multiplies ordinary input data
by Montgomery roots directly, removing root-Shoup derivation for both
input transforms. The inverse optimization is retained. All level-1 and
smaller tables and butterfly constants keep their original representation.
Purpose: Test the same representation saving on the twice-executed forward
path after its independently validated inverse counterpart improved online.
*/
/*
References:
- saffah_codex_6a_agg3, https://duck.ac/submission/120684 : reused its complete
mixed-radix NTT and prepared inverse level-1 Shoup constants.
- saffah_cc_v41_agg1, https://duck.ac/submission/121257 : reused the split
even/odd 64-bit AVX2 Montgomery reduction formula from its corner kernel.
- Original NTT attribution and notices are retained below.
Idea:
Store only inverse global level-0 roots in Montgomery form, generated
straight from a Montgomery unit seed. The final fused inverse stage can
then multiply ordinary data by those roots with one Montgomery reduction,
without deriving a Shoup constant for each loaded root. All other root
tables and the data itself stay in their original representation.
The geometric generator's block step is explicitly r^32 when its seed is
not one; this is required for a Montgomery seed to remain uniformly scaled.
Purpose: Test removal of repeated root-reciprocal work in the largest
inverse stage; validate all coefficients for three full-size inputs.
*/
/*
References:
- saffah_cc_v41_agg1, https://duck.ac/submission/111674: copied the NTT,
padded tables and fused inverse radix-16 stage. Full attribution follows.
- saffah_codex_6a_agg3, https://duck.ac/submission/120621: reused only its
independently verified final partial-vector output bounds correction.
Idea:
- Generate Shoup constants for inverse global level 1 once alongside its
existing geometric twiddle table. The fused inverse radix-16 stage then
loads those constants, avoiding their repeated derivation in four row
groups for each of five transforms. The reserved table storage already
exists and does not overlap the radix-5 twiddle values.
Purpose:
- Test whether amortizing these invariant computations improves runtime.
Scheduling and all other arithmetic remain the reference version. No new
assembly or system calls are added.
*/
// ===== REFERENCES =====
// [1] duck.ac 用户 saffah_cc_v41_agg1(本账号),提交 #111320 = 334.963655 ms
// <https://duck.ac/submission/111320>(本账号席位 e7y_ 第 1 棒,Accepted)——
// **本文件正文 = 该提交的逐字节副本**(仅去除注释行 + 本次单变量改动,见 §思路)。
// 其自带引用链(原样继承并在此逐项署名):
// · [1a] saffah_codex_6s_agg2 #110445 <https://duck.ac/submission/110445>(对手 T 件,337.150416 ms)
// 及其上游 #110438 <https://duck.ac/submission/110438>(前向 radix-16 通路 512 列重分块)
// · [1b] saffah_cc_v41_agg1 #107861 <https://duck.ac/submission/107861>(341.830352 ms)
// 与 #107753 <https://duck.ac/submission/107753>(344.421404 ms)
// · [1c] saffah_codex_6s_agg2 #107083 <https://duck.ac/submission/107083>(347.620902 ms,
// NTT 引擎本体:radix-5 分裂、AVX2 Shoup 蝶形、padpos 冲突打散)
// 许可合规:以上均为 duck.ac 公开提交,正文按站点规则公开可见;原件未附独立许可声明;
// 本文件按 RULES §3 逐项署名作者账号与原提交地址。
//
/*
References:
- duck.ac user saffah_codex_6s_agg2, https://duck.ac/submission/110438:
copied the complete NTT engine and forward tile-512 implementation; its
inherited references and license observations are retained below.
- duck.ac user saffah_cc_v41_agg1, https://duck.ac/submission/107861:
inherited baseline implementation of the mixed-radix NTT. Its public
submission displayed no separate license notice; attribution is retained.
Approach:
- Use the modulus identity p = 1 + 15*2^27 to form the low 32 bits of q*p
as q + (q<<31) - (q<<27), eliminating a vector integer multiplication
from every Shoup modular product while preserving exact modular arithmetic.
Purpose:
- Experimentally test whether the shorter reduction dependency improves
duck.ac's official running time and verify the full-size output hash.
*/
/*
References:
- duck.ac user saffah_cc_v41_agg1, https://duck.ac/submission/107861:
complete mixed-radix NTT body copied as the baseline, including its own
references to this account's #107083 and rival #107753. The public source
had no separate license notice; its original attribution is retained.
Approach:
- Retile the fused forward radix-16 pass to 512 coefficient columns.
Purpose:
- Experimentally test whether this parameter reduces transform time on
duck.ac's official CPU while preserving identical output.
*/
#define NTT_MAXN 8500000u
#pragma GCC optimize("O3,rename-registers")
#include <immintrin.h>
#include <cstdint>
#include <cstring>
#include <cstdlib>
__attribute__((target("avx2"))) static inline __m256i ldu256(const void *p) {
__m256i r; __asm__("vmovdqu %1, %0" : "=x"(r) : "m"(*(const __m256i *)p)); return r; }
__attribute__((target("avx2"))) static inline void stu256(void *p, __m256i v) {
__asm__("vmovdqu %1, %0" : "=m"(*(__m256i *)p) : "x"(v)); }
typedef uint32_t u32;
typedef uint64_t u64;
#define PF_DIST 192
static inline void pf5(const void *p) { _mm_prefetch((const char *)p, _MM_HINT_T0); }
#define NTP 2013265921u
#define TPAD 704u
#define TOPPAD 128u
static inline u32 padpos(u32 i) { return i + TOPPAD * ((i >> 20) + (i >> 18) + (i >> 16) + (i >> 14)); }
#define PADJUMP() if (j >= nxt0) { bp += TOPPAD; nxt0 += (1u << 20); } \
if (j >= nxt1) { bp += TOPPAD; nxt1 += (1u << 18); } \
if (j >= nxt2) { bp += TOPPAD; nxt2 += (1u << 16); } \
if (j >= nxt3) { bp += TOPPAD; nxt3 += (1u << 14); }
#define PADJUMP_S() if (s >= nxt0) { bp += TOPPAD; nxt0 += (1u << 20); } \
if (s >= nxt1) { bp += TOPPAD; nxt1 += (1u << 18); } \
if (s >= nxt2) { bp += TOPPAD; nxt2 += (1u << 16); } \
if (s >= nxt3) { bp += TOPPAD; nxt3 += (1u << 14); }
#define TGT __attribute__((target("avx2")))
static u32 g_scale_digit[10];
static u32 g_scale_lo[8] __attribute__((aligned(32)));
static u32 g_scale_hi[8] __attribute__((aligned(32)));
template<bool SCALE>
TGT static inline __m256i lds256(const void *p) {
__m256i v = ldu256(p);
if constexpr (SCALE) {
__m256i lo = ldu256(g_scale_lo), hi = ldu256(g_scale_hi);
__m256i a = _mm256_permutevar8x32_epi32(lo, v);
__m256i b = _mm256_permutevar8x32_epi32(hi, v);
return _mm256_blendv_epi8(a, b, _mm256_cmpgt_epi32(v, _mm256_set1_epi32(7)));
}
return v;
}
#ifndef NTT_MAXN
#define NTT_MAXN (1u << 25)
#endif
static u32 g_n, g_Sb;
static u32 g_pinv;
static int g_log2n, g_L, g_odd, g_nb, g_BT = 13;
static u32 g_ciF, g_cisF, g_ciI, g_cisI;
static u32 g_tabF[5 * NTT_MAXN / 2 + 4096] __attribute__((aligned(64)));
#define Q_T5OFF (5 * NTT_MAXN / 2 + 4096 - 4 * (1u << 22))
#define Q_SCF (g_tabF + Q_T5OFF + (1u << 22))
#define Q_SCI (g_tabI + Q_T5OFF + (1u << 22))
static u32 g_tabI[5 * NTT_MAXN / 2 + 4096] __attribute__((aligned(64)));
struct Tabs {
const u32 *l1[40], *l2[40], *l3[40], *m1[40], *m2[40], *m3[40];
const u32 *b2, *b2s;
const u32 *h1[40], *h2[40], *h3[40], *q1[40], *q2[40], *q3[40];
};
static Tabs g_TF, g_TI;
static inline u32 mulmod(u32 a, u32 b) { return (u32)((u64)a * b % NTP); }
static u32 powmod(u32 a, u32 e) { u32 r = 1; while (e) { if (e & 1) r = mulmod(r, a); a = mulmod(a, a); e >>= 1; } return r; }
static inline u32 shoup1s(u32 a, u32 w, u32 ws) {
u32 t = (u32)((u64)a * w);
u32 q = (u32)(((u64)a * ws) >> 32);
u32 r = t - (u32)((u64)q * NTP);
return r >= NTP ? r - NTP : r;
}
static inline u32 shoup_c(u32 w) { return (u32)(((u64)w << 32) / NTP); }
TGT static inline __m256i vmulhi(__m256i a, __m256i b) {
__m256i e = _mm256_mul_epu32(a, b);
__m256i o = _mm256_mul_epu32(_mm256_srli_epi64(a, 32), _mm256_srli_epi64(b, 32));
e = _mm256_srli_epi64(e, 32);
return _mm256_blend_epi32(e, o, 0xAA);
}
TGT static inline __m256i vmont(__m256i a, __m256i b, __m256i pv) {
const __m256i pinv = _mm256_set1_epi32((int)g_pinv);
__m256i tlo = _mm256_mullo_epi32(a, b);
__m256i thi = vmulhi(a, b);
__m256i m = _mm256_mullo_epi32(tlo, pinv);
__m256i mhi = vmulhi(m, pv);
__m256i nz = _mm256_andnot_si256(_mm256_cmpeq_epi32(tlo, _mm256_setzero_si256()), _mm256_set1_epi32(1));
__m256i u = _mm256_add_epi32(_mm256_add_epi32(thi, mhi), nz);
return _mm256_min_epu32(u, _mm256_sub_epi32(u, pv));
}
TGT static inline __m256i mont_prepared(__m256i a, __m256i b, __m256i pv) {
const __m256i np = _mm256_set1_epi64x((long long)g_pinv);
const __m256i p64 = _mm256_set1_epi64x((long long)NTP);
__m256i ae = _mm256_mul_epu32(a, b);
__m256i ao = _mm256_mul_epu32(_mm256_srli_epi64(a, 32), _mm256_srli_epi64(b, 32));
__m256i me = _mm256_mul_epu32(ae, np), mo = _mm256_mul_epu32(ao, np);
ae = _mm256_add_epi64(ae, _mm256_mul_epu32(me, p64));
ao = _mm256_add_epi64(ao, _mm256_mul_epu32(mo, p64));
__m256i value = _mm256_blend_epi32(_mm256_srli_epi64(ae, 32), ao, 0xaa);
return _mm256_min_epu32(value, _mm256_sub_epi32(value, pv));
}
#define MLO_V 572662301u
TGT static inline __m256i der_ws(__m256i w) {
__m256i mlo = _mm256_set1_epi32((int)MLO_V);
return _mm256_add_epi32(_mm256_add_epi32(w, w), vmulhi(w, mlo));
}
TGT static inline __m256i vshoup(__m256i a, __m256i w, __m256i ws, __m256i pv) {
__m256i t = _mm256_mullo_epi32(a, w);
__m256i q = vmulhi(a, ws);
__m256i r = _mm256_sub_epi32(t, _mm256_add_epi32(q, _mm256_sub_epi32(_mm256_slli_epi32(q, 31), _mm256_slli_epi32(q, 27))));
return _mm256_min_epu32(r, _mm256_sub_epi32(r, pv));
}
TGT static inline __m256i vaddm(__m256i a, __m256i b, __m256i pv) {
__m256i s = _mm256_add_epi32(a, b);
return _mm256_min_epu32(s, _mm256_sub_epi32(s, pv));
}
TGT static inline __m256i vsubm(__m256i a, __m256i b, __m256i pv) {
__m256i d = _mm256_sub_epi32(a, b);
return _mm256_min_epu32(d, _mm256_add_epi32(d, pv));
}
TGT static inline __m128i vmulhi4(__m128i a, __m128i b) {
__m128i e = _mm_mul_epu32(a, b);
__m128i o = _mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32));
e = _mm_srli_epi64(e, 32);
return _mm_blend_epi32(e, o, 0xAA);
}
TGT static inline __m128i sh4(__m128i a, __m128i w, __m128i ws, __m128i pv) {
__m128i t = _mm_mullo_epi32(a, w);
__m128i q = vmulhi4(a, ws);
__m128i r = _mm_sub_epi32(t, _mm_mullo_epi32(q, pv));
return _mm_min_epu32(r, _mm_sub_epi32(r, pv));
}
TGT static inline __m128i add4(__m128i a, __m128i b, __m128i pv) {
__m128i s = _mm_add_epi32(a, b);
return _mm_min_epu32(s, _mm_sub_epi32(s, pv));
}
TGT static inline __m128i sub4(__m128i a, __m128i b, __m128i pv) {
__m128i d = _mm_sub_epi32(a, b);
return _mm_min_epu32(d, _mm_add_epi32(d, pv));
}
TGT static inline __m256i ld2(const __m128i *lo, const __m128i *hi) {
return _mm256_inserti128_si256(_mm256_castsi128_si256(_mm_loadu_si128(lo)), _mm_loadu_si128(hi), 1);
}
TGT static inline void st2(__m128i *lo, __m128i *hi, __m256i v) {
_mm_storeu_si128(lo, _mm256_castsi256_si128(v));
_mm_storeu_si128(hi, _mm256_extracti128_si256(v, 1));
}
TGT static inline __m256i bc128(__m128i v) {
return _mm256_inserti128_si256(_mm256_castsi128_si256(v), v, 1);
}
TGT static void stage0_dif_src(u32 *a, const u32 *src, u32 len, u32 h,
const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
const __m256i zero = _mm256_setzero_si256();
u32 j = 0;
u32 lim1 = len - h;
for (; j + 8 <= lim1 + 1; j += 8) {
__builtin_prefetch(src + j + 192);
__builtin_prefetch(src + j + h + 192);
__builtin_prefetch(a + j + 192);
__builtin_prefetch(a + j + h + 192);
__builtin_prefetch(a + j + 2 * h + 192);
__builtin_prefetch(a + j + 3 * h + 192);
__builtin_prefetch(t1 + j + 192);
__m256i x0 = ldu256((src + j));
__m256i x1 = ldu256((src + j + h));
__m256i u0 = vaddm(x0, x1, pv), u2 = vsubm(x0, x1, pv);
__m256i E = vshoup(x1, cI, cIs, pv);
__m256i u1 = vaddm(x0, E, pv), u3 = vsubm(x0, E, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
stu256((a + j), u0);
stu256((a + j + h), u1);
stu256((a + j + 2 * h), u2);
stu256((a + j + 3 * h), u3);
}
for (; j <= lim1 && j < h; j++) {
u32 x0 = (j <= len) ? src[j] : 0, x1 = (j + h <= len) ? src[j + h] : 0;
u32 u0 = (u32)(((u64)x0 + x1) % NTP);
u32 u2 = (u32)(((u64)x0 + NTP - x1) % NTP);
u32 E = mulmod(x1, ci);
u32 u1 = (u32)(((u64)x0 + E) % NTP), u3 = (u32)(((u64)x0 + NTP - E) % NTP);
u1 = shoup1s(u1, t1[j], shoup_c(t1[j]));
u2 = shoup1s(u2, t2[j], shoup_c(t2[j]));
u3 = shoup1s(u3, t3[j], shoup_c(t3[j]));
a[j] = u0; a[j + h] = u1; a[j + 2 * h] = u2; a[j + 3 * h] = u3;
}
for (; j + 8 <= h; j += 8) {
__m256i x0 = (j + 8 <= len + 1) ? ldu256((src + j)) : zero;
__m256i u0 = x0, u1 = x0, u2 = x0, u3 = x0;
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
stu256((a + j), u0);
stu256((a + j + h), u1);
stu256((a + j + 2 * h), u2);
stu256((a + j + 3 * h), u3);
}
for (; j < h; j++) {
u32 x0 = (j <= len) ? src[j] : 0;
u32 u1 = shoup1s(x0, t1[j], shoup_c(t1[j]));
u32 u2 = shoup1s(x0, t2[j], shoup_c(t2[j]));
u32 u3 = shoup1s(x0, t3[j], shoup_c(t3[j]));
a[j] = x0; a[j + h] = u1; a[j + 2 * h] = u2; a[j + 3 * h] = u3;
}
}
TGT static void stage4_dif_s0(u32 *a, u32 blk, u32 h, u32 s, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * s) {
u32 *p = a + i;
u32 poff = 0, nxt18 = 1u << 18, nxt16 = 1u << 16;
for (u32 cb = 0; cb < h; cb += 16384u) {
u32 *pc = p + poff;
u32 j = cb, jr = 0;
for (; j + 16 <= cb + 16384u; j += 16, jr += 16) {
__builtin_prefetch(pc + jr + 192);
__builtin_prefetch(pc + jr + s + 192);
__builtin_prefetch(pc + jr + 2 * s + 192);
__builtin_prefetch(pc + jr + 3 * s + 192);
__builtin_prefetch(t1 + j + 192);
__builtin_prefetch(t2 + j + 192);
__builtin_prefetch(t3 + j + 192);
__m256i x0 = ldu256((pc + jr));
__m256i x1 = ldu256((pc + jr + s));
__m256i x2 = ldu256((pc + jr + 2 * s));
__m256i x3 = ldu256((pc + jr + 3 * s));
__m256i y0 = ldu256((pc + jr + 8));
__m256i y1 = ldu256((pc + jr + s + 8));
__m256i y2 = ldu256((pc + jr + 2 * s + 8));
__m256i y3 = ldu256((pc + jr + 3 * s + 8));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsubm(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
__m256i v1 = vaddm(B2, F, pv), v3 = vsubm(B2, F, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
{ __m256i w = ldu256((t1 + j + 8)); v1 = vshoup(v1, w, ldu256((s1 + j + 8)), pv); }
{ __m256i w = ldu256((t2 + j + 8)); v2 = vshoup(v2, w, ldu256((s2 + j + 8)), pv); }
{ __m256i w = ldu256((t3 + j + 8)); v3 = vshoup(v3, w, ldu256((s3 + j + 8)), pv); }
stu256((pc + jr), u0);
stu256((pc + jr + s), u1);
stu256((pc + jr + 2 * s), u2);
stu256((pc + jr + 3 * s), u3);
stu256((pc + jr + 8), v0);
stu256((pc + jr + s + 8), v1);
stu256((pc + jr + 2 * s + 8), v2);
stu256((pc + jr + 3 * s + 8), v3);
}
for (; j + 8 <= cb + 16384u; j += 8, jr += 8) {
__builtin_prefetch(pc + jr + 192);
__builtin_prefetch(pc + jr + s + 192);
__builtin_prefetch(pc + jr + 2 * s + 192);
__builtin_prefetch(pc + jr + 3 * s + 192);
__m256i x0 = ldu256((pc + jr));
__m256i x1 = ldu256((pc + jr + s));
__m256i x2 = ldu256((pc + jr + 2 * s));
__m256i x3 = ldu256((pc + jr + 3 * s));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
stu256((pc + jr), u0);
stu256((pc + jr + s), u1);
stu256((pc + jr + 2 * s), u2);
stu256((pc + jr + 3 * s), u3);
}
poff += 16384u + TOPPAD;
if (cb + 16384u >= nxt18) { poff += TOPPAD; nxt18 += (1u << 18); }
if (cb + 16384u >= nxt16) { poff += TOPPAD; nxt16 += (1u << 16); }
}
}
}
TGT static void stage4_dif_s1(u32 *a, u32 blk, u32 h, u32 s, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * s) {
u32 *p = a + i;
u32 poff = 0, nxt16 = 1u << 16;
for (u32 cb = 0; cb < h; cb += 16384u) {
u32 *pc = p + poff;
u32 j = cb, jr = 0;
for (; j + 16 <= cb + 16384u; j += 16, jr += 16) {
__builtin_prefetch(pc + jr + 192);
__builtin_prefetch(pc + jr + s + 192);
__builtin_prefetch(pc + jr + 2 * s + 192);
__builtin_prefetch(pc + jr + 3 * s + 192);
__builtin_prefetch(t1 + j + 192);
__builtin_prefetch(t2 + j + 192);
__builtin_prefetch(t3 + j + 192);
__m256i x0 = ldu256((pc + jr));
__m256i x1 = ldu256((pc + jr + s));
__m256i x2 = ldu256((pc + jr + 2 * s));
__m256i x3 = ldu256((pc + jr + 3 * s));
__m256i y0 = ldu256((pc + jr + 8));
__m256i y1 = ldu256((pc + jr + s + 8));
__m256i y2 = ldu256((pc + jr + 2 * s + 8));
__m256i y3 = ldu256((pc + jr + 3 * s + 8));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsubm(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
__m256i v1 = vaddm(B2, F, pv), v3 = vsubm(B2, F, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
{ __m256i w = ldu256((t1 + j + 8)); v1 = vshoup(v1, w, ldu256((s1 + j + 8)), pv); }
{ __m256i w = ldu256((t2 + j + 8)); v2 = vshoup(v2, w, ldu256((s2 + j + 8)), pv); }
{ __m256i w = ldu256((t3 + j + 8)); v3 = vshoup(v3, w, ldu256((s3 + j + 8)), pv); }
stu256((pc + jr), u0);
stu256((pc + jr + s), u1);
stu256((pc + jr + 2 * s), u2);
stu256((pc + jr + 3 * s), u3);
stu256((pc + jr + 8), v0);
stu256((pc + jr + s + 8), v1);
stu256((pc + jr + 2 * s + 8), v2);
stu256((pc + jr + 3 * s + 8), v3);
}
for (; j + 8 <= cb + 16384u; j += 8, jr += 8) {
__builtin_prefetch(pc + jr + 192);
__builtin_prefetch(pc + jr + s + 192);
__builtin_prefetch(pc + jr + 2 * s + 192);
__builtin_prefetch(pc + jr + 3 * s + 192);
__m256i x0 = ldu256((pc + jr));
__m256i x1 = ldu256((pc + jr + s));
__m256i x2 = ldu256((pc + jr + 2 * s));
__m256i x3 = ldu256((pc + jr + 3 * s));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
stu256((pc + jr), u0);
stu256((pc + jr + s), u1);
stu256((pc + jr + 2 * s), u2);
stu256((pc + jr + 3 * s), u3);
}
poff += 16384u + TOPPAD;
if (cb + 16384u >= nxt16) { poff += TOPPAD; nxt16 += (1u << 16); }
}
}
}
TGT static void stage4_dif_s2(u32 *a, u32 blk, u32 h, u32 s, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * s) {
u32 *p = a + i;
u32 poff = 0;
for (u32 cb = 0; cb < h; cb += 16384u) {
u32 *pc = p + poff;
u32 j = cb, jr = 0;
for (; j + 16 <= cb + 16384u; j += 16, jr += 16) {
__builtin_prefetch(pc + jr + 192);
__builtin_prefetch(pc + jr + s + 192);
__builtin_prefetch(pc + jr + 2 * s + 192);
__builtin_prefetch(pc + jr + 3 * s + 192);
__builtin_prefetch(t1 + j + 192);
__builtin_prefetch(t2 + j + 192);
__builtin_prefetch(t3 + j + 192);
__m256i x0 = ldu256((pc + jr));
__m256i x1 = ldu256((pc + jr + s));
__m256i x2 = ldu256((pc + jr + 2 * s));
__m256i x3 = ldu256((pc + jr + 3 * s));
__m256i y0 = ldu256((pc + jr + 8));
__m256i y1 = ldu256((pc + jr + s + 8));
__m256i y2 = ldu256((pc + jr + 2 * s + 8));
__m256i y3 = ldu256((pc + jr + 3 * s + 8));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsubm(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
__m256i v1 = vaddm(B2, F, pv), v3 = vsubm(B2, F, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
{ __m256i w = ldu256((t1 + j + 8)); v1 = vshoup(v1, w, ldu256((s1 + j + 8)), pv); }
{ __m256i w = ldu256((t2 + j + 8)); v2 = vshoup(v2, w, ldu256((s2 + j + 8)), pv); }
{ __m256i w = ldu256((t3 + j + 8)); v3 = vshoup(v3, w, ldu256((s3 + j + 8)), pv); }
stu256((pc + jr), u0);
stu256((pc + jr + s), u1);
stu256((pc + jr + 2 * s), u2);
stu256((pc + jr + 3 * s), u3);
stu256((pc + jr + 8), v0);
stu256((pc + jr + s + 8), v1);
stu256((pc + jr + 2 * s + 8), v2);
stu256((pc + jr + 3 * s + 8), v3);
}
for (; j + 8 <= cb + 16384u; j += 8, jr += 8) {
__builtin_prefetch(pc + jr + 192);
__builtin_prefetch(pc + jr + s + 192);
__builtin_prefetch(pc + jr + 2 * s + 192);
__builtin_prefetch(pc + jr + 3 * s + 192);
__m256i x0 = ldu256((pc + jr));
__m256i x1 = ldu256((pc + jr + s));
__m256i x2 = ldu256((pc + jr + 2 * s));
__m256i x3 = ldu256((pc + jr + 3 * s));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
stu256((pc + jr), u0);
stu256((pc + jr + s), u1);
stu256((pc + jr + 2 * s), u2);
stu256((pc + jr + 3 * s), u3);
}
poff += 16384u + TOPPAD;
}
}
}
TGT static void stage4_dif_s3(u32 *a, u32 blk, u32 h, u32 s, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * s) {
u32 *p = a + i;
u32 j = 0;
for (; j + 16 <= h; j += 16) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + s + 192);
__builtin_prefetch(p + j + 2 * s + 192);
__builtin_prefetch(p + j + 3 * s + 192);
__builtin_prefetch(t1 + j + 192);
__builtin_prefetch(t2 + j + 192);
__builtin_prefetch(t3 + j + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + s));
__m256i x2 = ldu256((p + j + 2 * s));
__m256i x3 = ldu256((p + j + 3 * s));
__m256i y0 = ldu256((p + j + 8));
__m256i y1 = ldu256((p + j + s + 8));
__m256i y2 = ldu256((p + j + 2 * s + 8));
__m256i y3 = ldu256((p + j + 3 * s + 8));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsubm(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
__m256i v1 = vaddm(B2, F, pv), v3 = vsubm(B2, F, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
{ __m256i w = ldu256((t1 + j + 8)); v1 = vshoup(v1, w, ldu256((s1 + j + 8)), pv); }
{ __m256i w = ldu256((t2 + j + 8)); v2 = vshoup(v2, w, ldu256((s2 + j + 8)), pv); }
{ __m256i w = ldu256((t3 + j + 8)); v3 = vshoup(v3, w, ldu256((s3 + j + 8)), pv); }
stu256((p + j), u0);
stu256((p + j + s), u1);
stu256((p + j + 2 * s), u2);
stu256((p + j + 3 * s), u3);
stu256((p + j + 8), v0);
stu256((p + j + s + 8), v1);
stu256((p + j + 2 * s + 8), v2);
stu256((p + j + 3 * s + 8), v3);
}
for (; j + 8 <= h; j += 8) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + s + 192);
__builtin_prefetch(p + j + 2 * s + 192);
__builtin_prefetch(p + j + 3 * s + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + s));
__m256i x2 = ldu256((p + j + 2 * s));
__m256i x3 = ldu256((p + j + 3 * s));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
stu256((p + j), u0);
stu256((p + j + s), u1);
stu256((p + j + 2 * s), u2);
stu256((p + j + 3 * s), u3);
}
}
}
TGT static void stage4_dif(u32 *a, u32 blk, u32 h, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * h) {
u32 *p = a + i;
u32 j = 0;
for (; j + 16 <= h; j += 16) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + h + 192);
__builtin_prefetch(p + j + 2 * h + 192);
__builtin_prefetch(p + j + 3 * h + 192);
__builtin_prefetch(t1 + j + 192);
__builtin_prefetch(t2 + j + 192);
__builtin_prefetch(t3 + j + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
__m256i y0 = ldu256((p + j + 8));
__m256i y1 = ldu256((p + j + h + 8));
__m256i y2 = ldu256((p + j + 2 * h + 8));
__m256i y3 = ldu256((p + j + 3 * h + 8));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsubm(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
__m256i v1 = vaddm(B2, F, pv), v3 = vsubm(B2, F, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
{ __m256i w = ldu256((t1 + j + 8)); v1 = vshoup(v1, w, ldu256((s1 + j + 8)), pv); }
{ __m256i w = ldu256((t2 + j + 8)); v2 = vshoup(v2, w, ldu256((s2 + j + 8)), pv); }
{ __m256i w = ldu256((t3 + j + 8)); v3 = vshoup(v3, w, ldu256((s3 + j + 8)), pv); }
stu256((p + j), u0);
stu256((p + j + h), u1);
stu256((p + j + 2 * h), u2);
stu256((p + j + 3 * h), u3);
stu256((p + j + 8), v0);
stu256((p + j + h + 8), v1);
stu256((p + j + 2 * h + 8), v2);
stu256((p + j + 3 * h + 8), v3);
}
for (; j + 8 <= h; j += 8) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + h + 192);
__builtin_prefetch(p + j + 2 * h + 192);
__builtin_prefetch(p + j + 3 * h + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
{ __m256i w = ldu256((t1 + j)); u1 = vshoup(u1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); u2 = vshoup(u2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); u3 = vshoup(u3, w, ldu256((s3 + j)), pv); }
stu256((p + j), u0);
stu256((p + j + h), u1);
stu256((p + j + 2 * h), u2);
stu256((p + j + 3 * h), u3);
}
}
}
TGT static void stage4_dif_t(u32 *a, u32 blk, u32 h, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * h) {
u32 *p = a + i;
u32 j = 0;
for (; j + 16 <= h; j += 16) {
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
__m256i y0 = ldu256((p + j + 8));
__m256i y1 = ldu256((p + j + h + 8));
__m256i y2 = ldu256((p + j + 2 * h + 8));
__m256i y3 = ldu256((p + j + 3 * h + 8));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsubm(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
__m256i v1 = vaddm(B2, F, pv), v3 = vsubm(B2, F, pv);
{ __m256i w = ldu256((t1 + j)); __m256i ws = ldu256((s1 + j)); u1 = vshoup(u1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j)); __m256i ws = ldu256((s2 + j)); u2 = vshoup(u2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j)); __m256i ws = ldu256((s3 + j)); u3 = vshoup(u3, w, ws, pv); }
{ __m256i w = ldu256((t1 + j + 8)); __m256i ws = ldu256((s1 + j + 8)); v1 = vshoup(v1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j + 8)); __m256i ws = ldu256((s2 + j + 8)); v2 = vshoup(v2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j + 8)); __m256i ws = ldu256((s3 + j + 8)); v3 = vshoup(v3, w, ws, pv); }
stu256((p + j), u0);
stu256((p + j + h), u1);
stu256((p + j + 2 * h), u2);
stu256((p + j + 3 * h), u3);
stu256((p + j + 8), v0);
stu256((p + j + h + 8), v1);
stu256((p + j + 2 * h + 8), v2);
stu256((p + j + 3 * h + 8), v3);
}
for (; j + 8 <= h; j += 8) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + h + 192);
__builtin_prefetch(p + j + 2 * h + 192);
__builtin_prefetch(p + j + 3 * h + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
{ __m256i w = ldu256((t1 + j)); __m256i ws = ldu256((s1 + j)); u1 = vshoup(u1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j)); __m256i ws = ldu256((s2 + j)); u2 = vshoup(u2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j)); __m256i ws = ldu256((s3 + j)); u3 = vshoup(u3, w, ws, pv); }
stu256((p + j), u0);
stu256((p + j + h), u1);
stu256((p + j + 2 * h), u2);
stu256((p + j + 3 * h), u3);
}
}
}
template<int H>
TGT static void stage4_dif_h(u32 *a, u32 blk, u32 _h, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * H) {
u32 *p = a + i;
u32 j = 0;
for (; j + 16 <= H; j += 16) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + H + 192);
__builtin_prefetch(p + j + 2 * H + 192);
__builtin_prefetch(p + j + 3 * H + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + H));
__m256i x2 = ldu256((p + j + 2 * H));
__m256i x3 = ldu256((p + j + 3 * H));
__m256i y0 = ldu256((p + j + 8));
__m256i y1 = ldu256((p + j + H + 8));
__m256i y2 = ldu256((p + j + 2 * H + 8));
__m256i y3 = ldu256((p + j + 3 * H + 8));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsubm(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
__m256i v1 = vaddm(B2, F, pv), v3 = vsubm(B2, F, pv);
{ __m256i w = ldu256((t1 + j)); __m256i ws = ldu256((s1 + j)); u1 = vshoup(u1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j)); __m256i ws = ldu256((s2 + j)); u2 = vshoup(u2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j)); __m256i ws = ldu256((s3 + j)); u3 = vshoup(u3, w, ws, pv); }
{ __m256i w = ldu256((t1 + j + 8)); __m256i ws = ldu256((s1 + j + 8)); v1 = vshoup(v1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j + 8)); __m256i ws = ldu256((s2 + j + 8)); v2 = vshoup(v2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j + 8)); __m256i ws = ldu256((s3 + j + 8)); v3 = vshoup(v3, w, ws, pv); }
stu256((p + j), u0);
stu256((p + j + H), u1);
stu256((p + j + 2 * H), u2);
stu256((p + j + 3 * H), u3);
stu256((p + j + 8), v0);
stu256((p + j + H + 8), v1);
stu256((p + j + 2 * H + 8), v2);
stu256((p + j + 3 * H + 8), v3);
}
for (; j + 8 <= H; j += 8) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + H + 192);
__builtin_prefetch(p + j + 2 * H + 192);
__builtin_prefetch(p + j + 3 * H + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + H));
__m256i x2 = ldu256((p + j + 2 * H));
__m256i x3 = ldu256((p + j + 3 * H));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
{ __m256i w = ldu256((t1 + j)); __m256i ws = ldu256((s1 + j)); u1 = vshoup(u1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j)); __m256i ws = ldu256((s2 + j)); u2 = vshoup(u2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j)); __m256i ws = ldu256((s3 + j)); u3 = vshoup(u3, w, ws, pv); }
stu256((p + j), u0);
stu256((p + j + H), u1);
stu256((p + j + 2 * H), u2);
stu256((p + j + 3 * H), u3);
}
}
}
TGT static void stage4_dif_nt(u32 *a, u32 blk, u32 h, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * h) {
u32 *p = a + i;
u32 j = 0;
for (; j + 16 <= h; j += 16) {
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
__m256i y0 = ldu256((p + j + 8));
__m256i y1 = ldu256((p + j + h + 8));
__m256i y2 = ldu256((p + j + 2 * h + 8));
__m256i y3 = ldu256((p + j + 3 * h + 8));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsubm(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
__m256i v1 = vaddm(B2, F, pv), v3 = vsubm(B2, F, pv);
u1 = vshoup(u1, ldu256((t1 + j)), der_ws(ldu256((t1 + j))), pv);
u2 = vshoup(u2, ldu256((t2 + j)), der_ws(ldu256((t2 + j))), pv);
u3 = vshoup(u3, ldu256((t3 + j)), der_ws(ldu256((t3 + j))), pv);
v1 = vshoup(v1, ldu256((t1 + j + 8)), der_ws(ldu256((t1 + j + 8))), pv);
v2 = vshoup(v2, ldu256((t2 + j + 8)), der_ws(ldu256((t2 + j + 8))), pv);
v3 = vshoup(v3, ldu256((t3 + j + 8)), der_ws(ldu256((t3 + j + 8))), pv);
_mm256_stream_si256((__m256i *)(p + j), u0);
_mm256_stream_si256((__m256i *)(p + j + h), u1);
_mm256_stream_si256((__m256i *)(p + j + 2 * h), u2);
_mm256_stream_si256((__m256i *)(p + j + 3 * h), u3);
_mm256_stream_si256((__m256i *)(p + j + 8), v0);
_mm256_stream_si256((__m256i *)(p + j + h + 8), v1);
_mm256_stream_si256((__m256i *)(p + j + 2 * h + 8), v2);
_mm256_stream_si256((__m256i *)(p + j + 3 * h + 8), v3);
}
for (; j + 8 <= h; j += 8) {
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
u1 = vshoup(u1, ldu256((t1 + j)), der_ws(ldu256((t1 + j))), pv);
u2 = vshoup(u2, ldu256((t2 + j)), der_ws(ldu256((t2 + j))), pv);
u3 = vshoup(u3, ldu256((t3 + j)), der_ws(ldu256((t3 + j))), pv);
_mm256_stream_si256((__m256i *)(p + j), u0);
_mm256_stream_si256((__m256i *)(p + j + h), u1);
_mm256_stream_si256((__m256i *)(p + j + 2 * h), u2);
_mm256_stream_si256((__m256i *)(p + j + 3 * h), u3);
}
}
}
TGT static inline __m256i final_dif4_vec(__m256i x, __m256i pv,
__m256i cI, __m256i cIs) {
__m256i t = _mm256_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
__m256i v = vaddm(x, t, pv);
__m256i d = vsubm(x, t, pv);
__m256i Z = _mm256_shuffle_epi32(d, 0x00);
__m256i P = _mm256_blend_epi32(v, Z, 0xAA);
__m256i E = vshoup(d, cI, cIs, pv);
__m256i E2 = _mm256_shuffle_epi32(E, 0x55);
__m256i W = _mm256_shuffle_epi32(v, 0x55);
__m256i Q = _mm256_blend_epi32(W, E2, 0xAA);
__m256i sum = vaddm(P, Q, pv);
__m256i dif = vsubm(P, Q, pv);
return _mm256_blend_epi32(sum, dif, 0xCC);
}
TGT static void stage4_dif_h1(u32 *a, u32 blk, u32 ci, u32 cis);
static long g_pmdelta = 0;
#define E7Z3_PM_ON() (g_pmdelta != 0)
TGT static inline __m256i e7z3_pmul(__m256i u, const u32 *p, __m256i pv) {
if (g_pmdelta == 0) return u;
const __m128i *lo = (const __m128i *)((const char *)p + g_pmdelta);
const __m128i *hi = (const __m128i *)((const char *)(p + 16) + g_pmdelta);
__m256i w = ld2(lo, hi);
return vshoup(w, u, der_ws(u), pv);
}
TGT static inline void e7z3_st(void *p, __m256i v) {
if (g_pmdelta == 0) stu256(p, v);
else stu256((void *)((char *)p + g_pmdelta), v);
}
TGT static inline void e7z3_st2(__m128i *lo, __m128i *hi, __m256i v) {
if (g_pmdelta == 0) st2(lo, hi, v);
else st2((__m128i *)((char *)lo + g_pmdelta), (__m128i *)((char *)hi + g_pmdelta), v);
}
TGT static void stage4_dif_h4(u32 *a, u32 nsub, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
if (nsub < 32) {
const __m128i pv4 = _mm256_castsi256_si128(pv), cI4 = _mm256_castsi256_si128(cI), cIs4 = _mm256_castsi256_si128(cIs);
__m128i W1 = _mm_loadu_si128((const __m128i *)t1), X1 = _mm_loadu_si128((const __m128i *)s1);
__m128i W2 = _mm_loadu_si128((const __m128i *)t2), X2 = _mm_loadu_si128((const __m128i *)s2);
__m128i W3 = _mm_loadu_si128((const __m128i *)t3), X3 = _mm_loadu_si128((const __m128i *)s3);
for (u32 i = 0; i < nsub; i += 16) {
u32 *p = a + i;
__m128i x0 = _mm_loadu_si128((const __m128i *)(p));
__m128i x1 = _mm_loadu_si128((const __m128i *)(p + 4));
__m128i x2 = _mm_loadu_si128((const __m128i *)(p + 8));
__m128i x3 = _mm_loadu_si128((const __m128i *)(p + 12));
__m128i A = add4(x0, x2, pv4), B = sub4(x0, x2, pv4);
__m128i C = add4(x1, x3, pv4), D = sub4(x1, x3, pv4);
__m128i u0 = add4(A, C, pv4), u2 = sub4(A, C, pv4);
__m128i E = sh4(D, cI4, cIs4, pv4);
__m128i u1 = add4(B, E, pv4), u3 = sub4(B, E, pv4);
u1 = sh4(u1, W1, X1, pv4); u2 = sh4(u2, W2, X2, pv4); u3 = sh4(u3, W3, X3, pv4);
_mm_storeu_si128((__m128i *)(p), u0);
_mm_storeu_si128((__m128i *)(p + 4), u1);
_mm_storeu_si128((__m128i *)(p + 8), u2);
_mm_storeu_si128((__m128i *)(p + 12), u3);
}
stage4_dif_h1(a, nsub, ci, cis);
return;
}
__m256i W1 = bc128(_mm_loadu_si128((const __m128i *)t1));
__m256i W2 = bc128(_mm_loadu_si128((const __m128i *)t2));
__m256i W3 = bc128(_mm_loadu_si128((const __m128i *)t3));
__m256i X1 = bc128(_mm_loadu_si128((const __m128i *)s1));
__m256i X2 = bc128(_mm_loadu_si128((const __m128i *)s2));
__m256i X3 = bc128(_mm_loadu_si128((const __m128i *)s3));
#pragma GCC unroll 2
for (u32 i = 0; i < nsub; i += 32) {
u32 *p = a + i;
__m256i x0 = ld2((const __m128i *)(p), (const __m128i *)(p + 16));
__m256i x1 = ld2((const __m128i *)(p + 4), (const __m128i *)(p + 20));
__m256i x2 = ld2((const __m128i *)(p + 8), (const __m128i *)(p + 24));
__m256i x3 = ld2((const __m128i *)(p + 12), (const __m128i *)(p + 28));
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsubm(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = vaddm(B, E, pv), u3 = vsubm(B, E, pv);
u1 = vshoup(u1, W1, X1, pv); u2 = vshoup(u2, W2, X2, pv); u3 = vshoup(u3, W3, X3, pv);
u0 = final_dif4_vec(u0, pv, cI, cIs);
u1 = final_dif4_vec(u1, pv, cI, cIs);
u2 = final_dif4_vec(u2, pv, cI, cIs);
u3 = final_dif4_vec(u3, pv, cI, cIs);
if (E7Z3_PM_ON()) {
u0 = e7z3_pmul(u0, p, pv); u1 = e7z3_pmul(u1, p + 4, pv);
u2 = e7z3_pmul(u2, p + 8, pv); u3 = e7z3_pmul(u3, p + 12, pv);
}
e7z3_st2((__m128i *)(p), (__m128i *)(p + 16), u0);
e7z3_st2((__m128i *)(p + 4), (__m128i *)(p + 20), u1);
e7z3_st2((__m128i *)(p + 8), (__m128i *)(p + 24), u2);
e7z3_st2((__m128i *)(p + 12), (__m128i *)(p + 28), u3);
}
}
TGT static void stage4_dif_h1(u32 *a, u32 blk, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 8) {
u32 *p = a + i;
__m256i x = ldu256(p);
__m256i t = _mm256_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
__m256i s = vaddm(x, t, pv);
__m256i d = vsubm(x, t, pv);
__m256i Z = _mm256_shuffle_epi32(d, 0x00);
__m256i P = _mm256_blend_epi32(s, Z, 0xAA);
__m256i E = vshoup(d, cI, cIs, pv);
__m256i E2 = _mm256_shuffle_epi32(E, 0x55);
__m256i W = _mm256_shuffle_epi32(s, 0x55);
__m256i Q = _mm256_blend_epi32(W, E2, 0xAA);
__m256i sum = vaddm(P, Q, pv);
__m256i dif = vsubm(P, Q, pv);
stu256(p, _mm256_blend_epi32(sum, dif, 0xCC));
}
}
TGT static void stage2_dif(u32 *a, u32 blk, const u32 *t, const u32 *s) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
u32 h = blk >> 1;
for (u32 j = 0; j < h; j += 8) {
__m256i x0 = ldu256((a + j));
__m256i x1 = ldu256((a + j + h));
__m256i u0 = vaddm(x0, x1, pv);
__m256i u1 = vshoup(vsubm(x0, x1, pv), ldu256((t + j)),
ldu256((s + j)), pv);
stu256((a + j), u0);
stu256((a + j + h), u1);
}
}
TGT static void merge4_dit_s2(u32 *a, u32 blk, u32 h, u32 s, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * s) {
u32 *p = a + i;
u32 poff = 0;
for (u32 cb = 0; cb < h; cb += 16384u) {
u32 *pc = p + poff;
for (u32 j = cb, jr = 0; j < cb + 16384u; j += 8, jr += 8) {
__builtin_prefetch(pc + jr + 192);
__builtin_prefetch(pc + jr + s + 192);
__builtin_prefetch(pc + jr + 2 * s + 192);
__builtin_prefetch(pc + jr + 3 * s + 192);
__builtin_prefetch(t1 + j + 192);
__builtin_prefetch(t2 + j + 192);
__builtin_prefetch(t3 + j + 192);
__m256i x0 = ldu256((pc + jr));
__m256i x1 = ldu256((pc + jr + s));
__m256i x2 = ldu256((pc + jr + 2 * s));
__m256i x3 = ldu256((pc + jr + 3 * s));
{ __m256i w = ldu256((t1 + j)); x1 = vshoup(x1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); x2 = vshoup(x2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); x3 = vshoup(x3, w, ldu256((s3 + j)), pv); }
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
stu256((pc + jr), vaddm(A, C, pv));
stu256((pc + jr + s), vaddm(B, E, pv));
stu256((pc + jr + 2 * s), vsubm(A, C, pv));
stu256((pc + jr + 3 * s), vsubm(B, E, pv));
}
poff += 16384u + TOPPAD;
}
}
}
TGT static void merge4_dit_s3(u32 *a, u32 blk, u32 h, u32 s, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * s) {
u32 *p = a + i;
for (u32 j = 0; j < h; j += 8) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + s + 192);
__builtin_prefetch(p + j + 2 * s + 192);
__builtin_prefetch(p + j + 3 * s + 192);
__builtin_prefetch(t1 + j + 192);
__builtin_prefetch(t2 + j + 192);
__builtin_prefetch(t3 + j + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + s));
__m256i x2 = ldu256((p + j + 2 * s));
__m256i x3 = ldu256((p + j + 3 * s));
{ __m256i w = ldu256((t1 + j)); x1 = vshoup(x1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); x2 = vshoup(x2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); x3 = vshoup(x3, w, ldu256((s3 + j)), pv); }
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
stu256((p + j), vaddm(A, C, pv));
stu256((p + j + s), vaddm(B, E, pv));
stu256((p + j + 2 * s), vsubm(A, C, pv));
stu256((p + j + 3 * s), vsubm(B, E, pv));
}
}
}
TGT static void merge4_dit(u32 *a, u32 blk, u32 h, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * h) {
u32 *p = a + i;
for (u32 j = 0; j < h; j += 8) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + h + 192);
__builtin_prefetch(p + j + 2 * h + 192);
__builtin_prefetch(p + j + 3 * h + 192);
__builtin_prefetch(t1 + j + 192);
__builtin_prefetch(t2 + j + 192);
__builtin_prefetch(t3 + j + 192);
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
{ __m256i w = ldu256((t1 + j)); x1 = vshoup(x1, w, ldu256((s1 + j)), pv); }
{ __m256i w = ldu256((t2 + j)); x2 = vshoup(x2, w, ldu256((s2 + j)), pv); }
{ __m256i w = ldu256((t3 + j)); x3 = vshoup(x3, w, ldu256((s3 + j)), pv); }
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
stu256((p + j), vaddm(A, C, pv));
stu256((p + j + h), vaddm(B, E, pv));
stu256((p + j + 2 * h), vsubm(A, C, pv));
stu256((p + j + 3 * h), vsubm(B, E, pv));
}
}
}
TGT static void merge4_dit_t(u32 *a, u32 blk, u32 h, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * h) {
u32 *p = a + i;
for (u32 j = 0; j < h; j += 8) {
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
{ __m256i w = ldu256((t1 + j)); __m256i ws = ldu256((s1 + j)); x1 = vshoup(x1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j)); __m256i ws = ldu256((s2 + j)); x2 = vshoup(x2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j)); __m256i ws = ldu256((s3 + j)); x3 = vshoup(x3, w, ws, pv); }
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
stu256((p + j), vaddm(A, C, pv));
stu256((p + j + h), vaddm(B, E, pv));
stu256((p + j + 2 * h), vsubm(A, C, pv));
stu256((p + j + 3 * h), vsubm(B, E, pv));
}
}
}
template<int H>
TGT static void merge4_dit_h(u32 *a, u32 blk, u32 _h, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * H) {
u32 *p = a + i;
u32 j = 0;
for (; j < H; j += 16) {
__builtin_prefetch(p + j + 192);
__builtin_prefetch(p + j + H + 192);
__builtin_prefetch(p + j + 2 * H + 192);
__builtin_prefetch(p + j + 3 * H + 192);
__m256i x0 = ldu256((p + j));
__m256i y0 = ldu256((p + j + 8));
__m256i x1 = ldu256((p + j + H));
__m256i y1 = ldu256((p + j + H + 8));
__m256i x2 = ldu256((p + j + 2 * H));
__m256i y2 = ldu256((p + j + 2 * H + 8));
__m256i x3 = ldu256((p + j + 3 * H));
__m256i y3 = ldu256((p + j + 3 * H + 8));
{ __m256i w = ldu256((t1 + j)); __m256i ws = ldu256((s1 + j)); x1 = vshoup(x1, w, ws, pv); }
{ __m256i w = ldu256((t1 + j + 8)); __m256i ws = ldu256((s1 + j + 8)); y1 = vshoup(y1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j)); __m256i ws = ldu256((s2 + j)); x2 = vshoup(x2, w, ws, pv); }
{ __m256i w = ldu256((t2 + j + 8)); __m256i ws = ldu256((s2 + j + 8)); y2 = vshoup(y2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j)); __m256i ws = ldu256((s3 + j)); x3 = vshoup(x3, w, ws, pv); }
{ __m256i w = ldu256((t3 + j + 8)); __m256i ws = ldu256((s3 + j + 8)); y3 = vshoup(y3, w, ws, pv); }
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsubm(y1, y3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
stu256((p + j), vaddm(A, C, pv));
stu256((p + j + 8), vaddm(A2, C2, pv));
stu256((p + j + H), vaddm(B, E, pv));
stu256((p + j + H + 8), vaddm(B2, F, pv));
stu256((p + j + 2 * H), vsubm(A, C, pv));
stu256((p + j + 2 * H + 8), vsubm(A2, C2, pv));
stu256((p + j + 3 * H), vsubm(B, E, pv));
stu256((p + j + 3 * H + 8), vsubm(B2, F, pv));
}
for (; j < H; j += 8) {
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + H));
__m256i x2 = ldu256((p + j + 2 * H));
__m256i x3 = ldu256((p + j + 3 * H));
{ __m256i w = ldu256((t1 + j)); __m256i ws = ldu256((s1 + j)); x1 = vshoup(x1, w, ws, pv); }
{ __m256i w = ldu256((t2 + j)); __m256i ws = ldu256((s2 + j)); x2 = vshoup(x2, w, ws, pv); }
{ __m256i w = ldu256((t3 + j)); __m256i ws = ldu256((s3 + j)); x3 = vshoup(x3, w, ws, pv); }
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
stu256((p + j), vaddm(A, C, pv));
stu256((p + j + H), vaddm(B, E, pv));
stu256((p + j + 2 * H), vsubm(A, C, pv));
stu256((p + j + 3 * H), vsubm(B, E, pv));
}
}
}
TGT static void merge4_dit_nt(u32 *a, u32 blk, u32 h, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 4 * h) {
u32 *p = a + i;
for (u32 j = 0; j < h; j += 8) {
__m256i x0 = ldu256((p + j));
__m256i x1 = ldu256((p + j + h));
__m256i x2 = ldu256((p + j + 2 * h));
__m256i x3 = ldu256((p + j + 3 * h));
x1 = vshoup(x1, ldu256((t1 + j)), der_ws(ldu256((t1 + j))), pv);
x2 = vshoup(x2, ldu256((t2 + j)), der_ws(ldu256((t2 + j))), pv);
x3 = vshoup(x3, ldu256((t3 + j)), der_ws(ldu256((t3 + j))), pv);
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
_mm256_stream_si256((__m256i *)(p + j), vaddm(A, C, pv));
_mm256_stream_si256((__m256i *)(p + j + h), vaddm(B, E, pv));
_mm256_stream_si256((__m256i *)(p + j + 2 * h), vsubm(A, C, pv));
_mm256_stream_si256((__m256i *)(p + j + 3 * h), vsubm(B, E, pv));
}
}
}
TGT static void merge4_dit_h4(u32 *a, u32 nsub, const u32 *t1, const u32 *t2, const u32 *t3,
const u32 *s1, const u32 *s2, const u32 *s3, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
if (nsub < 32) {
const __m128i pv4 = _mm256_castsi256_si128(pv), cI4 = _mm256_castsi256_si128(cI), cIs4 = _mm256_castsi256_si128(cIs);
__m128i W1 = _mm_loadu_si128((const __m128i *)t1), X1 = _mm_loadu_si128((const __m128i *)s1);
__m128i W2 = _mm_loadu_si128((const __m128i *)t2), X2 = _mm_loadu_si128((const __m128i *)s2);
__m128i W3 = _mm_loadu_si128((const __m128i *)t3), X3 = _mm_loadu_si128((const __m128i *)s3);
for (u32 i = 0; i < nsub; i += 16) {
u32 *p = a + i;
__m128i x0 = _mm_loadu_si128((const __m128i *)(p));
__m128i x1 = sh4(_mm_loadu_si128((const __m128i *)(p + 4)), W1, X1, pv4);
__m128i x2 = sh4(_mm_loadu_si128((const __m128i *)(p + 8)), W2, X2, pv4);
__m128i x3 = sh4(_mm_loadu_si128((const __m128i *)(p + 12)), W3, X3, pv4);
__m128i A = add4(x0, x2, pv4), B = sub4(x0, x2, pv4);
__m128i C = add4(x1, x3, pv4), D = sub4(x1, x3, pv4);
__m128i E = sh4(D, cI4, cIs4, pv4);
_mm_storeu_si128((__m128i *)(p), add4(A, C, pv4));
_mm_storeu_si128((__m128i *)(p + 4), add4(B, E, pv4));
_mm_storeu_si128((__m128i *)(p + 8), sub4(A, C, pv4));
_mm_storeu_si128((__m128i *)(p + 12), sub4(B, E, pv4));
}
return;
}
__m256i W1 = bc128(_mm_loadu_si128((const __m128i *)t1));
__m256i W2 = bc128(_mm_loadu_si128((const __m128i *)t2));
__m256i W3 = bc128(_mm_loadu_si128((const __m128i *)t3));
__m256i X1 = bc128(_mm_loadu_si128((const __m128i *)s1));
__m256i X2 = bc128(_mm_loadu_si128((const __m128i *)s2));
__m256i X3 = bc128(_mm_loadu_si128((const __m128i *)s3));
for (u32 i = 0; i < nsub; i += 32) {
u32 *p = a + i;
__m256i x0 = ld2((const __m128i *)(p), (const __m128i *)(p + 16));
__m256i x1 = ld2((const __m128i *)(p + 4), (const __m128i *)(p + 20));
__m256i x2 = ld2((const __m128i *)(p + 8), (const __m128i *)(p + 24));
__m256i x3 = ld2((const __m128i *)(p + 12), (const __m128i *)(p + 28));
x1 = vshoup(x1, W1, X1, pv); x2 = vshoup(x2, W2, X2, pv); x3 = vshoup(x3, W3, X3, pv);
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
st2((__m128i *)(p), (__m128i *)(p + 16), vaddm(A, C, pv));
st2((__m128i *)(p + 4), (__m128i *)(p + 20), vaddm(B, E, pv));
st2((__m128i *)(p + 8), (__m128i *)(p + 24), vsubm(A, C, pv));
st2((__m128i *)(p + 12), (__m128i *)(p + 28), vsubm(B, E, pv));
}
}
TGT static void merge4_dit_h1(u32 *a, u32 blk, u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
for (u32 i = 0; i < blk; i += 8) {
u32 *p = a + i;
__m256i x = ldu256(p);
__m256i t = _mm256_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
__m256i s = vaddm(x, t, pv);
__m256i d = vsubm(x, t, pv);
__m256i Z = _mm256_shuffle_epi32(d, 0x00);
__m256i P = _mm256_blend_epi32(s, Z, 0xAA);
__m256i E = vshoup(d, cI, cIs, pv);
__m256i E2 = _mm256_shuffle_epi32(E, 0x55);
__m256i W = _mm256_shuffle_epi32(s, 0x55);
__m256i Q = _mm256_blend_epi32(W, E2, 0xAA);
__m256i sum = vaddm(P, Q, pv);
__m256i dif = vsubm(P, Q, pv);
stu256(p, _mm256_blend_epi32(sum, dif, 0xCC));
}
}
TGT static void merge2_dit(u32 *a, u32 blk, const u32 *t, const u32 *s) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
u32 h = blk >> 1;
for (u32 j = 0; j < h; j += 8) {
__m256i x0 = ldu256((a + j));
__m256i x1 = vshoup(ldu256((a + j + h)),
ldu256((t + j)),
ldu256((s + j)), pv);
stu256((a + j), vaddm(x0, x1, pv));
stu256((a + j + h), vsubm(x0, x1, pv));
}
}
TGT static inline void ntst256(void *p, __m256i v) {
if (((uintptr_t)p & 31u) == 0) _mm256_stream_si256((__m256i *)p, v);
else stu256(p, v);
}
TGT static void gen_geo_val(u32 *dst, u32 cnt, u32 v0, u32 r);
TGT static void gen_geo(u32 *dst, u32 *sc, u32 cnt, u32 v0, u32 r) {
if (!sc) { gen_geo_val(dst, cnt, v0, r); return; } /* [E7Y-DEADSC] sc column not read */
if (!cnt) return;
if (cnt < 32) {
u32 x=v0%NTP;
for (u32 i=0;i<cnt;i++) { dst[i]=x; sc[i]=shoup_c(x); x=mulmod(x,r); }
return;
}
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i mlo = _mm256_set1_epi32(572662301u);
u32 seq[32], cur=v0%NTP;
for (int i=0;i<32;i++) { seq[i]=cur; cur=mulmod(cur,r); }
u32 rs=shoup_c(cur);
const __m256i rv=_mm256_set1_epi32((int)cur);
const __m256i rsv=_mm256_set1_epi32((int)rs);
__m256i a=ldu256(seq), b=ldu256(seq+8), c=ldu256(seq+16), d=ldu256(seq+24);
u32 i=0;
for (;i+32<=cnt;i+=32) {
ntst256(dst+i,a); ntst256(dst+i+8,b); ntst256(dst+i+16,c); ntst256(dst+i+24,d);
ntst256(sc+i,_mm256_add_epi32(_mm256_add_epi32(a,a),vmulhi(a,mlo)));
ntst256(sc+i+8,_mm256_add_epi32(_mm256_add_epi32(b,b),vmulhi(b,mlo)));
ntst256(sc+i+16,_mm256_add_epi32(_mm256_add_epi32(c,c),vmulhi(c,mlo)));
ntst256(sc+i+24,_mm256_add_epi32(_mm256_add_epi32(d,d),vmulhi(d,mlo)));
a=vshoup(a,rv,rsv,pv); b=vshoup(b,rv,rsv,pv);
c=vshoup(c,rv,rsv,pv); d=vshoup(d,rv,rsv,pv);
}
if (i<cnt) {
u32 x=mulmod(v0,powmod(r,i));
for (;i<cnt;i++) { dst[i]=x; sc[i]=shoup_c(x); x=mulmod(x,r); }
}
}
TGT static void gen_geo_val(u32 *dst, u32 cnt, u32 v0, u32 r) {
if (!cnt) return;
if (cnt < 32) {
u32 x=v0%NTP;
for (u32 i=0;i<cnt;i++) { dst[i]=x; x=mulmod(x,r); }
return;
}
const __m256i pv = _mm256_set1_epi32((int)NTP);
u32 seq[32], cur=v0%NTP;
for (int i=0;i<32;i++) { seq[i]=cur; cur=mulmod(cur,r); }
if (v0 != 1u) cur=powmod(r,32);
u32 rs=shoup_c(cur);
const __m256i rv=_mm256_set1_epi32((int)cur);
const __m256i rsv=_mm256_set1_epi32((int)rs);
__m256i a=ldu256(seq), b=ldu256(seq+8), c=ldu256(seq+16), d=ldu256(seq+24);
u32 i=0;
for (;i+32<=cnt;i+=32) {
ntst256(dst+i,a); ntst256(dst+i+8,b); ntst256(dst+i+16,c); ntst256(dst+i+24,d);
a=vshoup(a,rv,rsv,pv); b=vshoup(b,rv,rsv,pv);
c=vshoup(c,rv,rsv,pv); d=vshoup(d,rv,rsv,pv);
}
if (i<cnt) {
u32 x=mulmod(v0,powmod(r,i));
for (;i<cnt;i++) { dst[i]=x; x=mulmod(x,r); }
}
}
static void build_tabs(Tabs &T, u32 *tab, u32 n, u32 Sb, int L, int odd, u32 root, u32 *scbase, u32 skipsc) {
u32 *p = tab;
u32 *sp = scbase;
u32 S = n;
for (int l = 0; l < L; l++) {
u32 M = S >> 2;
const u32 MPr = M + TPAD;
T.l1[l] = p; T.l2[l] = p + MPr; T.l3[l] = p + 2 * MPr;
T.m1[l] = sp; T.m2[l] = sp + MPr; T.m3[l] = sp + 2 * MPr;
u32 base = powmod(root, n / S);
u32 b1 = base, b2 = mulmod(base, base), b3 = mulmod(b2, base);
{ const bool nosc = ((skipsc >> l) & 1u) != 0; /* [E7Y-DEADSC] */
const u32 unit = (l == 0) ? (u32)((1ull<<32)%NTP) : 1u;
gen_geo(p, nosc ? 0 : sp, M, unit, b1);
gen_geo(p + MPr, nosc ? 0 : sp + MPr, M, unit, b2);
gen_geo(p + 2 * MPr, nosc ? 0 : sp + 2 * MPr, M, unit, b3); }
p += 3 * MPr;
sp += 3 * MPr;
S >>= 2;
}
T.b2 = 0; T.b2s = 0;
if (odd) {
u32 M = Sb >> 1;
T.b2 = p; T.b2s = p + M;
gen_geo(p, p + M, M, 1, powmod(root, n / Sb));
p += 2 * M;
}
int s = 0;
u32 S2v = 4;
while (s < g_nb) {
u32 M = S2v >> 2;
if (M >= 32) p = (u32 *)(((uintptr_t)p + 31u) & ~(uintptr_t)31u);
const u32 MP = M + TPAD;
T.h1[s] = p; T.h2[s] = p + MP; T.h3[s] = p + 2 * MP;
T.q1[s] = p + 3 * MP; T.q2[s] = p + 4 * MP; T.q3[s] = p + 5 * MP;
u32 base = powmod(root, n / S2v);
u32 b1 = base, b2 = mulmod(base, base), b3 = mulmod(b2, base);
gen_geo(p, p + 3 * MP, M, 1, b1);
gen_geo(p + MP, p + 4 * MP, M, 1, b2);
gen_geo(p + 2 * MP, p + 5 * MP, M, 1, b3);
p += 6 * MP;
S2v <<= 2;
s++;
}
}
static void ntt_init(u32 n) {
{ u32 iv = 1; for (int q = 0; q < 5; q++) iv *= 2u - NTP * iv; g_pinv = 0u - iv; }
g_n = n;
int lg = 0; while ((1u << lg) < n) lg++;
g_log2n = lg;
const int BT = g_BT;
int L = 0;
while (lg - 2 * (L + 1) >= BT) L++;
g_L = L;
g_Sb = n >> (2 * L);
g_odd = ((lg - 2 * L) & 1) ? 1 : 0;
u32 S2 = g_Sb; if (g_odd) S2 >>= 1;
g_nb = 0; while (S2 >= 4) { S2 >>= 2; g_nb++; }
u32 w = powmod(31u, (NTP - 1) / n);
u32 wi = powmod(w, NTP - 2);
u32 iF = powmod(w, n >> 2), iI = powmod(wi, n >> 2);
g_ciF = iF; g_cisF = shoup_c(iF);
g_ciI = iI; g_cisI = shoup_c(iI);
build_tabs(g_TF, g_tabF, n, g_Sb, L, g_odd, w, Q_SCF, (1u << 0));
build_tabs(g_TI, g_tabI, n, g_Sb, L, g_odd, wi, Q_SCI, (1u << 0));
}
int g_fuse16 = 1;
#define E7_TILE 512u
static u32 g_scrA[16 * E7_TILE] __attribute__((aligned(64)));
TGT static void stage16_dif_top(u32 *a, u32 b, u32 S,
const u32 *tA1, const u32 *tA2, const u32 *tA3,
const u32 *tB1, const u32 *tB2, const u32 *tB3,
const u32 *sB1, const u32 *sB2, const u32 *sB3,
u32 ci, u32 cis) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const __m256i cI = _mm256_set1_epi32((int)ci), cIs = _mm256_set1_epi32((int)cis);
const u32 h = S >> 2, h4 = S >> 4;
const u32 bb = padpos(b);
for (u32 cb = 0; cb < h4; cb += 16384u) {
u32 *P[16];
for (int p = 0; p < 4; p++)
for (int q = 0; q < 4; q++)
P[p * 4 + q] = a + (padpos(b + cb + (u32)p * h4 + (u32)q * h) - bb);
for (u32 jj0 = 0; jj0 < 16384u; jj0 += E7_TILE) {
for (int p = 0; p < 4; p++) {
u32 *q0 = P[p*4+0], *q1 = P[p*4+1], *q2 = P[p*4+2], *q3 = P[p*4+3];
for (u32 jj = jj0; jj < jj0 + E7_TILE; jj += 8) {
u32 t = jj - jj0;
__builtin_prefetch(q0 + jj + 192);
__builtin_prefetch(q1 + jj + 192);
__builtin_prefetch(q2 + jj + 192);
__builtin_prefetch(q3 + jj + 192);
__m256i x0 = ldu256(q0 + jj), x1 = ldu256(q1 + jj),
x2 = ldu256(q2 + jj), x3 = ldu256(q3 + jj);
__m256i A = vaddm(x0, x2, pv), B = vsubm(x0, x2, pv);
__m256i C = vaddm(x1, x3, pv), D = vsubm(x1, x3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
{ __m256i w = ldu256(tA1 + (u32)p * h4 + cb + jj);
stu256(g_scrA + (p*4+1)*E7_TILE + t, mont_prepared(vaddm(B, E, pv), w, pv)); }
{ __m256i w = ldu256(tA2 + (u32)p * h4 + cb + jj);
stu256(g_scrA + (p*4+2)*E7_TILE + t, mont_prepared(vsubm(A, C, pv), w, pv)); }
{ __m256i w = ldu256(tA3 + (u32)p * h4 + cb + jj);
stu256(g_scrA + (p*4+3)*E7_TILE + t, mont_prepared(vsubm(B, E, pv), w, pv)); }
stu256(g_scrA + (p*4+0)*E7_TILE + t, vaddm(A, C, pv));
}
}
for (int i = 0; i < 4; i++) {
for (u32 jj = jj0; jj < jj0 + E7_TILE; jj += 8) {
u32 t = jj - jj0;
__m256i wB1 = ldu256(tB1 + cb + jj), wB2 = ldu256(tB2 + cb + jj), wB3 = ldu256(tB3 + cb + jj);
const __m256i dwB1 = ldu256(sB1 + cb + jj), dwB2 = ldu256(sB2 + cb + jj), dwB3 = ldu256(sB3 + cb + jj);
__m256i y0 = ldu256(g_scrA + (0*4+i)*E7_TILE + t), y1 = ldu256(g_scrA + (1*4+i)*E7_TILE + t),
y2 = ldu256(g_scrA + (2*4+i)*E7_TILE + t), y3 = ldu256(g_scrA + (3*4+i)*E7_TILE + t);
__m256i A = vaddm(y0, y2, pv), B = vsubm(y0, y2, pv);
__m256i C = vaddm(y1, y3, pv), D = vsubm(y1, y3, pv);
__m256i E = vshoup(D, cI, cIs, pv);
stu256(P[0*4+i] + jj, vaddm(A, C, pv));
stu256(P[1*4+i] + jj, vshoup(vaddm(B, E, pv), wB1, dwB1, pv));
stu256(P[2*4+i] + jj, vshoup(vsubm(A, C, pv), wB2, dwB2, pv));
stu256(P[3*4+i] + jj, vshoup(vsubm(B, E, pv), wB3, dwB3, pv));
}
}
}
}
}
#ifndef E7Z_SBFUSE
#define E7Z_SBFUSE 1
#endif
#ifndef E7Z_IVFUSE
#define E7Z_IVFUSE 1
#endif
TGT static void fwd_sb_ring(u32 *a, u32 b) {
u32 *p = a + padpos(b);
if (g_odd) stage2_dif(p, g_Sb, g_TF.b2, g_TF.b2s);
for (int s = g_nb - 1; s >= 0; s--) {
u32 S2 = 4u << (2 * s);
if (S2 >= 32) {
for (u32 i = 0; i < g_Sb; i += S2)
{ u32 *pp = p + i;
switch (S2) {
case 16384: stage4_dif_h<4096>(pp, 16384, 4096, g_TF.h1[s], g_TF.h2[s], g_TF.h3[s], g_TF.q1[s], g_TF.q2[s], g_TF.q3[s], g_ciF, g_cisF); break;
case 4096: stage4_dif_h<1024>(pp, 4096, 1024, g_TF.h1[s], g_TF.h2[s], g_TF.h3[s], g_TF.q1[s], g_TF.q2[s], g_TF.q3[s], g_ciF, g_cisF); break;
case 1024: stage4_dif_h<256>(pp, 1024, 256, g_TF.h1[s], g_TF.h2[s], g_TF.h3[s], g_TF.q1[s], g_TF.q2[s], g_TF.q3[s], g_ciF, g_cisF); break;
case 256: stage4_dif_h<64>(pp, 256, 64, g_TF.h1[s], g_TF.h2[s], g_TF.h3[s], g_TF.q1[s], g_TF.q2[s], g_TF.q3[s], g_ciF, g_cisF); break;
case 64: stage4_dif_h<16>(pp, 64, 16, g_TF.h1[s], g_TF.h2[s], g_TF.h3[s], g_TF.q1[s], g_TF.q2[s], g_TF.q3[s], g_ciF, g_cisF); break;
default: stage4_dif_t(pp, S2, S2 >> 2, g_TF.h1[s], g_TF.h2[s], g_TF.h3[s], g_TF.q1[s], g_TF.q2[s], g_TF.q3[s], g_ciF, g_cisF);
} }
} else if (S2 == 16) {
stage4_dif_h4(p, g_Sb, g_TF.h1[s], g_TF.h2[s], g_TF.h3[s], g_TF.q1[s], g_TF.q2[s], g_TF.q3[s], g_ciF, g_cisF);
} else {
}
}
}
TGT static void ntt_fwd(u32 *a, const u32 *src = 0, u32 len = 0) {
u32 S = g_n; int sb_done = 0;
int l0 = 0;
if (src && len >= (g_n >> 2) && len <= (g_n >> 1)) {
stage0_dif_src(a, src, len, g_n >> 2, g_TF.l1[0], g_TF.l2[0], g_TF.l3[0],
g_TF.m1[0], g_TF.m2[0], g_TF.m3[0], g_ciF, g_cisF);
S = g_n >> 2;
l0 = 1;
}
int lstart = l0;
if (g_fuse16 && l0 == 0 && S >= (1u << 22) && ((S >> 4) % 16384u) == 0u) {
stage16_dif_top(a, 0u, S, g_TF.l1[0], g_TF.l2[0], g_TF.l3[0],
g_TF.l1[1], g_TF.l2[1], g_TF.l3[1],
g_TF.m1[1], g_TF.m2[1], g_TF.m3[1], g_ciF, g_cisF);
_mm_sfence();
S >>= 4; lstart = 2;
}
for (int l = lstart; l < g_L; l++) {
u32 h = S >> 2;
if (l0 + 0 == l)
for (u32 b = 0; b < g_n; b += S)
stage4_dif_s0(a + padpos(b), S, h, h + 85 * TOPPAD, g_TF.l1[l], g_TF.l2[l], g_TF.l3[l], g_TF.m1[l], g_TF.m2[l], g_TF.m3[l], g_ciF, g_cisF);
else if (l0 + 1 == l)
for (u32 b = 0; b < g_n; b += S)
stage4_dif_s1(a + padpos(b), S, h, h + 21 * TOPPAD, g_TF.l1[l], g_TF.l2[l], g_TF.l3[l], g_TF.m1[l], g_TF.m2[l], g_TF.m3[l], g_ciF, g_cisF);
else if (l0 + 2 == l)
for (u32 b = 0; b < g_n; b += S)
stage4_dif_s2(a + padpos(b), S, h, h + 5 * TOPPAD, g_TF.l1[l], g_TF.l2[l], g_TF.l3[l], g_TF.m1[l], g_TF.m2[l], g_TF.m3[l], g_ciF, g_cisF);
else if (l0 + 3 == l) {
if (E7Z_SBFUSE && l == g_L - 1) {
for (u32 b = 0; b < g_n; b += S) {
stage4_dif_s3(a + padpos(b), S, h, h + 1 * TOPPAD, g_TF.l1[l], g_TF.l2[l], g_TF.l3[l], g_TF.m1[l], g_TF.m2[l], g_TF.m3[l], g_ciF, g_cisF);
for (u32 bb = 0; bb < S; bb += g_Sb) fwd_sb_ring(a, b + bb);
}
sb_done = 1;
} else
for (u32 b = 0; b < g_n; b += S)
stage4_dif_s3(a + padpos(b), S, h, h + 1 * TOPPAD, g_TF.l1[l], g_TF.l2[l], g_TF.l3[l], g_TF.m1[l], g_TF.m2[l], g_TF.m3[l], g_ciF, g_cisF);
}
else
for (u32 b = 0; b < g_n; b += S)
stage4_dif(a + padpos(b), S, h, g_TF.l1[l], g_TF.l2[l], g_TF.l3[l], g_TF.m1[l], g_TF.m2[l], g_TF.m3[l], g_ciF, g_cisF);
_mm_sfence();
S = h;
}
if (!sb_done) {
for (u32 b = 0; b < g_n; b += g_Sb) fwd_sb_ring(a, b);
}
}
#define INV44_TILE 512u
TGT static void fused_inv44(u32 *a){
const u32 h1=1u<<18,h0=1u<<20,sh1=h1+21*TOPPAD,sh0=h0+85*TOPPAD;
const u32 *u11=g_TI.l1[1],*u12=g_TI.l2[1],*u13=g_TI.l3[1];
const u32 *s11=g_TI.m1[1],*s12=g_TI.m2[1],*s13=g_TI.m3[1];
const u32 *u01=g_TI.l1[0],*u02=g_TI.l2[0],*u03=g_TI.l3[0];
const __m256i pv=_mm256_set1_epi32((int)NTP), ci=_mm256_set1_epi32((int)g_ciI),cis=_mm256_set1_epi32((int)g_cisI);
alignas(64) static u32 z[4][4][INV44_TILE+8];
for(u32 j0=0;j0<h1;j0+=INV44_TILE){
for(u32 r=0;r<4;r++)for(u32 t=0;t<INV44_TILE;t+=8){
u32 j=j0+t;u32 *p=a+r*sh0+padpos(j);__builtin_prefetch(p+128);__builtin_prefetch(p+sh1+128);__builtin_prefetch(p+2*sh1+128);__builtin_prefetch(p+3*sh1+128);
__m256i w1=ldu256(u11+j),w2=ldu256(u12+j),w3=ldu256(u13+j);
__m256i x0=ldu256(p),x1=vshoup(ldu256(p+sh1),w1,ldu256(s11+j),pv),x2=vshoup(ldu256(p+2*sh1),w2,ldu256(s12+j),pv),x3=vshoup(ldu256(p+3*sh1),w3,ldu256(s13+j),pv);
__m256i A=vaddm(x0,x2,pv),B=vsubm(x0,x2,pv),C=vaddm(x1,x3,pv),D=vsubm(x1,x3,pv),E=vshoup(D,ci,cis,pv);
stu256(z[r][0]+t,vaddm(A,C,pv));stu256(z[r][1]+t,vaddm(B,E,pv));stu256(z[r][2]+t,vsubm(A,C,pv));stu256(z[r][3]+t,vsubm(B,E,pv));
}
for(u32 k=0;k<4;k++)for(u32 t=0;t<INV44_TILE;t+=8){
u32 j=k*h1+j0+t;u32 *p=a+padpos(j);__builtin_prefetch(p+0*sh0+256,1,3);__builtin_prefetch(p+1*sh0+288,1,3);__builtin_prefetch(p+2*sh0+320,1,3);__builtin_prefetch(p+3*sh0+352,1,3);__builtin_prefetch(u01+j+128);__builtin_prefetch(u02+j+128);__builtin_prefetch(u03+j+128);
__m256i w1=ldu256(u01+j),w2=ldu256(u02+j),w3=ldu256(u03+j);
__m256i x0=ldu256(z[0][k]+t),x1=mont_prepared(ldu256(z[1][k]+t),w1,pv),x2=mont_prepared(ldu256(z[2][k]+t),w2,pv),x3=mont_prepared(ldu256(z[3][k]+t),w3,pv);
__m256i A=vaddm(x0,x2,pv),B=vsubm(x0,x2,pv),C=vaddm(x1,x3,pv),D=vsubm(x1,x3,pv),E=vshoup(D,ci,cis,pv);
stu256(p,vaddm(A,C,pv));stu256(p+sh0,vaddm(B,E,pv));stu256(p+2*sh0,vsubm(A,C,pv));stu256(p+3*sh0,vsubm(B,E,pv));
}
}
}
TGT static void ntt_inv(u32 *a, const u32 *Bmul = 0) {
for (u32 b = 0; b < g_n; b += g_Sb) {
u32 *p = a + padpos(b);
if (Bmul && !E7Z3_PM_ON()) {
const __m256i pv = _mm256_set1_epi32((int)NTP);
const u32 *q = Bmul + padpos(b);
for (u32 i = 0; i < g_Sb; i += 8) {
__m256i x = ldu256((p + i));
__m256i y = ldu256((q + i));
stu256((p + i), vshoup(x, y, der_ws(y), pv));
}
}
for (int s = 0; s < g_nb; s++) {
u32 S = 4u << (2 * s);
if (S >= 32) {
for (u32 i = 0; i < g_Sb; i += S)
{ u32 *pp = p + i;
switch (S) {
case 16384: merge4_dit_h<4096>(pp, 16384, 4096, g_TI.h1[s], g_TI.h2[s], g_TI.h3[s], g_TI.q1[s], g_TI.q2[s], g_TI.q3[s], g_ciI, g_cisI); break;
case 4096: merge4_dit_h<1024>(pp, 4096, 1024, g_TI.h1[s], g_TI.h2[s], g_TI.h3[s], g_TI.q1[s], g_TI.q2[s], g_TI.q3[s], g_ciI, g_cisI); break;
case 1024: merge4_dit_h<256>(pp, 1024, 256, g_TI.h1[s], g_TI.h2[s], g_TI.h3[s], g_TI.q1[s], g_TI.q2[s], g_TI.q3[s], g_ciI, g_cisI); break;
case 256: merge4_dit_h<64>(pp, 256, 64, g_TI.h1[s], g_TI.h2[s], g_TI.h3[s], g_TI.q1[s], g_TI.q2[s], g_TI.q3[s], g_ciI, g_cisI); break;
case 64: merge4_dit_h<16>(pp, 64, 16, g_TI.h1[s], g_TI.h2[s], g_TI.h3[s], g_TI.q1[s], g_TI.q2[s], g_TI.q3[s], g_ciI, g_cisI); break;
default: merge4_dit_t(pp, S, S >> 2, g_TI.h1[s], g_TI.h2[s], g_TI.h3[s], g_TI.q1[s], g_TI.q2[s], g_TI.q3[s], g_ciI, g_cisI);
} }
} else if (S == 16) {
merge4_dit_h4(p, g_Sb, g_TI.h1[s], g_TI.h2[s], g_TI.h3[s], g_TI.q1[s], g_TI.q2[s], g_TI.q3[s], g_ciI, g_cisI);
} else {
merge4_dit_h1(p, g_Sb, g_ciI, g_cisI);
}
}
if (g_odd) merge2_dit(p, g_Sb, g_TI.b2, g_TI.b2s);
if (E7Z_IVFUSE && g_L == 4) {
if ((b + g_Sb) % (g_n >> 6) == 0) { u32 SB = g_n >> 6, h = SB >> 2;
merge4_dit_s3(a + padpos(b + g_Sb - SB), SB, h, h + TOPPAD, g_TI.l1[3], g_TI.l2[3], g_TI.l3[3], g_TI.m1[3], g_TI.m2[3], g_TI.m3[3], g_ciI, g_cisI); }
if ((b + g_Sb) % (g_n >> 4) == 0) { u32 SB = g_n >> 4, h = SB >> 2;
merge4_dit_s2(a + padpos(b + g_Sb - SB), SB, h, h + 5 * TOPPAD, g_TI.l1[2], g_TI.l2[2], g_TI.l3[2], g_TI.m1[2], g_TI.m2[2], g_TI.m3[2], g_ciI, g_cisI); }
}
}
for (int l = g_L - 1; l >= 2; l--) {
if (E7Z_IVFUSE && g_L == 4 && (l == 2 || l == 3)) { _mm_sfence(); continue; }
u32 SB = g_n >> (2 * l), h = SB >> 2;
if(l==2) for(u32 b=0;b<g_n;b+=SB) merge4_dit_s2(a+padpos(b),SB,h,h+5*TOPPAD,g_TI.l1[l],g_TI.l2[l],g_TI.l3[l],g_TI.m1[l],g_TI.m2[l],g_TI.m3[l],g_ciI,g_cisI);
else if(l==3) for(u32 b=0;b<g_n;b+=SB) merge4_dit_s3(a+padpos(b),SB,h,h+TOPPAD,g_TI.l1[l],g_TI.l2[l],g_TI.l3[l],g_TI.m1[l],g_TI.m2[l],g_TI.m3[l],g_ciI,g_cisI);
else for(u32 b=0;b<g_n;b+=SB) merge4_dit(a+padpos(b),SB,h,g_TI.l1[l],g_TI.l2[l],g_TI.l3[l],g_TI.m1[l],g_TI.m2[l],g_TI.m3[l],g_ciI,g_cisI);
_mm_sfence();
}
fused_inv44(a);
}
static u32 g_M5, g_M5L;
static u32 *g_outC = 0;
static u32 g_ninvS = 0, g_limS = 0;
static int g_fuse = 0, g_nt = 0;
static u32 *g_t5[4], *g_u5[4];
static u32 g_c5a, g_c5b, g_c5c, g_c5d, g_c5ci, g_c5di, g_inv2;
static u32 g_c5k, g_c5dc, g_c5sc, g_c5dci, g_c5sci;
static u32 g_c5a_s, g_c5b_s, g_c5c_s, g_c5d_s, g_c5ci_s, g_c5di_s, g_inv2_s;
static u32 g_c5k_s, g_c5dc_s, g_c5sc_s, g_c5dci_s, g_c5sci_s;
static void init_r5(u32 N) {
u32 M = N / 5;
g_M5 = M + 448 * TOPPAD + 896;
g_M5L = M;
u32 w = powmod(31u, (NTP - 1) / N);
u32 wi = powmod(w, NTP - 2);
u32 rho = powmod(w, M);
u32 rho2 = mulmod(rho, rho), rho3 = mulmod(rho2, rho), rho4 = mulmod(rho3, rho);
g_c5a = (u32)(((u64)rho + rho4) % NTP);
g_c5b = (u32)(((u64)rho2 + rho3) % NTP);
g_c5c = (u32)(((u64)rho + NTP - rho4) % NTP);
g_c5d = (u32)(((u64)rho2 + NTP - rho3) % NTP);
g_c5ci = (u32)((NTP - (u64)g_c5c) % NTP);
g_c5di = (u32)((NTP - (u64)g_c5d) % NTP);
g_inv2 = (u32)(((u64)NTP + 1) / 2);
g_c5a = mulmod(g_c5a, g_inv2); g_c5b = mulmod(g_c5b, g_inv2);
g_c5c = mulmod(g_c5c, g_inv2); g_c5d = mulmod(g_c5d, g_inv2);
g_c5ci = mulmod(g_c5ci, g_inv2); g_c5di = mulmod(g_c5di, g_inv2);
g_c5k = mulmod((g_c5a + NTP - g_c5b) % NTP, g_inv2);
g_c5dc = (g_c5d + NTP - g_c5c) % NTP;
g_c5sc = (g_c5c + g_c5d) % NTP;
g_c5dci = (g_c5di + NTP - g_c5ci) % NTP;
g_c5sci = (g_c5ci + g_c5di) % NTP;
g_c5k_s = shoup_c(g_c5k); g_c5dc_s = shoup_c(g_c5dc); g_c5sc_s = shoup_c(g_c5sc);
g_c5dci_s = shoup_c(g_c5dci); g_c5sci_s = shoup_c(g_c5sci);
g_c5a_s = shoup_c(g_c5a); g_c5b_s = shoup_c(g_c5b);
g_c5c_s = shoup_c(g_c5c); g_c5d_s = shoup_c(g_c5d);
g_c5ci_s = shoup_c(g_c5ci); g_c5di_s = shoup_c(g_c5di);
g_inv2_s = shoup_c(g_inv2);
u32 *pf = g_tabF + (5 * NTT_MAXN / 2 + 4096) - 4 * M;
u32 *pi = g_tabI + (5 * NTT_MAXN / 2 + 4096) - 4 * M;
for (int q = 0; q < 4; q++) { g_t5[q] = pf + (size_t)q * M; g_u5[q] = pi + (size_t)q * M; }
gen_geo_val(g_t5[0], M, (u32)((1ull<<32)%NTP), w);
gen_geo_val(g_u5[0], M, (u32)((1ull<<32)%NTP), wi);
for (int q = 1; q < 4; q++) { g_t5[q] = g_t5[0]; g_u5[q] = g_u5[0]; }
}
TGT static inline void dft5v(__m256i x0, __m256i x1, __m256i x2, __m256i x3, __m256i x4,
__m256i &Y0, __m256i &Y1, __m256i &Y2, __m256i &Y3, __m256i &Y4,
__m256i pv, __m256i cK, __m256i cKs, __m256i cC, __m256i cCs,
__m256i cDiff, __m256i cDiffS, __m256i cSum, __m256i cSumS,
__m256i iv2, __m256i iv2s) {
(void)iv2; (void)iv2s;
__m256i P1 = vaddm(x1, x4, pv), M1 = vsubm(x1, x4, pv);
__m256i P2 = vaddm(x2, x3, pv), M2 = vsubm(x2, x3, pv);
__m256i S = vaddm(P1, P2, pv), D = vsubm(P1, P2, pv);
const __m256i negQuarterBase = _mm256_setr_epi32(2013265921, 503316480, 1006632960, 1509949440,
2013265921, 503316480, 1006632960, 1509949440);
__m256i rem4 = _mm256_and_si256(S, _mm256_set1_epi32(3));
__m256i U = _mm256_sub_epi32(_mm256_permutevar8x32_epi32(negQuarterBase, rem4),
_mm256_srli_epi32(S, 2));
U = _mm256_min_epu32(U, _mm256_sub_epi32(U, pv));
__m256i V = vshoup(D, cK, cKs, pv);
__m256i A = vaddm(U, V, pv), B = vsubm(U, V, pv);
__m256i T1 = vshoup(vaddm(M1, M2, pv), cC, cCs, pv);
__m256i T2 = vshoup(M2, cDiff, cDiffS, pv);
__m256i T3 = vshoup(M1, cSum, cSumS, pv);
__m256i R = vaddm(T1, T2, pv), I = vsubm(T3, T1, pv);
Y0 = vaddm(x0, S, pv);
Y1 = vaddm(x0, vaddm(A, R, pv), pv);
Y2 = vaddm(x0, vaddm(B, I, pv), pv);
Y3 = vaddm(x0, vsubm(B, I, pv), pv);
Y4 = vaddm(x0, vsubm(A, R, pv), pv);
}
TGT static inline void dft5v3(__m256i x0,__m256i x1,__m256i x2,__m256i, __m256i,__m256i &Y0,__m256i &Y1,__m256i &Y2,__m256i &Y3,__m256i &Y4,__m256i p,__m256i K,__m256i Ks,__m256i C,__m256i Cs,__m256i Dif,__m256i Difs,__m256i Sum,__m256i Sums,__m256i,__m256i){
__m256i S=vaddm(x1,x2,p),D=vsubm(x1,x2,p);
const __m256i tab=_mm256_setr_epi32(2013265921,503316480,1006632960,1509949440,2013265921,503316480,1006632960,1509949440);
__m256i U=_mm256_sub_epi32(_mm256_permutevar8x32_epi32(tab,_mm256_and_si256(S,_mm256_set1_epi32(3))),_mm256_srli_epi32(S,2));U=_mm256_min_epu32(U,_mm256_sub_epi32(U,p));
__m256i V=vshoup(D,K,Ks,p),A=vaddm(U,V,p),B=vsubm(U,V,p);
__m256i T=vshoup(S,C,Cs,p),R=vaddm(T,vshoup(x2,Dif,Difs,p),p),I=vsubm(vshoup(x1,Sum,Sums,p),T,p);
Y0=vaddm(x0,S,p);Y1=vaddm(x0,vaddm(A,R,p),p);Y2=vaddm(x0,vaddm(B,I,p),p);Y3=vaddm(x0,vsubm(B,I,p),p);Y4=vaddm(x0,vsubm(A,R,p),p);
}
TGT static inline void dft5v2(__m256i a,__m256i b,__m256i &z0,__m256i &z1,__m256i &z2,__m256i &z3,__m256i &z4,__m256i p){
z0=vaddm(a,b,p);
z1=vaddm(a,vshoup(b,_mm256_set1_epi32(645581151),_mm256_set1_epi32(1377239788),p),p);
z2=vaddm(a,vshoup(b,_mm256_set1_epi32(1956349769),_mm256_set1_epi32((int)4173546171u),p),p);
z3=vaddm(a,vshoup(b,_mm256_set1_epi32(609564788),_mm256_set1_epi32(1300404880),p),p);
z4=vaddm(a,vshoup(b,_mm256_set1_epi32(815036133),_mm256_set1_epi32(1738743749),p),p);
}
TGT static inline void stnfwd(void *p,__m256i v){_mm256_stream_si256((__m256i*)p,v);}
#define ST5_FWD2(a, j, Ya0, Ya1, Ya2, Ya3, Ya4, Yb0, Yb1, Yb2, Yb3, Yb4) \
{ stnfwd(((a) + (j)), (Ya0)); stnfwd(((a) + (j) + 8), (Yb0)); \
__m256i w1 = ldu256((tw0 + (j))); __m256i s1 = der_ws(w1); \
__m256i w1b = ldu256((tw0 + (j) + 8)); __m256i s1b = der_ws(w1b); \
__m256i w2 = mont_prepared(w1, w1, pv5); __m256i w2b = mont_prepared(w1b, w1b, pv5); \
__m256i w3 = mont_prepared(w2, w1, pv5); __m256i w3b = mont_prepared(w2b, w1b, pv5); \
__m256i w4 = mont_prepared(w2, w2, pv5); __m256i w4b = mont_prepared(w2b, w2b, pv5); \
stnfwd(((a) + (j) + (size_t)strideM5), mont_prepared((Ya1), w1, pv5)); \
stnfwd(((a) + (j) + 8 + (size_t)strideM5), mont_prepared((Yb1), w1b, pv5)); \
stnfwd(((a) + (j) + (size_t)2 * strideM5), mont_prepared((Ya2), w2, pv5)); \
stnfwd(((a) + (j) + 8 + (size_t)2 * strideM5), mont_prepared((Yb2), w2b, pv5)); \
stnfwd(((a) + (j) + (size_t)3 * strideM5), mont_prepared((Ya3), w3, pv5)); \
stnfwd(((a) + (j) + 8 + (size_t)3 * strideM5), mont_prepared((Yb3), w3b, pv5)); \
stnfwd(((a) + (j) + (size_t)4 * strideM5), mont_prepared((Ya4), w4, pv5)); \
stnfwd(((a) + (j) + 8 + (size_t)4 * strideM5), mont_prepared((Yb4), w4b, pv5)); \
}
#define ST5_FWD(a, j, Y0, Y1, Y2, Y3, Y4) \
{ stnfwd(((a) + (j)), (Y0)); \
__m256i w1 = ldu256((tw0 + (j))); \
__m256i s1 = der_ws(w1); \
__m256i w2 = mont_prepared(w1, w1, pv5); \
__m256i w3 = mont_prepared(w2, w1, pv5); \
__m256i w4 = mont_prepared(w2, w2, pv5); \
stnfwd(((a) + (j) + (size_t)strideM5), mont_prepared((Y1), w1, pv5)); \
stnfwd(((a) + (j) + (size_t)2 * strideM5), mont_prepared((Y2), w2, pv5)); \
stnfwd(((a) + (j) + (size_t)3 * strideM5), mont_prepared((Y3), w3, pv5)); \
stnfwd(((a) + (j) + (size_t)4 * strideM5), mont_prepared((Y4), w4, pv5)); \
}
template<bool SCALE> static inline void st5_scalar(u32 *a, const u32 x[5], u32 u, u32 M) {
u32 scaled[5];
const u32 *input = x;
if constexpr (SCALE) {
for (int q = 0; q < 5; q++) scaled[q] = g_scale_digit[x[q]];
input = scaled;
}
u32 P1 = (u32)(((u64)input[1] + input[4]) % NTP), M1 = (u32)(((u64)input[1] + NTP - input[4]) % NTP);
u32 P2 = (u32)(((u64)input[2] + input[3]) % NTP), M2 = (u32)(((u64)input[2] + NTP - input[3]) % NTP);
u32 aP1 = shoup1s(P1, g_c5a, g_c5a_s), cM1 = shoup1s(M1, g_c5c, g_c5c_s);
u32 bP2 = shoup1s(P2, g_c5b, g_c5b_s), dM2 = shoup1s(M2, g_c5d, g_c5d_s);
u32 bP1 = shoup1s(P1, g_c5b, g_c5b_s), dM1 = shoup1s(M1, g_c5d, g_c5d_s);
u32 aP2 = shoup1s(P2, g_c5a, g_c5a_s), cM2 = shoup1s(M2, g_c5c, g_c5c_s);
u32 Y[5];
Y[0] = (u32)(((u64)input[0] + P1 + P2) % NTP);
Y[1] = (u32)(((u64)input[0] + (u32)(((u64)aP1 + cM1 + bP2 + dM2) % NTP)) % NTP);
Y[2] = (u32)(((u64)input[0] + (u32)(((u64)bP1 + dM1 + aP2 + NTP - cM2) % NTP)) % NTP);
Y[3] = (u32)(((u64)input[0] + (u32)(((u64)bP1 + NTP - dM1 + aP2 + cM2) % NTP)) % NTP);
Y[4] = (u32)(((u64)input[0] + (u32)(((u64)aP1 + NTP - cM1 + bP2 + NTP - dM2) % NTP)) % NTP);
a[u] = Y[0];
{ u32 w1 = mulmod(g_t5[0][u], 943718400u), w2 = mulmod(w1, w1), w3 = mulmod(w2, w1), w4 = mulmod(w3, w1);
a[u + (size_t)g_M5] = shoup1s(Y[1], w1, shoup_c(w1));
a[u + (size_t)2 * g_M5] = shoup1s(Y[2], w2, shoup_c(w2));
a[u + (size_t)3 * g_M5] = shoup1s(Y[3], w3, shoup_c(w3));
a[u + (size_t)4 * g_M5] = shoup1s(Y[4], w4, shoup_c(w4)); }
}
template<bool SCALE> TGT static void stage5_dif_src(u32 *a, const u32 *src, u32 len, u32 M) {
const u32 *const tw0 = g_t5[0];
const u32 strideM5 = g_M5;
u32 *bp = a; u32 nxt0 = 1u << 20; u32 nxt1 = 1u << 18; u32 nxt2 = 1u << 16; u32 nxt3 = 1u << 14;
const __m256i pv5 = _mm256_set1_epi32((int)NTP);
const __m256i cA = _mm256_set1_epi32((int)g_c5a), cAs = _mm256_set1_epi32((int)g_c5a_s);
const __m256i cB = _mm256_set1_epi32((int)g_c5b), cBs = _mm256_set1_epi32((int)g_c5b_s);
const __m256i cC = _mm256_set1_epi32((int)g_c5c), cCs = _mm256_set1_epi32((int)g_c5c_s);
const __m256i cD = _mm256_set1_epi32((int)g_c5d), cDs = _mm256_set1_epi32((int)g_c5d_s);
const __m256i cK = _mm256_set1_epi32((int)g_c5k), cKs = _mm256_set1_epi32((int)g_c5k_s);
const __m256i cDiff = _mm256_set1_epi32((int)g_c5dc), cDiffS = _mm256_set1_epi32((int)g_c5dc_s);
const __m256i cSum = _mm256_set1_epi32((int)g_c5sc), cSumS = _mm256_set1_epi32((int)g_c5sc_s);
const __m256i iv2 = _mm256_set1_epi32((int)g_inv2), iv2s = _mm256_set1_epi32((int)g_inv2_s);
const __m256i zz = _mm256_setzero_si256();
u32 l4 = (len >= 4 * M) ? (len - 4 * M + 1) : 0; if (l4 > M) l4 = M;
u32 l3 = (len >= 3 * M) ? (len - 3 * M + 1) : 0; if (l3 > M) l3 = M;
u32 l2 = (len >= 2 * M) ? (len - 2 * M + 1) : 0; if (l2 > M) l2 = M;
u32 l1 = (len >= M) ? (len - M + 1) : 0; if (l1 > M) l1 = M;
u32 j = 0;
for (; (j & 7) && j < l4; j++) { PADJUMP() u32 xx[5]; for (int t = 0; t < 5; t++) xx[t] = src[j + (size_t)t * M]; st5_scalar<SCALE>(bp, xx, j, M); }
for (; j + 8 <= l4; j += 8) { PADJUMP()
pf5(src + j + PF_DIST); pf5(src + j + M + PF_DIST); pf5(src + j + 2 * M + PF_DIST);
pf5(src + j + 3 * M + PF_DIST); pf5(src + j + 4 * M + PF_DIST);
pf5(tw0 + j + PF_DIST);
__m256i x0 = lds256<SCALE>((src + j));
__m256i x1 = lds256<SCALE>((src + j + M));
__m256i x2 = lds256<SCALE>((src + j + 2 * M));
__m256i x3 = lds256<SCALE>((src + j + 3 * M));
__m256i x4 = lds256<SCALE>((src + j + 4 * M));
__m256i Y0, Y1, Y2, Y3, Y4;
dft5v(x0, x1, x2, x3, x4, Y0, Y1, Y2, Y3, Y4, pv5, cK, cKs, cC, cCs, cDiff, cDiffS, cSum, cSumS, iv2, iv2s);
ST5_FWD(bp, j, Y0, Y1, Y2, Y3, Y4);
}
for (; j < l4; j++) { PADJUMP() u32 xx[5]; for (int t = 0; t < 5; t++) xx[t] = src[j + (size_t)t * M]; st5_scalar<SCALE>(bp, xx, j, M); }
for (; (j & 7) && j < l3; j++) { PADJUMP() u32 xx[5]; for (int t = 0; t < 5; t++) xx[t] = (t < 4 && j + (size_t)t * M <= len) ? src[j + (size_t)t * M] : 0; st5_scalar<SCALE>(bp, xx, j, M); }
for (; j + 8 <= l3; j += 8) { PADJUMP()
__m256i x0 = lds256<SCALE>((src + j));
__m256i x1 = lds256<SCALE>((src + j + M));
__m256i x2 = lds256<SCALE>((src + j + 2 * M));
__m256i x3 = lds256<SCALE>((src + j + 3 * M));
__m256i Y0, Y1, Y2, Y3, Y4;
dft5v(x0, x1, x2, x3, zz, Y0, Y1, Y2, Y3, Y4, pv5, cK, cKs, cC, cCs, cDiff, cDiffS, cSum, cSumS, iv2, iv2s);
ST5_FWD(bp, j, Y0, Y1, Y2, Y3, Y4);
}
for (; j < l3; j++) { PADJUMP() u32 xx[5]; for (int t = 0; t < 5; t++) xx[t] = (t < 4 && j + (size_t)t * M <= len) ? src[j + (size_t)t * M] : 0; st5_scalar<SCALE>(bp, xx, j, M); }
for (; (j & 7) && j < l2; j++) { PADJUMP() u32 xx[5]; for (int t = 0; t < 5; t++) xx[t] = (t < 3 && j + (size_t)t * M <= len) ? src[j + (size_t)t * M] : 0; st5_scalar<SCALE>(bp, xx, j, M); }
for (; (j & 15) && j + 8 <= l2; j += 8) { PADJUMP()
__m256i x0 = lds256<SCALE>((src + j));
__m256i x1 = lds256<SCALE>((src + j + M));
__m256i x2 = lds256<SCALE>((src + j + 2 * M));
__m256i Y0, Y1, Y2, Y3, Y4;
dft5v3(x0, x1, x2, zz, zz, Y0, Y1, Y2, Y3, Y4, pv5, cK, cKs, cC, cCs, cDiff, cDiffS, cSum, cSumS, iv2, iv2s);
ST5_FWD(bp, j, Y0, Y1, Y2, Y3, Y4);
}
for (; j + 16 <= l2; j += 16) { PADJUMP()
__m256i x0 = lds256<SCALE>((src + j));
__m256i x1 = lds256<SCALE>((src + j + M));
__m256i x2 = lds256<SCALE>((src + j + 2 * M));
__m256i y0 = lds256<SCALE>((src + j + 8));
__m256i y1 = lds256<SCALE>((src + j + M + 8));
__m256i y2 = lds256<SCALE>((src + j + 2 * M + 8));
__m256i Y0, Y1, Y2, Y3, Y4, Z0, Z1, Z2, Z3, Z4;
dft5v3(x0, x1, x2, zz, zz, Y0, Y1, Y2, Y3, Y4, pv5, cK, cKs, cC, cCs, cDiff, cDiffS, cSum, cSumS, iv2, iv2s);
dft5v3(y0, y1, y2, zz, zz, Z0, Z1, Z2, Z3, Z4, pv5, cK, cKs, cC, cCs, cDiff, cDiffS, cSum, cSumS, iv2, iv2s);
ST5_FWD2(bp, j, Y0, Y1, Y2, Y3, Y4, Z0, Z1, Z2, Z3, Z4);
}
for (; j + 8 <= l2; j += 8) { PADJUMP()
__m256i x0 = lds256<SCALE>((src + j));
__m256i x1 = lds256<SCALE>((src + j + M));
__m256i x2 = lds256<SCALE>((src + j + 2 * M));
__m256i Y0, Y1, Y2, Y3, Y4;
dft5v3(x0, x1, x2, zz, zz, Y0, Y1, Y2, Y3, Y4, pv5, cK, cKs, cC, cCs, cDiff, cDiffS, cSum, cSumS, iv2, iv2s);
ST5_FWD(bp, j, Y0, Y1, Y2, Y3, Y4);
}
for (; j < l2; j++) { PADJUMP() u32 xx[5]; for (int t = 0; t < 5; t++) xx[t] = (t < 3 && j + (size_t)t * M <= len) ? src[j + (size_t)t * M] : 0; st5_scalar<SCALE>(bp, xx, j, M); }
for (; (j & 7) && j < l1; j++) { PADJUMP() u32 xx[5]; for (int t = 0; t < 5; t++) xx[t] = (t < 2 && j + (size_t)t * M <= len) ? src[j + (size_t)t * M] : 0; st5_scalar<SCALE>(bp, xx, j, M); }
for (; (j & 15) && j + 8 <= l1; j += 8) { PADJUMP()
__m256i x0 = lds256<SCALE>((src + j));
__m256i x1 = lds256<SCALE>((src + j + M));
__m256i Y0, Y1, Y2, Y3, Y4;
dft5v2(x0, x1, Y0, Y1, Y2, Y3, Y4, pv5);
ST5_FWD(bp, j, Y0, Y1, Y2, Y3, Y4);
}
for (; j + 16 <= l1; j += 16) { PADJUMP()
__m256i x0 = lds256<SCALE>((src + j));
__m256i x1 = lds256<SCALE>((src + j + M));
__m256i y0 = lds256<SCALE>((src + j + 8));
__m256i y1 = lds256<SCALE>((src + j + M + 8));
__m256i Y0, Y1, Y2, Y3, Y4, Z0, Z1, Z2, Z3, Z4;
dft5v2(x0, x1, Y0, Y1, Y2, Y3, Y4, pv5);
dft5v2(y0, y1, Z0, Z1, Z2, Z3, Z4, pv5);
ST5_FWD2(bp, j, Y0, Y1, Y2, Y3, Y4, Z0, Z1, Z2, Z3, Z4);
}
for (; j + 8 <= l1; j += 8) { PADJUMP()
__m256i x0 = lds256<SCALE>((src + j));
__m256i x1 = lds256<SCALE>((src + j + M));
__m256i Y0, Y1, Y2, Y3, Y4;
dft5v2(x0, x1, Y0, Y1, Y2, Y3, Y4, pv5);
ST5_FWD(bp, j, Y0, Y1, Y2, Y3, Y4);
}
for (; j < l1; j++) { PADJUMP() u32 xx[5]; for (int t = 0; t < 5; t++) xx[t] = (t < 2 && j + (size_t)t * M <= len) ? src[j + (size_t)t * M] : 0; st5_scalar<SCALE>(bp, xx, j, M); }
for (; (j & 7) && j < M; j++) { PADJUMP()
u32 x0 = (j <= len) ? src[j] : 0;
bp[j] = x0;
u32 w1 = mulmod(g_t5[0][j], 943718400u), w2 = mulmod(w1, w1), w3 = mulmod(w2, w1), w4 = mulmod(w3, w1);
bp[j + (size_t)g_M5] = shoup1s(x0, w1, shoup_c(w1));
bp[j + (size_t)2 * g_M5] = shoup1s(x0, w2, shoup_c(w2));
bp[j + (size_t)3 * g_M5] = shoup1s(x0, w3, shoup_c(w3));
bp[j + (size_t)4 * g_M5] = shoup1s(x0, w4, shoup_c(w4));
}
for (; j + 8 <= M; j += 8) { PADJUMP()
__m256i x0 = (j + 8 <= len + 1) ? lds256<SCALE>((src + j)) : zz;
stnfwd((bp + j), x0);
{
__m256i w1 = ldu256((g_t5[0] + j));
__m256i s1 = der_ws(w1);
__m256i w2 = mont_prepared(w1, w1, pv5), w3 = mont_prepared(w2, w1, pv5), w4 = mont_prepared(w2, w2, pv5);
stnfwd((bp + j + (size_t)g_M5), mont_prepared(x0, w1, pv5));
stnfwd((bp + j + (size_t)2 * g_M5), mont_prepared(x0, w2, pv5));
stnfwd((bp + j + (size_t)3 * g_M5), mont_prepared(x0, w3, pv5));
stnfwd((bp + j + (size_t)4 * g_M5), mont_prepared(x0, w4, pv5));
}
}
for (; j < M; j++) { PADJUMP()
u32 x0 = (j <= len) ? src[j] : 0;
bp[j] = x0;
u32 w1 = mulmod(g_t5[0][j], 943718400u), w2 = mulmod(w1, w1), w3 = mulmod(w2, w1), w4 = mulmod(w3, w1);
bp[j + (size_t)g_M5] = shoup1s(x0, w1, shoup_c(w1));
bp[j + (size_t)2 * g_M5] = shoup1s(x0, w2, shoup_c(w2));
bp[j + (size_t)3 * g_M5] = shoup1s(x0, w3, shoup_c(w3));
bp[j + (size_t)4 * g_M5] = shoup1s(x0, w4, shoup_c(w4));
}
}
TGT static void merge5_dit(u32 *a, u32 M) {
const u32 strideM5 = g_M5;
const u32 *const tw0 = g_u5[0];
const int fuse = 1, nt = g_nt;
u32 *const outC = g_outC;
const u32 limit = g_limS;
u32 *bp = a; u32 nxt0 = 1u << 20; u32 nxt1 = 1u << 18; u32 nxt2 = 1u << 16; u32 nxt3 = 1u << 14;
const __m256i pv5 = _mm256_set1_epi32((int)NTP);
const u32 final_scale = g_ninvS;
const u32 final_scale_shoup = shoup_c(final_scale);
const __m256i nv = _mm256_set1_epi32((int)final_scale);
const __m256i ns = _mm256_set1_epi32((int)final_scale_shoup);
const __m256i cA = _mm256_set1_epi32((int)g_c5a), cAs = _mm256_set1_epi32((int)g_c5a_s);
const __m256i cB = _mm256_set1_epi32((int)g_c5b), cBs = _mm256_set1_epi32((int)g_c5b_s);
const __m256i cCi = _mm256_set1_epi32((int)g_c5ci), cCis = _mm256_set1_epi32((int)g_c5ci_s);
const __m256i cDi = _mm256_set1_epi32((int)g_c5di), cDis = _mm256_set1_epi32((int)g_c5di_s);
const __m256i cK = _mm256_set1_epi32((int)g_c5k), cKs = _mm256_set1_epi32((int)g_c5k_s);
const __m256i cDiffI = _mm256_set1_epi32((int)g_c5dci), cDiffIs = _mm256_set1_epi32((int)g_c5dci_s);
const __m256i cSumI = _mm256_set1_epi32((int)g_c5sci), cSumIs = _mm256_set1_epi32((int)g_c5sci_s);
const __m256i iv2 = _mm256_set1_epi32((int)g_inv2), iv2s = _mm256_set1_epi32((int)g_inv2_s);
__m128i carry5[5];
const int shiftnt = fuse && !nt && (((uintptr_t)outC & 31) == 16);
u32 s = 0;
for (; s + 16 <= M; s += 16) { PADJUMP_S()
pf5(bp + s + PF_DIST); pf5(bp + s + (size_t)strideM5 + PF_DIST); pf5(bp + s + (size_t)2 * strideM5 + PF_DIST);
pf5(bp + s + (size_t)3 * strideM5 + PF_DIST); pf5(bp + s + (size_t)4 * strideM5 + PF_DIST);
pf5(tw0 + s + PF_DIST);
__m256i ta[5], tb[5];
ta[0] = ldu256((bp + s)); tb[0] = ldu256((bp + s + 8));
{
__m256i w1 = ldu256((tw0 + s));
__m256i s1 = der_ws(w1);
__m256i w2 = mont_prepared(w1, w1, pv5), w3 = mont_prepared(w2, w1, pv5), w4 = mont_prepared(w2, w2, pv5);
__m256i w1b = ldu256((tw0 + s + 8));
__m256i s1b = der_ws(w1b);
__m256i w2b = mont_prepared(w1b, w1b, pv5), w3b = mont_prepared(w2b, w1b, pv5), w4b = mont_prepared(w2b, w2b, pv5);
ta[1] = mont_prepared(ldu256((bp + s + (size_t)strideM5)), w1, pv5);
tb[1] = mont_prepared(ldu256((bp + s + 8 + (size_t)strideM5)), w1b, pv5);
ta[2] = mont_prepared(ldu256((bp + s + (size_t)2 * strideM5)), w2, pv5);
tb[2] = mont_prepared(ldu256((bp + s + 8 + (size_t)2 * strideM5)), w2b, pv5);
ta[3] = mont_prepared(ldu256((bp + s + (size_t)3 * strideM5)), w3, pv5);
tb[3] = mont_prepared(ldu256((bp + s + 8 + (size_t)3 * strideM5)), w3b, pv5);
ta[4] = mont_prepared(ldu256((bp + s + (size_t)4 * strideM5)), w4, pv5);
tb[4] = mont_prepared(ldu256((bp + s + 8 + (size_t)4 * strideM5)), w4b, pv5);
}
__m256i Y0, Y1, Y2, Y3, Y4, Z0, Z1, Z2, Z3, Z4;
dft5v(ta[0], ta[1], ta[2], ta[3], ta[4], Y0, Y1, Y2, Y3, Y4, pv5, cK, cKs, cCi, cCis, cDiffI, cDiffIs, cSumI, cSumIs, iv2, iv2s);
dft5v(tb[0], tb[1], tb[2], tb[3], tb[4], Z0, Z1, Z2, Z3, Z4, pv5, cK, cKs, cCi, cCis, cDiffI, cDiffIs, cSumI, cSumIs, iv2, iv2s);
if (fuse) {
u32 *C = outC + s;
if (nt) {
_mm256_stream_si256((__m256i *)(C), Y0); _mm256_stream_si256((__m256i *)(C + 8), Z0);
_mm256_stream_si256((__m256i *)(C + M), Y1); _mm256_stream_si256((__m256i *)(C + M + 8), Z1);
_mm256_stream_si256((__m256i *)(C + 2 * M), Y2); _mm256_stream_si256((__m256i *)(C + 2 * M + 8), Z2);
_mm256_stream_si256((__m256i *)(C + 3 * M), Y3); _mm256_stream_si256((__m256i *)(C + 3 * M + 8), Z3);
if (s + 16 <= limit - 4 * M) { _mm256_stream_si256((__m256i *)(C + 4 * M), Y4); _mm256_stream_si256((__m256i *)(C + 4 * M + 8), Z4); }
else if (s < limit - 4 * M) {
u32 tail[16]; stu256(tail, Y4); stu256(tail + 8, Z4);
for (u32 k = 0; k < limit - 4 * M - s; ++k) C[4 * M + k] = tail[k];
}
} else if (shiftnt) {
#define SH5(q,Y,Z) { u32 *D=C+(q)*M; if(s) _mm256_stream_si256((__m256i*)(D-4),_mm256_inserti128_si256(_mm256_castsi128_si256(carry5[q]),_mm256_castsi256_si128(Y),1)); else _mm_storeu_si128((__m128i*)D,_mm256_castsi256_si128(Y)); _mm256_stream_si256((__m256i*)(D+4),_mm256_permute2x128_si256(Y,Z,0x21)); carry5[q]=_mm256_extracti128_si256(Z,1); if(s+16==M || ((q)==4 && s+16>=limit-4*M)) _mm_storeu_si128((__m128i*)(D+12),carry5[q]); }
SH5(0,Y0,Z0) SH5(1,Y1,Z1) SH5(2,Y2,Z2) SH5(3,Y3,Z3)
if(s+16<=limit-4*M) { SH5(4,Y4,Z4) }
else if(s<limit-4*M) {
u32 *D = C + 4 * M;
if (s) _mm_storeu_si128((__m128i *)(D - 4), carry5[4]);
u32 tail[16]; stu256(tail, Y4); stu256(tail + 8, Z4);
for (u32 k = 0; k < limit - 4 * M - s; ++k) D[k] = tail[k];
}
#undef SH5
} else {
stu256((C), Y0); stu256((C + 8), Z0);
stu256((C + M), Y1); stu256((C + M + 8), Z1);
stu256((C + 2 * M), Y2); stu256((C + 2 * M + 8), Z2);
stu256((C + 3 * M), Y3); stu256((C + 3 * M + 8), Z3);
if (s + 16 <= limit - 4 * M) { stu256((C + 4 * M), Y4); stu256((C + 4 * M + 8), Z4); }
else if (s < limit - 4 * M) {
u32 tail[16]; stu256(tail, Y4); stu256(tail + 8, Z4);
for (u32 k = 0; k < limit - 4 * M - s; ++k) C[4 * M + k] = tail[k];
}
}
} else {
stu256((bp + s), Y0); stu256((bp + s + 8), Z0);
stu256((bp + s + (size_t)strideM5), Y1); stu256((bp + s + 8 + (size_t)strideM5), Z1);
stu256((bp + s + (size_t)2 * strideM5), Y2); stu256((bp + s + 8 + (size_t)2 * strideM5), Z2);
stu256((bp + s + (size_t)3 * strideM5), Y3); stu256((bp + s + 8 + (size_t)3 * strideM5), Z3);
stu256((bp + s + (size_t)4 * strideM5), Y4); stu256((bp + s + 8 + (size_t)4 * strideM5), Z4);
}
}
for (; s + 8 <= M; s += 8) { PADJUMP_S()
pf5(bp + s + PF_DIST); pf5(bp + s + (size_t)strideM5 + PF_DIST); pf5(bp + s + (size_t)2 * strideM5 + PF_DIST);
pf5(bp + s + (size_t)3 * strideM5 + PF_DIST); pf5(bp + s + (size_t)4 * strideM5 + PF_DIST);
pf5(tw0 + s + PF_DIST);
__m256i t0 = ldu256((bp + s));
__m256i t[5]; t[0] = t0;
{
__m256i w1 = ldu256((tw0 + s));
__m256i s1 = der_ws(w1);
__m256i w2 = mont_prepared(w1, w1, pv5), w3 = mont_prepared(w2, w1, pv5), w4 = mont_prepared(w2, w2, pv5);
t[1] = mont_prepared(ldu256((bp + s + (size_t)strideM5)), w1, pv5);
t[2] = mont_prepared(ldu256((bp + s + (size_t)2 * strideM5)), w2, pv5);
t[3] = mont_prepared(ldu256((bp + s + (size_t)3 * strideM5)), w3, pv5);
t[4] = mont_prepared(ldu256((bp + s + (size_t)4 * strideM5)), w4, pv5);
}
__m256i Y0, Y1, Y2, Y3, Y4;
dft5v(t[0], t[1], t[2], t[3], t[4], Y0, Y1, Y2, Y3, Y4, pv5, cK, cKs, cCi, cCis, cDiffI, cDiffIs, cSumI, cSumIs, iv2, iv2s);
if (fuse) {
u32 *C = outC + s;
if (nt) {
_mm256_stream_si256((__m256i *)(C), Y0);
_mm256_stream_si256((__m256i *)(C + M), Y1);
_mm256_stream_si256((__m256i *)(C + 2 * M), Y2);
_mm256_stream_si256((__m256i *)(C + 3 * M), Y3);
if (s + 8 <= limit - 4 * M) _mm256_stream_si256((__m256i *)(C + 4 * M), Y4);
else if (s < limit - 4 * M) {
u32 tail[8]; stu256(tail, Y4);
for (u32 k = 0; k < limit - 4 * M - s; ++k) C[4 * M + k] = tail[k];
}
} else {
stu256((C), Y0);
stu256((C + M), Y1);
stu256((C + 2 * M), Y2);
stu256((C + 3 * M), Y3);
if (s + 8 <= limit - 4 * M) stu256((C + 4 * M), Y4);
else if (s < limit - 4 * M) {
u32 tail[8]; stu256(tail, Y4);
for (u32 k = 0; k < limit - 4 * M - s; ++k) C[4 * M + k] = tail[k];
}
}
} else {
stu256((bp + s), Y0);
stu256((bp + s + strideM5), Y1);
stu256((bp + s + 2 * strideM5), Y2);
stu256((bp + s + 3 * strideM5), Y3);
stu256((bp + s + 4 * strideM5), Y4);
}
}
for (; s < M; s++) { PADJUMP_S()
u32 t[5]; t[0] = bp[s];
{ u32 w1 = mulmod(tw0[s], 943718400u), w2 = mulmod(w1, w1), w3 = mulmod(w2, w1), w4 = mulmod(w3, w1);
t[1] = shoup1s(bp[s + (size_t)strideM5], w1, shoup_c(w1));
t[2] = shoup1s(bp[s + (size_t)2 * strideM5], w2, shoup_c(w2));
t[3] = shoup1s(bp[s + (size_t)3 * strideM5], w3, shoup_c(w3));
t[4] = shoup1s(bp[s + (size_t)4 * strideM5], w4, shoup_c(w4)); }
u32 P1 = (u32)(((u64)t[1] + t[4]) % NTP), M1 = (u32)(((u64)t[1] + NTP - t[4]) % NTP);
u32 P2 = (u32)(((u64)t[2] + t[3]) % NTP), M2 = (u32)(((u64)t[2] + NTP - t[3]) % NTP);
u32 Y[5];
Y[0] = (u32)(((u64)t[0] + P1 + P2) % NTP);
Y[1] = (u32)(((u64)t[0] + (u32)(((u64)mulmod(g_c5a, P1) + mulmod(g_c5ci, M1)) % NTP) + (u32)(((u64)mulmod(g_c5b, P2) + mulmod(g_c5di, M2)) % NTP)) % NTP);
Y[2] = (u32)(((u64)t[0] + (u32)(((u64)mulmod(g_c5b, P1) + mulmod(g_c5di, M1)) % NTP) + (u32)(((u64)mulmod(g_c5a, P2) + NTP - mulmod(g_c5ci, M2)) % NTP)) % NTP);
Y[3] = (u32)(((u64)t[0] + (u32)(((u64)mulmod(g_c5b, P1) + NTP - mulmod(g_c5di, M1)) % NTP) + (u32)(((u64)mulmod(g_c5a, P2) + mulmod(g_c5ci, M2)) % NTP)) % NTP);
Y[4] = (u32)(((u64)t[0] + (u32)(((u64)mulmod(g_c5a, P1) + NTP - mulmod(g_c5ci, M1)) % NTP) + (u32)(((u64)mulmod(g_c5b, P2) + NTP - mulmod(g_c5di, M2)) % NTP)) % NTP);
if (fuse) {
u32 *C = outC + s;
C[0] = Y[0];
C[M] = Y[1];
C[2 * M] = Y[2];
C[3 * M] = Y[3];
if (s < limit - 4 * M) C[4 * M] = Y[4];
} else {
bp[s] = Y[0];
bp[s + strideM5] = Y[1]; bp[s + 2 * strideM5] = Y[2]; bp[s + 3 * strideM5] = Y[3]; bp[s + 4 * strideM5] = Y[4];
}
}
}
template<bool SCALE> static void ntt5_fwd(u32 *a, const u32 *src, u32 len) {
stage5_dif_src<SCALE>(a, src, len, g_M5L); _mm_sfence();
for (int q = 0; q < 5; q++) ntt_fwd(a + (size_t)q * g_M5, 0, 0);
}
static u32 g_dataA[5 * ((1u << 22) + 448 * TOPPAD + 896)] __attribute__((aligned(64)));
static u32 g_dataB[5 * ((1u << 22) + 448 * TOPPAD + 896)] __attribute__((aligned(64)));
static int g_ready = 0;
__attribute__((target("avx2"))) void poly_multiply(unsigned *a, int n, unsigned *b, int m, unsigned *c) {
const u32 M = 1u << 22;
const u32 N = 5u * M;
if (!g_ready) { ntt_init(M); init_r5(N); _mm_sfence(); g_ready = 1; }
u32 *A = g_dataA, *B = g_dataB;
(void)n; (void)m; u32 na = 10000000u, nb = 10000000u;
u32 ninv = powmod(N % NTP, NTP - 2);
g_scale_digit[0] = 0;
for (int d = 1; d < 10; d++) {
u64 v = (u64)g_scale_digit[d - 1] + ninv;
g_scale_digit[d] = (u32)(v >= NTP ? v - NTP : v);
}
for (int d = 0; d < 8; d++) { g_scale_lo[d] = g_scale_digit[d]; g_scale_hi[d] = d < 2 ? g_scale_digit[d + 8] : 0; }
ntt5_fwd<false>(A, a, na);
stage5_dif_src<true>(B, b, nb, g_M5L); _mm_sfence();
{
g_ninvS = ninv; g_limS = na + nb + 1; g_outC = c;
g_nt = (((uintptr_t)c & 31) == 0);
g_fuse = 1;
}
for(int q=0;q<5;q++){u32 *aa=A+(size_t)q*g_M5,*bb=B+(size_t)q*g_M5;
g_pmdelta=(long)((char*)aa-(char*)bb);
ntt_fwd(bb,0,0);
g_pmdelta=0;
ntt_inv(aa,0);}
merge5_dit(A,g_M5L);
_mm_sfence();
}
| Compilation | N/A | N/A | Compile OK | Score: N/A | 显示更多 |
| Testcase #1 | 326.139 ms | 310 MB + 420 KB | Accepted | Score: 100 | 显示更多 |