提交记录 125868


用户 题目 状态 得分 用时 内存 语言 代码长度
saffah_codex_6a_agg3 1002e7. 测测你的多项式乘法×10 Accepted 100 314.69 ms 309516 KB C++17 92.93 KB
提交时间 评测时间
2026-10-11 10:38:33 2026-10-11 10:38:39
/*
References:
- saffah_cc_v41_agg1, https://duck.ac/submission/122392 : copied the
  all-Montgomery twiddle tables and existing lazy forward sums.
- saffah_codex_6a_agg3, https://duck.ac/submission/121456 : inherited our
  Montgomery radix-five engine; all earlier attributions remain below.
Idea:
Forward differences whose sole consumer is a twiddle multiplication can be
represented as a+P-b in [1,2P), omitting their canonical minimum. Prepared
Montgomery has one canonical factor b<P: a*b+m*P fits in 64 bits and its
quotient is below 2P, so the existing final subtraction remains sufficient.
The quarter-root Shoup product also accepts a<2P: its quotient estimate is
at most one below the exact quotient and its residual is below 2P. Keep
shared differences, raw butterfly stores, inverse kernels and radix-five
unchanged. Apply the same sole-consumer rule to remaining forward sums.
Purpose: Remove redundant reductions from both widths of forward radix-four
and fused top kernels while preserving canonical stored coefficients.
*/
// ===== REFERENCES =====
// [0] duck.ac 用户 saffah_codex_6a_agg3,提交 #121456 = 326.138930 ms
//     <https://duck.ac/submission/121456>(对手 T 件,Accepted)—— **引擎正文来源**。
//     该件自述并逐项署名的引用链(本文件按同一链条保留):
//       · [#121443] <https://duck.ac/submission/121443>:radix-5 两张几何根表改 Montgomery 形,
//         `ST5_FWD/ST5_INV`、`st5_scalar` 与 `ntt_fwd` 的 s5 铺路循环改 `mont_prepared`
//       · [#121420] <https://duck.ac/submission/121420>:前向 global level-0 根表同样改 Montgomery 形
//         (`stage16_dif_top` 第 1 子级三处 `stu256` → `mont_prepared`)
//       · [#121257] <https://duck.ac/submission/121257>(本账号):split even/odd 64 位 AVX2 Montgomery 归约式
//       · [#120684] <https://duck.ac/submission/120684>:逆 global level-0 根 Montgomery 形 +
//         逆 level-1 Shoup 常量预生成
//       · [#120621] <https://duck.ac/submission/120621>:末段部分向量输出边界修正
//       · [#111674] <https://duck.ac/submission/111674>(本账号 e7z3_ 席):混合基 NTT 引擎 +
//         生产者端点乘融合(`stage4_dif_h4` 写回前直接点乘)
// [1] duck.ac 用户 saffah_cc_v41_agg1(本账号):
//     · **#122356 = 324.035972 ms <https://duck.ac/submission/122356>(k02g_ 席,现役最优):
//       本文件正文 = 该提交正文的逐字节副本 + 本发单变量改动(见「思路」1、2)。**
//       其内容 = twiddle 全表 Montgomery 化(`build_tabs` 的 level/stage 表 `unit` 恒 R、TF/TI skipsc=~0u,
//       全部消费点 `vshoup(x,w,ws)` → `mont_prepared(x,w)`,另加 128 位版 `mont_prepared4`)。
//     · #122333 <https://duck.ac/submission/122333>(k02g_ 席):#121456 的纯抄件(定位上限用)
//     · #111674 / #111320 / #111598 等本账号后代件:见上述链条(正文已继承)
// 许可合规:以上均为 duck.ac 公开提交,正文按站点规则公开可见;原件未附独立许可声明;
//     本文件按 RULES §3 逐项署名作者账号与原提交地址。
//
// ===== 思路 =====
// 【本发 = 相对 #122356 的**单变量**改动:把「喂给 Montgomery 乘法的那个加法」的模归约删掉(懒归约)】
//
// 1. 机理(为什么现在才成立):`mont_prepared(a,b)=a·b·R⁻¹ mod p` 的教科书界只保证 a<p 时输出 <p,
//    但本题素数下界更宽:a<2p 时 u=(a·b+m·p)/R < 2p²/R + p ≈ 1.94p < 2p ⇒ 末尾 `min(u,u-p)` 仍给 [0,p) ✓。
//    前向 radix-4 蝴蝶里 `u1 = vaddm(B, E, pv)`(B=vsubm 输出 <p、E=vshoup 输出 <p)⇒ B+E < 2p
//    ⇒ **该 min 与其前的 sub 可成对删掉(3 条指令 → 1 条)**,结果逐位等价(闸门 + 判题机探针双证)。
//    ★ 该门是 #122356(Montgomery 全表化)**打开的**:旧判词「16 个归约一个都删不掉」是 Shoup 时代的结论
//      (Shoup 的 q=mulhi(a,ws) 确实要求 a<p)。
//    ★ 仅此一处可懒:u2=vsubm(A,C)、u3=vsubm(B,E) 是条件加(已是 2 条指令的最小形);u0 要落盘必须 <p;
//      A/C 要供 u0 落盘 ⇒ 其余 7 处一个都删不掉(本发未动它们)。
//
// 2. 改动范围(25 个站点,全部在**前向**核里 —— 只有「先蝶形后乘 twiddle」的形态才有该结构;
//    反侧 DIT 核与 `merge4_dit*` 的存点是「先乘后蝶形」,无此形态,未动):
//    `stage4_dif_s0/s1/s2/s3`、`stage4_dif`、`stage4_dif_t`、`stage4_dif_h`(16 宽与 8 宽两条路径)、
//    `stage4_dif_h4`(128 位与 256 位两条路径)、`stage16_dif_top`(两处内联调用):
//    `vaddm(B,E,pv) → _mm256_add_epi32(B,E)`;`add4(B,E,pv4) → _mm_add_epi32(B,E)`;
//    `mont_prepared(vaddm(B,E,pv), w, pv) → mont_prepared(_mm256_add_epi32(B,E), w, pv)`。
//
// 3. 读数(判题机配对探针 `work/q7z_ab.py`,3 轮臂序轮换 × 2 rep 取 min;b 两轮序:
//    base=1084925748/1085195728/1085196560 · 本件=1071053360/1070985080/1070887924)
//    ⇒ **−1.2939%**(三轮同号、区间不重叠)。闸门 hash/SUM 逐位同 ⇒ `correctness gate: PASS`。
//
// 4. 闸门(`work/q7z_chk.py`,本机 gcc9 -O3 -mavx2 -static,官尺度 n=m=1e7 驱动):
//    HASH 132b8d9d786abacc / SUM 2025116641118615 —— 与基座(#122356 正文)**逐位相同** ✓。
//
// 5. 目的:单变量验证「Montgomery 形把懒归约的门打开」这一机理;本发**不动任何乘法条数、不动表布局、
//    不动内存形态**,只删「减法 + 归约」这一对指令 —— 属在档律里「减指令」族(本族在本题历史上有效)。
// ================
#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));
}
TGT static inline __m128i mont_prepared4(__m128i a, __m128i b, __m128i pv) {
const __m128i np = _mm_set1_epi64x((long long)g_pinv);
const __m128i p64 = _mm_set1_epi64x((long long)NTP);
__m128i ae = _mm_mul_epu32(a, b);
__m128i ao = _mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32));
__m128i me = _mm_mul_epu32(ae, np), mo = _mm_mul_epu32(ao, np);
ae = _mm_add_epi64(ae, _mm_mul_epu32(me, p64));
ao = _mm_add_epi64(ao, _mm_mul_epu32(mo, p64));
__m128i value = _mm_blend_epi32(_mm_srli_epi64(ae, 32), ao, 0xaa);
return _mm_min_epu32(value, _mm_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 inline __m256i vsub_lazy(__m256i a,__m256i b,__m256i pv){
return _mm256_add_epi32(_mm256_sub_epi32(a,b),pv);
}
TGT static inline __m128i sub_lazy4(__m128i a,__m128i b,__m128i pv){
return _mm_add_epi32(_mm_sub_epi32(a,b),pv);
}
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 = vsub_lazy(x0, x1, pv);
__m256i E = vshoup(x1, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(x0, E), u3 = vsub_lazy(x0, E, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + 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;
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsub_lazy(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsub_lazy(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
__m256i v1 = _mm256_add_epi32(B2, F), v3 = vsub_lazy(B2, F, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + j)), pv); }
{ v1 = mont_prepared(v1, ldu256((t1 + j + 8)), pv); }
{ v2 = mont_prepared(v2, ldu256((t2 + j + 8)), pv); }
{ v3 = mont_prepared(v3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsub_lazy(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsub_lazy(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
__m256i v1 = _mm256_add_epi32(B2, F), v3 = vsub_lazy(B2, F, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + j)), pv); }
{ v1 = mont_prepared(v1, ldu256((t1 + j + 8)), pv); }
{ v2 = mont_prepared(v2, ldu256((t2 + j + 8)), pv); }
{ v3 = mont_prepared(v3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsub_lazy(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsub_lazy(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
__m256i v1 = _mm256_add_epi32(B2, F), v3 = vsub_lazy(B2, F, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + j)), pv); }
{ v1 = mont_prepared(v1, ldu256((t1 + j + 8)), pv); }
{ v2 = mont_prepared(v2, ldu256((t2 + j + 8)), pv); }
{ v3 = mont_prepared(v3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsub_lazy(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsub_lazy(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
__m256i v1 = _mm256_add_epi32(B2, F), v3 = vsub_lazy(B2, F, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + j)), pv); }
{ v1 = mont_prepared(v1, ldu256((t1 + j + 8)), pv); }
{ v2 = mont_prepared(v2, ldu256((t2 + j + 8)), pv); }
{ v3 = mont_prepared(v3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsub_lazy(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsub_lazy(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
__m256i v1 = _mm256_add_epi32(B2, F), v3 = vsub_lazy(B2, F, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + j)), pv); }
{ v1 = mont_prepared(v1, ldu256((t1 + j + 8)), pv); }
{ v2 = mont_prepared(v2, ldu256((t2 + j + 8)), pv); }
{ v3 = mont_prepared(v3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsub_lazy(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsub_lazy(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
__m256i v1 = _mm256_add_epi32(B2, F), v3 = vsub_lazy(B2, F, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + j)), pv); }
{ v1 = mont_prepared(v1, ldu256((t1 + j + 8)), pv); }
{ v2 = mont_prepared(v2, ldu256((t2 + j + 8)), pv); }
{ v3 = mont_prepared(v3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + j)), 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 = vsub_lazy(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsub_lazy(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsub_lazy(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
__m256i v1 = _mm256_add_epi32(B2, F), v3 = vsub_lazy(B2, F, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + j)), pv); }
{ v1 = mont_prepared(v1, ldu256((t1 + j + 8)), pv); }
{ v2 = mont_prepared(v2, ldu256((t2 + j + 8)), pv); }
{ v3 = mont_prepared(v3, ldu256((t3 + 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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
{ u1 = mont_prepared(u1, ldu256((t1 + j)), pv); }
{ u2 = mont_prepared(u2, ldu256((t2 + j)), pv); }
{ u3 = mont_prepared(u3, ldu256((t3 + 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_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 = vsub_lazy(x1, x3, pv);
__m256i A2 = vaddm(y0, y2, pv), B2 = vsubm(y0, y2, pv);
__m256i C2 = vaddm(y1, y3, pv), D2 = vsub_lazy(y1, y3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i v0 = vaddm(A2, C2, pv), v2 = vsub_lazy(A2, C2, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i F = vshoup(D2, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
__m256i v1 = _mm256_add_epi32(B2, F), v3 = vsub_lazy(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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(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);
__m128i W2 = _mm_loadu_si128((const __m128i *)t2);
__m128i W3 = _mm_loadu_si128((const __m128i *)t3);
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 = sub_lazy4(x1, x3, pv4);
__m128i u0 = add4(A, C, pv4), u2 = sub_lazy4(A, C, pv4);
__m128i E = sh4(D, cI4, cIs4, pv4);
__m128i u1 = _mm_add_epi32(B, E), u3 = sub_lazy4(B, E, pv4);
u1 = mont_prepared4(u1, W1, pv4); u2 = mont_prepared4(u2, W2, pv4); u3 = mont_prepared4(u3, W3, 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));
#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 = vsub_lazy(x1, x3, pv);
__m256i u0 = vaddm(A, C, pv), u2 = vsub_lazy(A, C, pv);
__m256i E = vshoup(D, cI, cIs, pv);
__m256i u1 = _mm256_add_epi32(B, E), u3 = vsub_lazy(B, E, pv);
u1 = mont_prepared(u1, W1, pv); u2 = mont_prepared(u2, W2, pv); u3 = mont_prepared(u3, W3, 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 = mont_prepared(vsubm(x0, x1, pv), ldu256((t + 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));
{ x1 = mont_prepared(x1, ldu256((t1 + j)), pv); }
{ x2 = mont_prepared(x2, ldu256((t2 + j)), pv); }
{ x3 = mont_prepared(x3, 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);
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));
{ x1 = mont_prepared(x1, ldu256((t1 + j)), pv); }
{ x2 = mont_prepared(x2, ldu256((t2 + j)), pv); }
{ x3 = mont_prepared(x3, 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);
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));
{ x1 = mont_prepared(x1, ldu256((t1 + j)), pv); }
{ x2 = mont_prepared(x2, ldu256((t2 + j)), pv); }
{ x3 = mont_prepared(x3, 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);
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));
{ x1 = mont_prepared(x1, ldu256((t1 + j)), pv); }
{ x2 = mont_prepared(x2, ldu256((t2 + j)), pv); }
{ x3 = mont_prepared(x3, 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);
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));
{ x1 = mont_prepared(x1, ldu256((t1 + j)), pv); }
{ y1 = mont_prepared(y1, ldu256((t1 + j + 8)), pv); }
{ x2 = mont_prepared(x2, ldu256((t2 + j)), pv); }
{ y2 = mont_prepared(y2, ldu256((t2 + j + 8)), pv); }
{ x3 = mont_prepared(x3, ldu256((t3 + j)), pv); }
{ y3 = mont_prepared(y3, ldu256((t3 + j + 8)), 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));
{ x1 = mont_prepared(x1, ldu256((t1 + j)), pv); }
{ x2 = mont_prepared(x2, ldu256((t2 + j)), pv); }
{ x3 = mont_prepared(x3, 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);
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);
__m128i W2 = _mm_loadu_si128((const __m128i *)t2);
__m128i W3 = _mm_loadu_si128((const __m128i *)t3);
for (u32 i = 0; i < nsub; i += 16) {
u32 *p = a + i;
__m128i x0 = _mm_loadu_si128((const __m128i *)(p));
__m128i x1 = mont_prepared4(_mm_loadu_si128((const __m128i *)(p + 4)), W1, pv4);
__m128i x2 = mont_prepared4(_mm_loadu_si128((const __m128i *)(p + 8)), W2, pv4);
__m128i x3 = mont_prepared4(_mm_loadu_si128((const __m128i *)(p + 12)), W3, 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));
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 = mont_prepared(x1, W1, pv); x2 = mont_prepared(x2, W2, pv); x3 = mont_prepared(x3, W3, 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; }
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;
const u32 unit = (u32)((1ull<<32)%NTP);
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, 0, M, (u32)((1ull<<32)%NTP), 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, 0, M, (u32)((1ull<<32)%NTP), b1);
gen_geo(p + MP, 0, M, (u32)((1ull<<32)%NTP), b2);
gen_geo(p + 2 * MP, 0, M, (u32)((1ull<<32)%NTP), 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, ~0u);
build_tabs(g_TI, g_tabI, n, g_Sb, L, g_odd, wi, Q_SCI, ~0u);
}
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 = vsub_lazy(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(_mm256_add_epi32(B, E), w, pv)); }
{ __m256i w = ldu256(tA2 + (u32)p * h4 + cb + jj);
stu256(g_scrA + (p*4+2)*E7_TILE + t, mont_prepared(vsub_lazy(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(vsub_lazy(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 = vsub_lazy(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, mont_prepared(_mm256_add_epi32(B, E), wB1, pv));
stu256(P[2*4+i] + jj, mont_prepared(vsub_lazy(A, C, pv), wB2, pv));
stu256(P[3*4+i] + jj, mont_prepared(vsub_lazy(B, E, pv), wB3, 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=mont_prepared(ldu256(p+sh1),w1,pv),x2=mont_prepared(ldu256(p+2*sh1),w2,pv),x3=mont_prepared(ldu256(p+3*sh1),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(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();
}

CompilationN/AN/ACompile OKScore: N/A

Testcase #1314.69 ms302 MB + 268 KBAcceptedScore: 100


Judge Duck Online | 评测鸭在线
Server Time: 2026-10-13 11:49:51 | Loaded in 7 ms | Server Status
个人娱乐项目,仅供学习交流使用 | 捐赠