mirror of
https://github.com/arnaucube/poulpy.git
synced 2026-02-10 13:16:44 +01:00
spqlios basic wrapper
This commit is contained in:
168
spqlios/lib/test/testlib/fft64_dft.cpp
Normal file
168
spqlios/lib/test/testlib/fft64_dft.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "fft64_dft.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "../../spqlios/reim/reim_fft.h"
|
||||
#include "../../spqlios/reim/reim_fft_internal.h"
|
||||
|
||||
reim_fft64vec::reim_fft64vec(uint64_t n) : v(n, 0) {}
|
||||
reim4_elem reim_fft64vec::get_blk(uint64_t blk) const {
|
||||
return reim_view(v.size() / 2, (double*)v.data()).get_blk(blk);
|
||||
}
|
||||
double* reim_fft64vec::data() { return v.data(); }
|
||||
const double* reim_fft64vec::data() const { return v.data(); }
|
||||
uint64_t reim_fft64vec::nn() const { return v.size(); }
|
||||
reim_fft64vec::reim_fft64vec(uint64_t n, const double* data) : v(data, data + n) {}
|
||||
void reim_fft64vec::save_as(double* dest) const { memcpy(dest, v.data(), nn() * sizeof(double)); }
|
||||
reim_fft64vec reim_fft64vec::zero(uint64_t n) { return reim_fft64vec(n); }
|
||||
void reim_fft64vec::set_blk(uint64_t blk, const reim4_elem& value) {
|
||||
reim_view(v.size() / 2, (double*)v.data()).set_blk(blk, value);
|
||||
}
|
||||
reim_fft64vec reim_fft64vec::dft_random(uint64_t n, uint64_t log2bound) {
|
||||
return simple_fft64(znx_i64::random_log2bound(n, log2bound));
|
||||
}
|
||||
reim_fft64vec reim_fft64vec::random(uint64_t n, double log2bound) {
|
||||
double bound = pow(2., log2bound);
|
||||
reim_fft64vec res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.v[i] = uniform_f64_bounds(-bound, bound);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
reim_fft64vec operator+(const reim_fft64vec& a, const reim_fft64vec& b) {
|
||||
uint64_t nn = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == a.nn(), "ring dimension mismatch");
|
||||
reim_fft64vec res(nn);
|
||||
double* rv = res.data();
|
||||
const double* av = a.data();
|
||||
const double* bv = b.data();
|
||||
for (uint64_t i = 0; i < nn; ++i) {
|
||||
rv[i] = av[i] + bv[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
reim_fft64vec operator-(const reim_fft64vec& a, const reim_fft64vec& b) {
|
||||
uint64_t nn = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == a.nn(), "ring dimension mismatch");
|
||||
reim_fft64vec res(nn);
|
||||
double* rv = res.data();
|
||||
const double* av = a.data();
|
||||
const double* bv = b.data();
|
||||
for (uint64_t i = 0; i < nn; ++i) {
|
||||
rv[i] = av[i] - bv[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
reim_fft64vec operator*(const reim_fft64vec& a, const reim_fft64vec& b) {
|
||||
uint64_t nn = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == a.nn(), "ring dimension mismatch");
|
||||
REQUIRE_DRAMATICALLY(nn >= 2, "test not defined for nn=1");
|
||||
uint64_t m = nn / 2;
|
||||
reim_fft64vec res(nn);
|
||||
double* rv = res.data();
|
||||
const double* av = a.data();
|
||||
const double* bv = b.data();
|
||||
for (uint64_t i = 0; i < m; ++i) {
|
||||
rv[i] = av[i] * bv[i] - av[m + i] * bv[m + i];
|
||||
rv[m + i] = av[i] * bv[m + i] + av[m + i] * bv[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
reim_fft64vec& operator+=(reim_fft64vec& a, const reim_fft64vec& b) {
|
||||
uint64_t nn = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == a.nn(), "ring dimension mismatch");
|
||||
double* av = a.data();
|
||||
const double* bv = b.data();
|
||||
for (uint64_t i = 0; i < nn; ++i) {
|
||||
av[i] = av[i] + bv[i];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
reim_fft64vec& operator-=(reim_fft64vec& a, const reim_fft64vec& b) {
|
||||
uint64_t nn = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == a.nn(), "ring dimension mismatch");
|
||||
double* av = a.data();
|
||||
const double* bv = b.data();
|
||||
for (uint64_t i = 0; i < nn; ++i) {
|
||||
av[i] = av[i] - bv[i];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
reim_fft64vec simple_fft64(const znx_i64& polynomial) {
|
||||
const uint64_t nn = polynomial.nn();
|
||||
const uint64_t m = nn / 2;
|
||||
reim_fft64vec res(nn);
|
||||
double* dat = res.data();
|
||||
for (uint64_t i = 0; i < nn; ++i) dat[i] = polynomial.get_coeff(i);
|
||||
reim_fft_simple(m, dat);
|
||||
return res;
|
||||
}
|
||||
|
||||
znx_i64 simple_rint_ifft64(const reim_fft64vec& fftvec) {
|
||||
const uint64_t nn = fftvec.nn();
|
||||
const uint64_t m = nn / 2;
|
||||
std::vector<double> vv(fftvec.data(), fftvec.data() + nn);
|
||||
double* v = vv.data();
|
||||
reim_ifft_simple(m, v);
|
||||
znx_i64 res(nn);
|
||||
for (uint64_t i = 0; i < nn; ++i) {
|
||||
res.set_coeff(i, rint(v[i] / m));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
rnx_f64 naive_ifft64(const reim_fft64vec& fftvec) {
|
||||
const uint64_t nn = fftvec.nn();
|
||||
const uint64_t m = nn / 2;
|
||||
std::vector<double> vv(fftvec.data(), fftvec.data() + nn);
|
||||
double* v = vv.data();
|
||||
reim_ifft_simple(m, v);
|
||||
rnx_f64 res(nn);
|
||||
for (uint64_t i = 0; i < nn; ++i) {
|
||||
res.set_coeff(i, v[i] / m);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
double infty_dist(const reim_fft64vec& a, const reim_fft64vec& b) {
|
||||
const uint64_t n = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == a.nn(), "dimensions mismatch");
|
||||
const double* da = a.data();
|
||||
const double* db = b.data();
|
||||
double d = 0;
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
double di = abs(da[i] - db[i]);
|
||||
if (di > d) d = di;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
reim_fft64vec simple_fft64(const rnx_f64& polynomial) {
|
||||
const uint64_t nn = polynomial.nn();
|
||||
const uint64_t m = nn / 2;
|
||||
reim_fft64vec res(nn);
|
||||
double* dat = res.data();
|
||||
for (uint64_t i = 0; i < nn; ++i) dat[i] = polynomial.get_coeff(i);
|
||||
reim_fft_simple(m, dat);
|
||||
return res;
|
||||
}
|
||||
|
||||
reim_fft64vec operator*(double coeff, const reim_fft64vec& v) {
|
||||
const uint64_t nn = v.nn();
|
||||
reim_fft64vec res(nn);
|
||||
double* rr = res.data();
|
||||
const double* vv = v.data();
|
||||
for (uint64_t i = 0; i < nn; ++i) rr[i] = coeff * vv[i];
|
||||
return res;
|
||||
}
|
||||
|
||||
rnx_f64 simple_ifft64(const reim_fft64vec& v) {
|
||||
const uint64_t nn = v.nn();
|
||||
const uint64_t m = nn / 2;
|
||||
rnx_f64 res(nn);
|
||||
double* dat = res.data();
|
||||
memcpy(dat, v.data(), nn * sizeof(double));
|
||||
reim_ifft_simple(m, dat);
|
||||
return res;
|
||||
}
|
||||
43
spqlios/lib/test/testlib/fft64_dft.h
Normal file
43
spqlios/lib/test/testlib/fft64_dft.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef SPQLIOS_FFT64_DFT_H
|
||||
#define SPQLIOS_FFT64_DFT_H
|
||||
|
||||
#include "negacyclic_polynomial.h"
|
||||
#include "reim4_elem.h"
|
||||
|
||||
class reim_fft64vec {
|
||||
std::vector<double> v;
|
||||
|
||||
public:
|
||||
reim_fft64vec() = default;
|
||||
explicit reim_fft64vec(uint64_t n);
|
||||
reim_fft64vec(uint64_t n, const double* data);
|
||||
uint64_t nn() const;
|
||||
static reim_fft64vec zero(uint64_t n);
|
||||
/** random complex coefficients (unstructured) */
|
||||
static reim_fft64vec random(uint64_t n, double log2bound);
|
||||
/** random fft of a small int polynomial */
|
||||
static reim_fft64vec dft_random(uint64_t n, uint64_t log2bound);
|
||||
double* data();
|
||||
const double* data() const;
|
||||
void save_as(double* dest) const;
|
||||
reim4_elem get_blk(uint64_t blk) const;
|
||||
void set_blk(uint64_t blk, const reim4_elem& value);
|
||||
};
|
||||
|
||||
reim_fft64vec operator+(const reim_fft64vec& a, const reim_fft64vec& b);
|
||||
reim_fft64vec operator-(const reim_fft64vec& a, const reim_fft64vec& b);
|
||||
reim_fft64vec operator*(const reim_fft64vec& a, const reim_fft64vec& b);
|
||||
reim_fft64vec operator*(double coeff, const reim_fft64vec& v);
|
||||
reim_fft64vec& operator+=(reim_fft64vec& a, const reim_fft64vec& b);
|
||||
reim_fft64vec& operator-=(reim_fft64vec& a, const reim_fft64vec& b);
|
||||
|
||||
/** infty distance */
|
||||
double infty_dist(const reim_fft64vec& a, const reim_fft64vec& b);
|
||||
|
||||
reim_fft64vec simple_fft64(const znx_i64& polynomial);
|
||||
znx_i64 simple_rint_ifft64(const reim_fft64vec& fftvec);
|
||||
rnx_f64 naive_ifft64(const reim_fft64vec& fftvec);
|
||||
reim_fft64vec simple_fft64(const rnx_f64& polynomial);
|
||||
rnx_f64 simple_ifft64(const reim_fft64vec& v);
|
||||
|
||||
#endif // SPQLIOS_FFT64_DFT_H
|
||||
238
spqlios/lib/test/testlib/fft64_layouts.cpp
Normal file
238
spqlios/lib/test/testlib/fft64_layouts.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#include "fft64_layouts.h"
|
||||
#ifdef VALGRIND_MEM_TESTS
|
||||
#include "valgrind/memcheck.h"
|
||||
#endif
|
||||
|
||||
void* alloc64(uint64_t size) {
|
||||
static uint64_t _msk64 = -64;
|
||||
if (size == 0) return nullptr;
|
||||
uint64_t rsize = (size + 63) & _msk64;
|
||||
uint8_t* reps = (uint8_t*)spqlios_alloc(rsize);
|
||||
REQUIRE_DRAMATICALLY(reps != 0, "Out of memory");
|
||||
#ifdef VALGRIND_MEM_TESTS
|
||||
VALGRIND_MAKE_MEM_NOACCESS(reps + size, rsize - size);
|
||||
#endif
|
||||
return reps;
|
||||
}
|
||||
|
||||
fft64_vec_znx_dft_layout::fft64_vec_znx_dft_layout(uint64_t n, uint64_t size)
|
||||
: nn(n), //
|
||||
size(size), //
|
||||
data((VEC_ZNX_DFT*)alloc64(n * size * 8)), //
|
||||
view(n / 2, size, (double*)data) {}
|
||||
|
||||
fft64_vec_znx_dft_layout::~fft64_vec_znx_dft_layout() { spqlios_free(data); }
|
||||
|
||||
double* fft64_vec_znx_dft_layout::get_addr(uint64_t idx) {
|
||||
REQUIRE_DRAMATICALLY(idx < size, "index overflow " << idx << " / " << size);
|
||||
return ((double*)data) + idx * nn;
|
||||
}
|
||||
const double* fft64_vec_znx_dft_layout::get_addr(uint64_t idx) const {
|
||||
REQUIRE_DRAMATICALLY(idx < size, "index overflow " << idx << " / " << size);
|
||||
return ((double*)data) + idx * nn;
|
||||
}
|
||||
reim_fft64vec fft64_vec_znx_dft_layout::get_copy_zext(uint64_t idx) const {
|
||||
if (idx < size) {
|
||||
return reim_fft64vec(nn, get_addr(idx));
|
||||
} else {
|
||||
return reim_fft64vec::zero(nn);
|
||||
}
|
||||
}
|
||||
void fft64_vec_znx_dft_layout::fill_dft_random_log2bound(uint64_t bits) {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
set(i, simple_fft64(znx_i64::random_log2bound(nn, bits)));
|
||||
}
|
||||
}
|
||||
void fft64_vec_znx_dft_layout::set(uint64_t idx, const reim_fft64vec& value) {
|
||||
REQUIRE_DRAMATICALLY(value.nn() == nn, "ring dimension mismatch");
|
||||
value.save_as(get_addr(idx));
|
||||
}
|
||||
thash fft64_vec_znx_dft_layout::content_hash() const { return test_hash(data, size * nn * sizeof(double)); }
|
||||
|
||||
reim4_elem fft64_vec_znx_dft_layout::get(uint64_t idx, uint64_t blk) const {
|
||||
REQUIRE_DRAMATICALLY(idx < size, "index overflow: " << idx << " / " << size);
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "blk overflow: " << blk << " / " << nn / 8);
|
||||
double* reim = ((double*)data) + idx * nn;
|
||||
return reim4_elem(reim + blk * 4, reim + nn / 2 + blk * 4);
|
||||
}
|
||||
reim4_elem fft64_vec_znx_dft_layout::get_zext(uint64_t idx, uint64_t blk) const {
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "blk overflow: " << blk << " / " << nn / 8);
|
||||
if (idx < size) {
|
||||
return get(idx, blk);
|
||||
} else {
|
||||
return reim4_elem::zero();
|
||||
}
|
||||
}
|
||||
void fft64_vec_znx_dft_layout::set(uint64_t idx, uint64_t blk, const reim4_elem& value) {
|
||||
REQUIRE_DRAMATICALLY(idx < size, "index overflow: " << idx << " / " << size);
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "blk overflow: " << blk << " / " << nn / 8);
|
||||
double* reim = ((double*)data) + idx * nn;
|
||||
value.save_re_im(reim + blk * 4, reim + nn / 2 + blk * 4);
|
||||
}
|
||||
void fft64_vec_znx_dft_layout::fill_random(double log2bound) {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
set(i, reim_fft64vec::random(nn, log2bound));
|
||||
}
|
||||
}
|
||||
void fft64_vec_znx_dft_layout::fill_dft_random(uint64_t log2bound) {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
set(i, reim_fft64vec::dft_random(nn, log2bound));
|
||||
}
|
||||
}
|
||||
|
||||
fft64_vec_znx_big_layout::fft64_vec_znx_big_layout(uint64_t n, uint64_t size)
|
||||
: nn(n), //
|
||||
size(size), //
|
||||
data((VEC_ZNX_BIG*)alloc64(n * size * 8)) {}
|
||||
|
||||
znx_i64 fft64_vec_znx_big_layout::get_copy(uint64_t index) const {
|
||||
REQUIRE_DRAMATICALLY(index < size, "index overflow: " << index << " / " << size);
|
||||
return znx_i64(nn, ((int64_t*)data) + index * nn);
|
||||
}
|
||||
znx_i64 fft64_vec_znx_big_layout::get_copy_zext(uint64_t index) const {
|
||||
if (index < size) {
|
||||
return znx_i64(nn, ((int64_t*)data) + index * nn);
|
||||
} else {
|
||||
return znx_i64::zero(nn);
|
||||
}
|
||||
}
|
||||
void fft64_vec_znx_big_layout::set(uint64_t index, const znx_i64& value) {
|
||||
REQUIRE_DRAMATICALLY(index < size, "index overflow: " << index << " / " << size);
|
||||
value.save_as(((int64_t*)data) + index * nn);
|
||||
}
|
||||
void fft64_vec_znx_big_layout::fill_random() {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
set(i, znx_i64::random_log2bound(nn, 1));
|
||||
}
|
||||
}
|
||||
fft64_vec_znx_big_layout::~fft64_vec_znx_big_layout() { spqlios_free(data); }
|
||||
|
||||
fft64_vmp_pmat_layout::fft64_vmp_pmat_layout(uint64_t n, uint64_t nrows, uint64_t ncols)
|
||||
: nn(n),
|
||||
nrows(nrows),
|
||||
ncols(ncols), //
|
||||
data((VMP_PMAT*)alloc64(nrows * ncols * nn * 8)) {}
|
||||
|
||||
double* fft64_vmp_pmat_layout::get_addr(uint64_t row, uint64_t col, uint64_t blk) const {
|
||||
REQUIRE_DRAMATICALLY(row < nrows, "row overflow: " << row << " / " << nrows);
|
||||
REQUIRE_DRAMATICALLY(col < ncols, "col overflow: " << col << " / " << ncols);
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "block overflow: " << blk << " / " << (nn / 8));
|
||||
double* d = (double*)data;
|
||||
if (col == (ncols - 1) && (ncols % 2 == 1)) {
|
||||
// special case: last column out of an odd column number
|
||||
return d + blk * nrows * ncols * 8 // major: blk
|
||||
+ col * nrows * 8 // col == ncols-1
|
||||
+ row * 8;
|
||||
} else {
|
||||
// general case: columns go by pair
|
||||
return d + blk * nrows * ncols * 8 // major: blk
|
||||
+ (col / 2) * (2 * nrows) * 8 // second: col pair index
|
||||
+ row * 2 * 8 // third: row index
|
||||
+ (col % 2) * 8; // minor: col in colpair
|
||||
}
|
||||
}
|
||||
|
||||
reim4_elem fft64_vmp_pmat_layout::get(uint64_t row, uint64_t col, uint64_t blk) const {
|
||||
return reim4_elem(get_addr(row, col, blk));
|
||||
}
|
||||
reim4_elem fft64_vmp_pmat_layout::get_zext(uint64_t row, uint64_t col, uint64_t blk) const {
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "block overflow: " << blk << " / " << (nn / 8));
|
||||
if (row < nrows && col < ncols) {
|
||||
return reim4_elem(get_addr(row, col, blk));
|
||||
} else {
|
||||
return reim4_elem::zero();
|
||||
}
|
||||
}
|
||||
void fft64_vmp_pmat_layout::set(uint64_t row, uint64_t col, uint64_t blk, const reim4_elem& value) const {
|
||||
value.save_as(get_addr(row, col, blk));
|
||||
}
|
||||
|
||||
fft64_vmp_pmat_layout::~fft64_vmp_pmat_layout() { spqlios_free(data); }
|
||||
|
||||
reim_fft64vec fft64_vmp_pmat_layout::get_zext(uint64_t row, uint64_t col) const {
|
||||
if (row >= nrows || col >= ncols) {
|
||||
return reim_fft64vec::zero(nn);
|
||||
}
|
||||
if (nn < 8) {
|
||||
// the pmat is just col major
|
||||
double* addr = (double*)data + (row + col * nrows) * nn;
|
||||
return reim_fft64vec(nn, addr);
|
||||
}
|
||||
// otherwise, reconstruct it block by block
|
||||
reim_fft64vec res(nn);
|
||||
for (uint64_t blk = 0; blk < nn / 8; ++blk) {
|
||||
reim4_elem v = get(row, col, blk);
|
||||
res.set_blk(blk, v);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
void fft64_vmp_pmat_layout::set(uint64_t row, uint64_t col, const reim_fft64vec& value) {
|
||||
REQUIRE_DRAMATICALLY(row < nrows, "row overflow: " << row << " / " << nrows);
|
||||
REQUIRE_DRAMATICALLY(col < ncols, "row overflow: " << col << " / " << ncols);
|
||||
if (nn < 8) {
|
||||
// the pmat is just col major
|
||||
double* addr = (double*)data + (row + col * nrows) * nn;
|
||||
value.save_as(addr);
|
||||
return;
|
||||
}
|
||||
// otherwise, reconstruct it block by block
|
||||
for (uint64_t blk = 0; blk < nn / 8; ++blk) {
|
||||
reim4_elem v = value.get_blk(blk);
|
||||
set(row, col, blk, v);
|
||||
}
|
||||
}
|
||||
void fft64_vmp_pmat_layout::fill_random(double log2bound) {
|
||||
for (uint64_t row = 0; row < nrows; ++row) {
|
||||
for (uint64_t col = 0; col < ncols; ++col) {
|
||||
set(row, col, reim_fft64vec::random(nn, log2bound));
|
||||
}
|
||||
}
|
||||
}
|
||||
void fft64_vmp_pmat_layout::fill_dft_random(uint64_t log2bound) {
|
||||
for (uint64_t row = 0; row < nrows; ++row) {
|
||||
for (uint64_t col = 0; col < ncols; ++col) {
|
||||
set(row, col, reim_fft64vec::dft_random(nn, log2bound));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fft64_svp_ppol_layout::fft64_svp_ppol_layout(uint64_t n)
|
||||
: nn(n), //
|
||||
data((SVP_PPOL*)alloc64(nn * 8)) {}
|
||||
|
||||
reim_fft64vec fft64_svp_ppol_layout::get_copy() const { return reim_fft64vec(nn, (double*)data); }
|
||||
|
||||
void fft64_svp_ppol_layout::set(const reim_fft64vec& value) { value.save_as((double*)data); }
|
||||
|
||||
void fft64_svp_ppol_layout::fill_dft_random(uint64_t log2bound) { set(reim_fft64vec::dft_random(nn, log2bound)); }
|
||||
|
||||
void fft64_svp_ppol_layout::fill_random(double log2bound) { set(reim_fft64vec::random(nn, log2bound)); }
|
||||
|
||||
fft64_svp_ppol_layout::~fft64_svp_ppol_layout() { spqlios_free(data); }
|
||||
thash fft64_svp_ppol_layout::content_hash() const { return test_hash(data, nn * sizeof(double)); }
|
||||
|
||||
fft64_cnv_left_layout::fft64_cnv_left_layout(uint64_t n, uint64_t size)
|
||||
: nn(n), //
|
||||
size(size),
|
||||
data((CNV_PVEC_L*)alloc64(size * nn * 8)) {}
|
||||
|
||||
reim4_elem fft64_cnv_left_layout::get(uint64_t idx, uint64_t blk) {
|
||||
REQUIRE_DRAMATICALLY(idx < size, "idx overflow: " << idx << " / " << size);
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "block overflow: " << blk << " / " << (nn / 8));
|
||||
return reim4_elem(((double*)data) + blk * size + idx);
|
||||
}
|
||||
|
||||
fft64_cnv_left_layout::~fft64_cnv_left_layout() { spqlios_free(data); }
|
||||
|
||||
fft64_cnv_right_layout::fft64_cnv_right_layout(uint64_t n, uint64_t size)
|
||||
: nn(n), //
|
||||
size(size),
|
||||
data((CNV_PVEC_R*)alloc64(size * nn * 8)) {}
|
||||
|
||||
reim4_elem fft64_cnv_right_layout::get(uint64_t idx, uint64_t blk) {
|
||||
REQUIRE_DRAMATICALLY(idx < size, "idx overflow: " << idx << " / " << size);
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "block overflow: " << blk << " / " << (nn / 8));
|
||||
return reim4_elem(((double*)data) + blk * size + idx);
|
||||
}
|
||||
|
||||
fft64_cnv_right_layout::~fft64_cnv_right_layout() { spqlios_free(data); }
|
||||
109
spqlios/lib/test/testlib/fft64_layouts.h
Normal file
109
spqlios/lib/test/testlib/fft64_layouts.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#ifndef SPQLIOS_FFT64_LAYOUTS_H
|
||||
#define SPQLIOS_FFT64_LAYOUTS_H
|
||||
|
||||
#include "../../spqlios/arithmetic/vec_znx_arithmetic.h"
|
||||
#include "fft64_dft.h"
|
||||
#include "negacyclic_polynomial.h"
|
||||
#include "reim4_elem.h"
|
||||
|
||||
/** @brief test layout for the VEC_ZNX_DFT */
|
||||
struct fft64_vec_znx_dft_layout {
|
||||
public:
|
||||
const uint64_t nn;
|
||||
const uint64_t size;
|
||||
VEC_ZNX_DFT* const data;
|
||||
reim_vector_view view;
|
||||
/** @brief fill with random double values (unstructured) */
|
||||
void fill_random(double log2bound);
|
||||
/** @brief fill with random ffts of small int polynomials */
|
||||
void fill_dft_random(uint64_t log2bound);
|
||||
reim4_elem get(uint64_t idx, uint64_t blk) const;
|
||||
reim4_elem get_zext(uint64_t idx, uint64_t blk) const;
|
||||
void set(uint64_t idx, uint64_t blk, const reim4_elem& value);
|
||||
fft64_vec_znx_dft_layout(uint64_t n, uint64_t size);
|
||||
void fill_random_log2bound(uint64_t bits);
|
||||
void fill_dft_random_log2bound(uint64_t bits);
|
||||
double* get_addr(uint64_t idx);
|
||||
const double* get_addr(uint64_t idx) const;
|
||||
reim_fft64vec get_copy_zext(uint64_t idx) const;
|
||||
void set(uint64_t idx, const reim_fft64vec& value);
|
||||
thash content_hash() const;
|
||||
~fft64_vec_znx_dft_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the VEC_ZNX_BIG */
|
||||
class fft64_vec_znx_big_layout {
|
||||
public:
|
||||
const uint64_t nn;
|
||||
const uint64_t size;
|
||||
VEC_ZNX_BIG* const data;
|
||||
fft64_vec_znx_big_layout(uint64_t n, uint64_t size);
|
||||
void fill_random();
|
||||
znx_i64 get_copy(uint64_t index) const;
|
||||
znx_i64 get_copy_zext(uint64_t index) const;
|
||||
void set(uint64_t index, const znx_i64& value);
|
||||
thash content_hash() const;
|
||||
~fft64_vec_znx_big_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the VMP_PMAT */
|
||||
class fft64_vmp_pmat_layout {
|
||||
public:
|
||||
const uint64_t nn;
|
||||
const uint64_t nrows;
|
||||
const uint64_t ncols;
|
||||
VMP_PMAT* const data;
|
||||
fft64_vmp_pmat_layout(uint64_t n, uint64_t nrows, uint64_t ncols);
|
||||
double* get_addr(uint64_t row, uint64_t col, uint64_t blk) const;
|
||||
reim4_elem get(uint64_t row, uint64_t col, uint64_t blk) const;
|
||||
thash content_hash() const;
|
||||
reim4_elem get_zext(uint64_t row, uint64_t col, uint64_t blk) const;
|
||||
reim_fft64vec get_zext(uint64_t row, uint64_t col) const;
|
||||
void set(uint64_t row, uint64_t col, uint64_t blk, const reim4_elem& v) const;
|
||||
void set(uint64_t row, uint64_t col, const reim_fft64vec& value);
|
||||
/** @brief fill with random double values (unstructured) */
|
||||
void fill_random(double log2bound);
|
||||
/** @brief fill with random ffts of small int polynomials */
|
||||
void fill_dft_random(uint64_t log2bound);
|
||||
~fft64_vmp_pmat_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the SVP_PPOL */
|
||||
class fft64_svp_ppol_layout {
|
||||
public:
|
||||
const uint64_t nn;
|
||||
SVP_PPOL* const data;
|
||||
fft64_svp_ppol_layout(uint64_t n);
|
||||
thash content_hash() const;
|
||||
reim_fft64vec get_copy() const;
|
||||
void set(const reim_fft64vec&);
|
||||
/** @brief fill with random double values (unstructured) */
|
||||
void fill_random(double log2bound);
|
||||
/** @brief fill with random ffts of small int polynomials */
|
||||
void fill_dft_random(uint64_t log2bound);
|
||||
~fft64_svp_ppol_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the CNV_PVEC_L */
|
||||
class fft64_cnv_left_layout {
|
||||
const uint64_t nn;
|
||||
const uint64_t size;
|
||||
CNV_PVEC_L* const data;
|
||||
fft64_cnv_left_layout(uint64_t n, uint64_t size);
|
||||
reim4_elem get(uint64_t idx, uint64_t blk);
|
||||
thash content_hash() const;
|
||||
~fft64_cnv_left_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the CNV_PVEC_R */
|
||||
class fft64_cnv_right_layout {
|
||||
const uint64_t nn;
|
||||
const uint64_t size;
|
||||
CNV_PVEC_R* const data;
|
||||
fft64_cnv_right_layout(uint64_t n, uint64_t size);
|
||||
reim4_elem get(uint64_t idx, uint64_t blk);
|
||||
thash content_hash() const;
|
||||
~fft64_cnv_right_layout();
|
||||
};
|
||||
|
||||
#endif // SPQLIOS_FFT64_LAYOUTS_H
|
||||
229
spqlios/lib/test/testlib/mod_q120.cpp
Normal file
229
spqlios/lib/test/testlib/mod_q120.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "mod_q120.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
|
||||
int64_t centermod(int64_t v, int64_t q) {
|
||||
int64_t t = v % q;
|
||||
if (t >= (q + 1) / 2) return t - q;
|
||||
if (t < -q / 2) return t + q;
|
||||
return t;
|
||||
}
|
||||
|
||||
int64_t centermod(uint64_t v, int64_t q) {
|
||||
int64_t t = int64_t(v % uint64_t(q));
|
||||
if (t >= q / 2) return t - q;
|
||||
return t;
|
||||
}
|
||||
|
||||
mod_q120::mod_q120() {
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
a[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mod_q120::mod_q120(int64_t a0, int64_t a1, int64_t a2, int64_t a3) {
|
||||
a[0] = centermod(a0, Qi[0]);
|
||||
a[1] = centermod(a1, Qi[1]);
|
||||
a[2] = centermod(a2, Qi[2]);
|
||||
a[3] = centermod(a3, Qi[3]);
|
||||
}
|
||||
|
||||
mod_q120 operator+(const mod_q120& x, const mod_q120& y) {
|
||||
mod_q120 r;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
r.a[i] = centermod(x.a[i] + y.a[i], mod_q120::Qi[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
mod_q120 operator-(const mod_q120& x, const mod_q120& y) {
|
||||
mod_q120 r;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
r.a[i] = centermod(x.a[i] - y.a[i], mod_q120::Qi[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
mod_q120 operator*(const mod_q120& x, const mod_q120& y) {
|
||||
mod_q120 r;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
r.a[i] = centermod(x.a[i] * y.a[i], mod_q120::Qi[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
mod_q120& operator+=(mod_q120& x, const mod_q120& y) {
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
x.a[i] = centermod(x.a[i] + y.a[i], mod_q120::Qi[i]);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
mod_q120& operator-=(mod_q120& x, const mod_q120& y) {
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
x.a[i] = centermod(x.a[i] - y.a[i], mod_q120::Qi[i]);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
mod_q120& operator*=(mod_q120& x, const mod_q120& y) {
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
x.a[i] = centermod(x.a[i] * y.a[i], mod_q120::Qi[i]);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int64_t modq_pow(int64_t x, int32_t k, int64_t q) {
|
||||
k = (k % (q - 1) + q - 1) % (q - 1);
|
||||
|
||||
int64_t res = 1;
|
||||
int64_t x_pow = centermod(x, q);
|
||||
while (k != 0) {
|
||||
if (k & 1) res = centermod(res * x_pow, q);
|
||||
x_pow = centermod(x_pow * x_pow, q);
|
||||
k >>= 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
mod_q120 pow(const mod_q120& x, int32_t k) {
|
||||
const int64_t r0 = modq_pow(x.a[0], k, x.Qi[0]);
|
||||
const int64_t r1 = modq_pow(x.a[1], k, x.Qi[1]);
|
||||
const int64_t r2 = modq_pow(x.a[2], k, x.Qi[2]);
|
||||
const int64_t r3 = modq_pow(x.a[3], k, x.Qi[3]);
|
||||
return mod_q120{r0, r1, r2, r3};
|
||||
}
|
||||
|
||||
static int64_t half_modq(int64_t x, int64_t q) {
|
||||
// q must be odd in this function
|
||||
if (x % 2 == 0) return x / 2;
|
||||
return centermod((x + q) / 2, q);
|
||||
}
|
||||
|
||||
mod_q120 half(const mod_q120& x) {
|
||||
const int64_t r0 = half_modq(x.a[0], x.Qi[0]);
|
||||
const int64_t r1 = half_modq(x.a[1], x.Qi[1]);
|
||||
const int64_t r2 = half_modq(x.a[2], x.Qi[2]);
|
||||
const int64_t r3 = half_modq(x.a[3], x.Qi[3]);
|
||||
return mod_q120{r0, r1, r2, r3};
|
||||
}
|
||||
|
||||
bool operator==(const mod_q120& x, const mod_q120& y) {
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
if (x.a[i] != y.a[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const mod_q120& x) {
|
||||
return out << "q120{" << x.a[0] << "," << x.a[1] << "," << x.a[2] << "," << x.a[3] << "}";
|
||||
}
|
||||
|
||||
mod_q120 mod_q120::from_q120a(const void* addr) {
|
||||
static const uint64_t _2p32 = UINT64_C(1) << 32;
|
||||
const uint64_t* in = (const uint64_t*)addr;
|
||||
mod_q120 r;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
REQUIRE_DRAMATICALLY(in[i] < _2p32, "invalid layout a q120");
|
||||
r.a[i] = centermod(in[i], mod_q120::Qi[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
mod_q120 mod_q120::from_q120b(const void* addr) {
|
||||
const uint64_t* in = (const uint64_t*)addr;
|
||||
mod_q120 r;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
r.a[i] = centermod(in[i], mod_q120::Qi[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
mod_q120 mod_q120::from_q120c(const void* addr) {
|
||||
//static const uint64_t _mask_2p32 = (uint64_t(1) << 32) - 1;
|
||||
const uint32_t* in = (const uint32_t*)addr;
|
||||
mod_q120 r;
|
||||
for (uint64_t i = 0, k = 0; i < 8; i += 2, ++k) {
|
||||
const uint64_t q = mod_q120::Qi[k];
|
||||
uint64_t u = in[i];
|
||||
uint64_t w = in[i + 1];
|
||||
REQUIRE_DRAMATICALLY(((u << 32) % q) == (w % q),
|
||||
"invalid layout q120c: " << u << ".2^32 != " << (w >> 32) << " mod " << q);
|
||||
r.a[k] = centermod(u, q);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
__int128_t mod_q120::to_int128() const {
|
||||
static const __int128_t qm[] = {(__int128_t(Qi[1]) * Qi[2]) * Qi[3], (__int128_t(Qi[0]) * Qi[2]) * Qi[3],
|
||||
(__int128_t(Qi[0]) * Qi[1]) * Qi[3], (__int128_t(Qi[0]) * Qi[1]) * Qi[2]};
|
||||
static const int64_t CRTi[] = {Q1_CRT_CST, Q2_CRT_CST, Q3_CRT_CST, Q4_CRT_CST};
|
||||
static const __int128_t q = qm[0] * Qi[0];
|
||||
static const __int128_t qs2 = q / 2;
|
||||
__int128_t res = 0;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
res += (a[i] * CRTi[i] % Qi[i]) * qm[i];
|
||||
}
|
||||
res = (((res % q) + q + qs2) % q) - qs2; // centermod
|
||||
return res;
|
||||
}
|
||||
void mod_q120::save_as_q120a(void* dest) const {
|
||||
int64_t* d = (int64_t*)dest;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
d[i] = a[i] + Qi[i];
|
||||
}
|
||||
}
|
||||
void mod_q120::save_as_q120b(void* dest) const {
|
||||
int64_t* d = (int64_t*)dest;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
d[i] = a[i] + (Qi[i] * (1 + uniform_u64_bits(32)));
|
||||
}
|
||||
}
|
||||
void mod_q120::save_as_q120c(void* dest) const {
|
||||
int32_t* d = (int32_t*)dest;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
d[2 * i] = a[i] + 3 * Qi[i];
|
||||
d[2 * i + 1] = (uint64_t(d[2 * i]) << 32) % uint64_t(Qi[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mod_q120 uniform_q120() {
|
||||
test_rng& gen = randgen();
|
||||
std::uniform_int_distribution<uint64_t> dista(0, mod_q120::Qi[0]);
|
||||
std::uniform_int_distribution<uint64_t> distb(0, mod_q120::Qi[1]);
|
||||
std::uniform_int_distribution<uint64_t> distc(0, mod_q120::Qi[2]);
|
||||
std::uniform_int_distribution<uint64_t> distd(0, mod_q120::Qi[3]);
|
||||
return mod_q120(dista(gen), distb(gen), distc(gen), distd(gen));
|
||||
}
|
||||
|
||||
void uniform_q120a(void* dest) {
|
||||
uint64_t* res = (uint64_t*)dest;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
res[i] = uniform_u64_bits(32);
|
||||
}
|
||||
}
|
||||
|
||||
void uniform_q120b(void* dest) {
|
||||
uint64_t* res = (uint64_t*)dest;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
res[i] = uniform_u64();
|
||||
}
|
||||
}
|
||||
|
||||
void uniform_q120c(void* dest) {
|
||||
uint32_t* res = (uint32_t*)dest;
|
||||
static const uint64_t _2p32 = uint64_t(1) << 32;
|
||||
for (uint64_t i = 0, k = 0; i < 8; i += 2, ++k) {
|
||||
const uint64_t q = mod_q120::Qi[k];
|
||||
const uint64_t z = uniform_u64_bits(32);
|
||||
const uint64_t z_pow_red = (z << 32) % q;
|
||||
const uint64_t room = (_2p32 - z_pow_red) / q;
|
||||
const uint64_t z_pow = z_pow_red + (uniform_u64() % room) * q;
|
||||
REQUIRE_DRAMATICALLY(z < _2p32, "bug!");
|
||||
REQUIRE_DRAMATICALLY(z_pow < _2p32, "bug!");
|
||||
REQUIRE_DRAMATICALLY(z_pow % q == (z << 32) % q, "bug!");
|
||||
|
||||
res[i] = (uint32_t)z;
|
||||
res[i + 1] = (uint32_t)z_pow;
|
||||
}
|
||||
}
|
||||
49
spqlios/lib/test/testlib/mod_q120.h
Normal file
49
spqlios/lib/test/testlib/mod_q120.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef SPQLIOS_MOD_Q120_H
|
||||
#define SPQLIOS_MOD_Q120_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../spqlios/q120/q120_common.h"
|
||||
#include "test_commons.h"
|
||||
|
||||
/** @brief centered modulo q */
|
||||
int64_t centermod(int64_t v, int64_t q);
|
||||
int64_t centermod(uint64_t v, int64_t q);
|
||||
|
||||
/** @brief this class represents an integer mod Q120 */
|
||||
class mod_q120 {
|
||||
public:
|
||||
static constexpr int64_t Qi[] = {Q1, Q2, Q3, Q4};
|
||||
int64_t a[4];
|
||||
mod_q120(int64_t a1, int64_t a2, int64_t a3, int64_t a4);
|
||||
mod_q120();
|
||||
__int128_t to_int128() const;
|
||||
static mod_q120 from_q120a(const void* addr);
|
||||
static mod_q120 from_q120b(const void* addr);
|
||||
static mod_q120 from_q120c(const void* addr);
|
||||
void save_as_q120a(void* dest) const;
|
||||
void save_as_q120b(void* dest) const;
|
||||
void save_as_q120c(void* dest) const;
|
||||
};
|
||||
|
||||
mod_q120 operator+(const mod_q120& x, const mod_q120& y);
|
||||
mod_q120 operator-(const mod_q120& x, const mod_q120& y);
|
||||
mod_q120 operator*(const mod_q120& x, const mod_q120& y);
|
||||
mod_q120& operator+=(mod_q120& x, const mod_q120& y);
|
||||
mod_q120& operator-=(mod_q120& x, const mod_q120& y);
|
||||
mod_q120& operator*=(mod_q120& x, const mod_q120& y);
|
||||
std::ostream& operator<<(std::ostream& out, const mod_q120& x);
|
||||
bool operator==(const mod_q120& x, const mod_q120& y);
|
||||
mod_q120 pow(const mod_q120& x, int32_t k);
|
||||
mod_q120 half(const mod_q120& x);
|
||||
|
||||
/** @brief a uniformly drawn number mod Q120 */
|
||||
mod_q120 uniform_q120();
|
||||
/** @brief a uniformly random mod Q120 layout A (4 integers < 2^32) */
|
||||
void uniform_q120a(void* dest);
|
||||
/** @brief a uniformly random mod Q120 layout B (4 integers < 2^64) */
|
||||
void uniform_q120b(void* dest);
|
||||
/** @brief a uniformly random mod Q120 layout C (4 integers repr. x,2^32x) */
|
||||
void uniform_q120c(void* dest);
|
||||
|
||||
#endif // SPQLIOS_MOD_Q120_H
|
||||
18
spqlios/lib/test/testlib/negacyclic_polynomial.cpp
Normal file
18
spqlios/lib/test/testlib/negacyclic_polynomial.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "negacyclic_polynomial_impl.h"
|
||||
|
||||
// explicit instantiation
|
||||
EXPLICIT_INSTANTIATE_POLYNOMIAL(__int128_t);
|
||||
EXPLICIT_INSTANTIATE_POLYNOMIAL(int64_t);
|
||||
EXPLICIT_INSTANTIATE_POLYNOMIAL(double);
|
||||
|
||||
double infty_dist(const rnx_f64& a, const rnx_f64& b) {
|
||||
const uint64_t nn = a.nn();
|
||||
const double* aa = a.data();
|
||||
const double* bb = b.data();
|
||||
double res = 0.;
|
||||
for (uint64_t i = 0; i < nn; ++i) {
|
||||
double d = fabs(aa[i] - bb[i]);
|
||||
if (d > res) res = d;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
69
spqlios/lib/test/testlib/negacyclic_polynomial.h
Normal file
69
spqlios/lib/test/testlib/negacyclic_polynomial.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef SPQLIOS_NEGACYCLIC_POLYNOMIAL_H
|
||||
#define SPQLIOS_NEGACYCLIC_POLYNOMIAL_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "test_commons.h"
|
||||
|
||||
template <typename T>
|
||||
class polynomial;
|
||||
typedef polynomial<__int128_t> znx_i128;
|
||||
typedef polynomial<int64_t> znx_i64;
|
||||
typedef polynomial<double> rnx_f64;
|
||||
|
||||
template <typename T>
|
||||
class polynomial {
|
||||
public:
|
||||
std::vector<T> coeffs;
|
||||
/** @brief create a polynomial out of existing coeffs */
|
||||
polynomial(uint64_t N, const T* c);
|
||||
/** @brief zero polynomial of dimension N */
|
||||
explicit polynomial(uint64_t N);
|
||||
/** @brief empty polynomial (dim 0) */
|
||||
polynomial();
|
||||
|
||||
/** @brief ring dimension */
|
||||
uint64_t nn() const;
|
||||
/** @brief special setter (accept any indexes, and does the negacyclic translation) */
|
||||
void set_coeff(int64_t i, T v);
|
||||
/** @brief special getter (accept any indexes, and does the negacyclic translation) */
|
||||
T get_coeff(int64_t i) const;
|
||||
/** @brief returns the coefficient layout */
|
||||
T* data();
|
||||
/** @brief returns the coefficient layout (const version) */
|
||||
const T* data() const;
|
||||
/** @brief saves to the layout */
|
||||
void save_as(T* dest) const;
|
||||
/** @brief zero */
|
||||
static polynomial<T> zero(uint64_t n);
|
||||
/** @brief random polynomial with coefficients in [-2^log2bounds, 2^log2bounds]*/
|
||||
static polynomial<T> random_log2bound(uint64_t n, uint64_t log2bound);
|
||||
/** @brief random polynomial with coefficients in [-2^log2bounds, 2^log2bounds]*/
|
||||
static polynomial<T> random(uint64_t n);
|
||||
/** @brief random polynomial with coefficient in [lb;ub] */
|
||||
static polynomial<T> random_bound(uint64_t n, const T lb, const T ub);
|
||||
};
|
||||
|
||||
/** @brief equality operator (used during tests) */
|
||||
template <typename T>
|
||||
bool operator==(const polynomial<T>& a, const polynomial<T>& b);
|
||||
|
||||
/** @brief addition operator (used during tests) */
|
||||
template <typename T>
|
||||
polynomial<T> operator+(const polynomial<T>& a, const polynomial<T>& b);
|
||||
|
||||
/** @brief subtraction operator (used during tests) */
|
||||
template <typename T>
|
||||
polynomial<T> operator-(const polynomial<T>& a, const polynomial<T>& b);
|
||||
|
||||
/** @brief negation operator (used during tests) */
|
||||
template <typename T>
|
||||
polynomial<T> operator-(const polynomial<T>& a);
|
||||
|
||||
template <typename T>
|
||||
polynomial<T> naive_product(const polynomial<T>& a, const polynomial<T>& b);
|
||||
|
||||
/** @brief distance between two real polynomials (used during tests) */
|
||||
double infty_dist(const rnx_f64& a, const rnx_f64& b);
|
||||
|
||||
#endif // SPQLIOS_NEGACYCLIC_POLYNOMIAL_H
|
||||
247
spqlios/lib/test/testlib/negacyclic_polynomial_impl.h
Normal file
247
spqlios/lib/test/testlib/negacyclic_polynomial_impl.h
Normal file
@@ -0,0 +1,247 @@
|
||||
#ifndef SPQLIOS_NEGACYCLIC_POLYNOMIAL_IMPL_H
|
||||
#define SPQLIOS_NEGACYCLIC_POLYNOMIAL_IMPL_H
|
||||
|
||||
#include "negacyclic_polynomial.h"
|
||||
|
||||
template <typename T>
|
||||
polynomial<T>::polynomial(uint64_t N, const T* c) : coeffs(N) {
|
||||
for (uint64_t i = 0; i < N; ++i) coeffs[i] = c[i];
|
||||
}
|
||||
/** @brief zero polynomial of dimension N */
|
||||
template <typename T>
|
||||
polynomial<T>::polynomial(uint64_t N) : coeffs(N, 0) {}
|
||||
/** @brief empty polynomial (dim 0) */
|
||||
template <typename T>
|
||||
polynomial<T>::polynomial() {}
|
||||
|
||||
/** @brief ring dimension */
|
||||
template <typename T>
|
||||
uint64_t polynomial<T>::nn() const {
|
||||
uint64_t n = coeffs.size();
|
||||
REQUIRE_DRAMATICALLY(is_pow2(n), "polynomial dim is not a pow of 2");
|
||||
return n;
|
||||
}
|
||||
|
||||
/** @brief special setter (accept any indexes, and does the negacyclic translation) */
|
||||
template <typename T>
|
||||
void polynomial<T>::set_coeff(int64_t i, T v) {
|
||||
const uint64_t n = nn();
|
||||
const uint64_t _2nm = 2 * n - 1;
|
||||
uint64_t pos = uint64_t(i) & _2nm;
|
||||
if (pos < n) {
|
||||
coeffs[pos] = v;
|
||||
} else {
|
||||
coeffs[pos - n] = -v;
|
||||
}
|
||||
}
|
||||
/** @brief special getter (accept any indexes, and does the negacyclic translation) */
|
||||
template <typename T>
|
||||
T polynomial<T>::get_coeff(int64_t i) const {
|
||||
const uint64_t n = nn();
|
||||
const uint64_t _2nm = 2 * n - 1;
|
||||
uint64_t pos = uint64_t(i) & _2nm;
|
||||
if (pos < n) {
|
||||
return coeffs[pos];
|
||||
} else {
|
||||
return -coeffs[pos - n];
|
||||
}
|
||||
}
|
||||
/** @brief returns the coefficient layout */
|
||||
template <typename T>
|
||||
T* polynomial<T>::data() {
|
||||
return coeffs.data();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void polynomial<T>::save_as(T* dest) const {
|
||||
const uint64_t n = nn();
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
dest[i] = coeffs[i];
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief returns the coefficient layout (const version) */
|
||||
template <typename T>
|
||||
const T* polynomial<T>::data() const {
|
||||
return coeffs.data();
|
||||
}
|
||||
|
||||
/** @brief returns the coefficient layout (const version) */
|
||||
template <typename T>
|
||||
polynomial<T> polynomial<T>::zero(uint64_t n) {
|
||||
return polynomial<T>(n);
|
||||
}
|
||||
|
||||
/** @brief equality operator (used during tests) */
|
||||
template <typename T>
|
||||
bool operator==(const polynomial<T>& a, const polynomial<T>& b) {
|
||||
uint64_t n = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == n, "wrong dimensions");
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
if (a.get_coeff(i) != b.get_coeff(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief addition operator (used during tests) */
|
||||
template <typename T>
|
||||
polynomial<T> operator+(const polynomial<T>& a, const polynomial<T>& b) {
|
||||
uint64_t n = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == n, "wrong dimensions");
|
||||
polynomial<T> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, a.get_coeff(i) + b.get_coeff(i));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @brief subtraction operator (used during tests) */
|
||||
template <typename T>
|
||||
polynomial<T> operator-(const polynomial<T>& a, const polynomial<T>& b) {
|
||||
uint64_t n = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == n, "wrong dimensions");
|
||||
polynomial<T> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, a.get_coeff(i) - b.get_coeff(i));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @brief subtraction operator (used during tests) */
|
||||
template <typename T>
|
||||
polynomial<T> operator-(const polynomial<T>& a) {
|
||||
uint64_t n = a.nn();
|
||||
polynomial<T> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, -a.get_coeff(i));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @brief random polynomial */
|
||||
template <typename T>
|
||||
polynomial<T> random_polynomial(uint64_t n);
|
||||
|
||||
/** @brief random int64 polynomial */
|
||||
template <>
|
||||
polynomial<int64_t> random_polynomial(uint64_t n) {
|
||||
polynomial<int64_t> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, uniform_i64());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @brief random float64 gaussian polynomial */
|
||||
template <>
|
||||
polynomial<double> random_polynomial(uint64_t n) {
|
||||
polynomial<double> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, random_f64_gaussian());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
polynomial<T> random_polynomial_bounds(uint64_t n, const T lb, const T ub);
|
||||
|
||||
/** @brief random int64 polynomial */
|
||||
template <>
|
||||
polynomial<int64_t> random_polynomial_bounds(uint64_t n, const int64_t lb, const int64_t ub) {
|
||||
polynomial<int64_t> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, uniform_i64_bounds(lb, ub));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @brief random float64 gaussian polynomial */
|
||||
template <>
|
||||
polynomial<double> random_polynomial_bounds(uint64_t n, const double lb, const double ub) {
|
||||
polynomial<double> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, uniform_f64_bounds(lb, ub));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @brief random int64 polynomial */
|
||||
template <>
|
||||
polynomial<__int128_t> random_polynomial_bounds(uint64_t n, const __int128_t lb, const __int128_t ub) {
|
||||
polynomial<__int128_t> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, uniform_i128_bounds(lb, ub));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
polynomial<T> random_polynomial_bits(uint64_t n, const uint64_t bits) {
|
||||
T b = UINT64_C(1) << bits;
|
||||
return random_polynomial_bounds(n, -b, b);
|
||||
}
|
||||
|
||||
template <>
|
||||
polynomial<int64_t> polynomial<int64_t>::random_log2bound(uint64_t n, uint64_t log2bound) {
|
||||
polynomial<int64_t> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, uniform_i64_bits(log2bound));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <>
|
||||
polynomial<int64_t> polynomial<int64_t>::random(uint64_t n) {
|
||||
polynomial<int64_t> res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, uniform_u64());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <>
|
||||
polynomial<double> polynomial<double>::random_log2bound(uint64_t n, uint64_t log2bound) {
|
||||
polynomial<double> res(n);
|
||||
double bound = pow(2., log2bound);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, uniform_f64_bounds(-bound, bound));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <>
|
||||
polynomial<double> polynomial<double>::random(uint64_t n) {
|
||||
polynomial<double> res(n);
|
||||
double bound = 2.;
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, uniform_f64_bounds(-bound, bound));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
polynomial<T> naive_product(const polynomial<T>& a, const polynomial<T>& b) {
|
||||
const int64_t nn = a.nn();
|
||||
REQUIRE_DRAMATICALLY(b.nn() == uint64_t(nn), "dimension mismatch!");
|
||||
polynomial<T> res(nn);
|
||||
for (int64_t i = 0; i < nn; ++i) {
|
||||
T ri = 0;
|
||||
for (int64_t j = 0; j < nn; ++j) {
|
||||
ri += a.get_coeff(j) * b.get_coeff(i - j);
|
||||
}
|
||||
res.set_coeff(i, ri);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define EXPLICIT_INSTANTIATE_POLYNOMIAL(TYPE) \
|
||||
template class polynomial<TYPE>; \
|
||||
template bool operator==(const polynomial<TYPE>& a, const polynomial<TYPE>& b); \
|
||||
template polynomial<TYPE> operator+(const polynomial<TYPE>& a, const polynomial<TYPE>& b); \
|
||||
template polynomial<TYPE> operator-(const polynomial<TYPE>& a, const polynomial<TYPE>& b); \
|
||||
template polynomial<TYPE> operator-(const polynomial<TYPE>& a); \
|
||||
template polynomial<TYPE> random_polynomial_bits(uint64_t n, const uint64_t bits); \
|
||||
template polynomial<TYPE> naive_product(const polynomial<TYPE>& a, const polynomial<TYPE>& b); \
|
||||
// template polynomial<TYPE> random_polynomial(uint64_t n);
|
||||
|
||||
#endif // SPQLIOS_NEGACYCLIC_POLYNOMIAL_IMPL_H
|
||||
122
spqlios/lib/test/testlib/ntt120_dft.cpp
Normal file
122
spqlios/lib/test/testlib/ntt120_dft.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "ntt120_dft.h"
|
||||
|
||||
#include "mod_q120.h"
|
||||
|
||||
// @brief alternative version of the NTT
|
||||
|
||||
/** for all s=k/2^17, root_of_unity(s) = omega_0^k */
|
||||
static mod_q120 root_of_unity(double s) {
|
||||
static mod_q120 omega_2pow17{OMEGA1, OMEGA2, OMEGA3, OMEGA4};
|
||||
static double _2pow17 = 1 << 17;
|
||||
return pow(omega_2pow17, s * _2pow17);
|
||||
}
|
||||
static mod_q120 root_of_unity_inv(double s) {
|
||||
static mod_q120 omega_2pow17{OMEGA1, OMEGA2, OMEGA3, OMEGA4};
|
||||
static double _2pow17 = 1 << 17;
|
||||
return pow(omega_2pow17, -s * _2pow17);
|
||||
}
|
||||
|
||||
/** recursive naive ntt */
|
||||
static void q120_ntt_naive_rec(uint64_t n, double entry_pwr, mod_q120* data) {
|
||||
if (n == 1) return;
|
||||
const uint64_t h = n / 2;
|
||||
const double s = entry_pwr / 2.;
|
||||
mod_q120 om = root_of_unity(s);
|
||||
for (uint64_t j = 0; j < h; ++j) {
|
||||
mod_q120 om_right = data[h + j] * om;
|
||||
data[h + j] = data[j] - om_right;
|
||||
data[j] = data[j] + om_right;
|
||||
}
|
||||
q120_ntt_naive_rec(h, s, data);
|
||||
q120_ntt_naive_rec(h, s + 0.5, data + h);
|
||||
}
|
||||
static void q120_intt_naive_rec(uint64_t n, double entry_pwr, mod_q120* data) {
|
||||
if (n == 1) return;
|
||||
const uint64_t h = n / 2;
|
||||
const double s = entry_pwr / 2.;
|
||||
q120_intt_naive_rec(h, s, data);
|
||||
q120_intt_naive_rec(h, s + 0.5, data + h);
|
||||
mod_q120 om = root_of_unity_inv(s);
|
||||
for (uint64_t j = 0; j < h; ++j) {
|
||||
mod_q120 dat_diff = half(data[j] - data[h + j]);
|
||||
data[j] = half(data[j] + data[h + j]);
|
||||
data[h + j] = dat_diff * om;
|
||||
}
|
||||
}
|
||||
|
||||
/** user friendly version */
|
||||
q120_nttvec simple_ntt120(const znx_i64& polynomial) {
|
||||
const uint64_t n = polynomial.nn();
|
||||
q120_nttvec res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
int64_t xi = polynomial.get_coeff(i);
|
||||
res.v[i] = mod_q120(xi, xi, xi, xi);
|
||||
}
|
||||
q120_ntt_naive_rec(n, 0.5, res.v.data());
|
||||
return res;
|
||||
}
|
||||
|
||||
znx_i128 simple_intt120(const q120_nttvec& fftvec) {
|
||||
const uint64_t n = fftvec.nn();
|
||||
q120_nttvec copy = fftvec;
|
||||
znx_i128 res(n);
|
||||
q120_intt_naive_rec(n, 0.5, copy.v.data());
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.set_coeff(i, copy.v[i].to_int128());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
bool operator==(const q120_nttvec& a, const q120_nttvec& b) { return a.v == b.v; }
|
||||
|
||||
std::vector<mod_q120> q120_ntt_naive(const std::vector<mod_q120>& x) {
|
||||
std::vector<mod_q120> res = x;
|
||||
q120_ntt_naive_rec(res.size(), 0.5, res.data());
|
||||
return res;
|
||||
}
|
||||
q120_nttvec::q120_nttvec(uint64_t n) : v(n) {}
|
||||
q120_nttvec::q120_nttvec(uint64_t n, const q120b* data) : v(n) {
|
||||
int64_t* d = (int64_t*)data;
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
v[i] = mod_q120::from_q120b(d + 4 * i);
|
||||
}
|
||||
}
|
||||
q120_nttvec::q120_nttvec(uint64_t n, const q120c* data) : v(n) {
|
||||
int64_t* d = (int64_t*)data;
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
v[i] = mod_q120::from_q120c(d + 4 * i);
|
||||
}
|
||||
}
|
||||
uint64_t q120_nttvec::nn() const { return v.size(); }
|
||||
q120_nttvec q120_nttvec::zero(uint64_t n) { return q120_nttvec(n); }
|
||||
void q120_nttvec::save_as(q120a* dest) const {
|
||||
int64_t* const d = (int64_t*)dest;
|
||||
const uint64_t n = nn();
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
v[i].save_as_q120a(d + 4 * i);
|
||||
}
|
||||
}
|
||||
void q120_nttvec::save_as(q120b* dest) const {
|
||||
int64_t* const d = (int64_t*)dest;
|
||||
const uint64_t n = nn();
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
v[i].save_as_q120b(d + 4 * i);
|
||||
}
|
||||
}
|
||||
void q120_nttvec::save_as(q120c* dest) const {
|
||||
int64_t* const d = (int64_t*)dest;
|
||||
const uint64_t n = nn();
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
v[i].save_as_q120c(d + 4 * i);
|
||||
}
|
||||
}
|
||||
mod_q120 q120_nttvec::get_blk(uint64_t blk) const {
|
||||
REQUIRE_DRAMATICALLY(blk < nn(), "blk overflow");
|
||||
return v[blk];
|
||||
}
|
||||
q120_nttvec q120_nttvec::random(uint64_t n) {
|
||||
q120_nttvec res(n);
|
||||
for (uint64_t i = 0; i < n; ++i) {
|
||||
res.v[i] = uniform_q120();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
31
spqlios/lib/test/testlib/ntt120_dft.h
Normal file
31
spqlios/lib/test/testlib/ntt120_dft.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef SPQLIOS_NTT120_DFT_H
|
||||
#define SPQLIOS_NTT120_DFT_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../../spqlios/q120/q120_arithmetic.h"
|
||||
#include "mod_q120.h"
|
||||
#include "negacyclic_polynomial.h"
|
||||
#include "test_commons.h"
|
||||
|
||||
class q120_nttvec {
|
||||
public:
|
||||
std::vector<mod_q120> v;
|
||||
q120_nttvec() = default;
|
||||
explicit q120_nttvec(uint64_t n);
|
||||
q120_nttvec(uint64_t n, const q120b* data);
|
||||
q120_nttvec(uint64_t n, const q120c* data);
|
||||
uint64_t nn() const;
|
||||
static q120_nttvec zero(uint64_t n);
|
||||
static q120_nttvec random(uint64_t n);
|
||||
void save_as(q120a* dest) const;
|
||||
void save_as(q120b* dest) const;
|
||||
void save_as(q120c* dest) const;
|
||||
mod_q120 get_blk(uint64_t blk) const;
|
||||
};
|
||||
|
||||
q120_nttvec simple_ntt120(const znx_i64& polynomial);
|
||||
znx_i128 simple_intt120(const q120_nttvec& fftvec);
|
||||
bool operator==(const q120_nttvec& a, const q120_nttvec& b);
|
||||
|
||||
#endif // SPQLIOS_NTT120_DFT_H
|
||||
66
spqlios/lib/test/testlib/ntt120_layouts.cpp
Normal file
66
spqlios/lib/test/testlib/ntt120_layouts.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "ntt120_layouts.h"
|
||||
|
||||
mod_q120x2::mod_q120x2() {}
|
||||
mod_q120x2::mod_q120x2(const mod_q120& a, const mod_q120& b) {
|
||||
value[0] = a;
|
||||
value[1] = b;
|
||||
}
|
||||
mod_q120x2::mod_q120x2(q120x2b* addr) {
|
||||
uint64_t* p = (uint64_t*)addr;
|
||||
value[0] = mod_q120::from_q120b(p);
|
||||
value[1] = mod_q120::from_q120b(p + 4);
|
||||
}
|
||||
|
||||
ntt120_vec_znx_dft_layout::ntt120_vec_znx_dft_layout(uint64_t n, uint64_t size)
|
||||
: nn(n), //
|
||||
size(size), //
|
||||
data((VEC_ZNX_DFT*)alloc64(n * size * 4 * sizeof(uint64_t))) {}
|
||||
|
||||
mod_q120x2 ntt120_vec_znx_dft_layout::get_copy_zext(uint64_t idx, uint64_t blk) {
|
||||
return mod_q120x2(get_blk(idx, blk));
|
||||
}
|
||||
q120x2b* ntt120_vec_znx_dft_layout::get_blk(uint64_t idx, uint64_t blk) {
|
||||
REQUIRE_DRAMATICALLY(idx < size, "idx overflow");
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 2, "blk overflow");
|
||||
uint64_t* d = (uint64_t*)data;
|
||||
return (q120x2b*)(d + 4 * nn * idx + 8 * blk);
|
||||
}
|
||||
ntt120_vec_znx_dft_layout::~ntt120_vec_znx_dft_layout() { spqlios_free(data); }
|
||||
q120_nttvec ntt120_vec_znx_dft_layout::get_copy_zext(uint64_t idx) {
|
||||
int64_t* d = (int64_t*)data;
|
||||
if (idx < size) {
|
||||
return q120_nttvec(nn, (q120b*)(d + idx * nn * 4));
|
||||
} else {
|
||||
return q120_nttvec::zero(nn);
|
||||
}
|
||||
}
|
||||
void ntt120_vec_znx_dft_layout::set(uint64_t idx, const q120_nttvec& value) {
|
||||
REQUIRE_DRAMATICALLY(idx < size, "index overflow: " << idx << " / " << size);
|
||||
q120b* dest_addr = (q120b*)((int64_t*)data + idx * nn * 4);
|
||||
value.save_as(dest_addr);
|
||||
}
|
||||
void ntt120_vec_znx_dft_layout::fill_random() {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
set(i, q120_nttvec::random(nn));
|
||||
}
|
||||
}
|
||||
thash ntt120_vec_znx_dft_layout::content_hash() const { return test_hash(data, nn * size * 4 * sizeof(int64_t)); }
|
||||
ntt120_vec_znx_big_layout::ntt120_vec_znx_big_layout(uint64_t n, uint64_t size)
|
||||
: nn(n), //
|
||||
size(size),
|
||||
data((VEC_ZNX_BIG*)alloc64(n * size * sizeof(__int128_t))) {}
|
||||
|
||||
znx_i128 ntt120_vec_znx_big_layout::get_copy(uint64_t index) const { return znx_i128(nn, get_addr(index)); }
|
||||
znx_i128 ntt120_vec_znx_big_layout::get_copy_zext(uint64_t index) const {
|
||||
if (index < size) {
|
||||
return znx_i128(nn, get_addr(index));
|
||||
} else {
|
||||
return znx_i128::zero(nn);
|
||||
}
|
||||
}
|
||||
__int128* ntt120_vec_znx_big_layout::get_addr(uint64_t index) const {
|
||||
REQUIRE_DRAMATICALLY(index < size, "index overflow: " << index << " / " << size);
|
||||
return (__int128_t*)data + index * nn;
|
||||
}
|
||||
void ntt120_vec_znx_big_layout::set(uint64_t index, const znx_i128& value) { value.save_as(get_addr(index)); }
|
||||
ntt120_vec_znx_big_layout::~ntt120_vec_znx_big_layout() { spqlios_free(data); }
|
||||
103
spqlios/lib/test/testlib/ntt120_layouts.h
Normal file
103
spqlios/lib/test/testlib/ntt120_layouts.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef SPQLIOS_NTT120_LAYOUTS_H
|
||||
#define SPQLIOS_NTT120_LAYOUTS_H
|
||||
|
||||
#include "../../spqlios/arithmetic/vec_znx_arithmetic.h"
|
||||
#include "mod_q120.h"
|
||||
#include "negacyclic_polynomial.h"
|
||||
#include "ntt120_dft.h"
|
||||
#include "test_commons.h"
|
||||
|
||||
struct q120b_vector_view {};
|
||||
|
||||
struct mod_q120x2 {
|
||||
mod_q120 value[2];
|
||||
mod_q120x2();
|
||||
mod_q120x2(const mod_q120& a, const mod_q120& b);
|
||||
mod_q120x2(__int128_t value);
|
||||
explicit mod_q120x2(q120x2b* addr);
|
||||
explicit mod_q120x2(q120x2c* addr);
|
||||
void save_as(q120x2b* addr) const;
|
||||
void save_as(q120x2c* addr) const;
|
||||
static mod_q120x2 random();
|
||||
};
|
||||
mod_q120x2 operator+(const mod_q120x2& a, const mod_q120x2& b);
|
||||
mod_q120x2 operator-(const mod_q120x2& a, const mod_q120x2& b);
|
||||
mod_q120x2 operator*(const mod_q120x2& a, const mod_q120x2& b);
|
||||
bool operator==(const mod_q120x2& a, const mod_q120x2& b);
|
||||
bool operator!=(const mod_q120x2& a, const mod_q120x2& b);
|
||||
mod_q120x2& operator+=(mod_q120x2& a, const mod_q120x2& b);
|
||||
mod_q120x2& operator-=(mod_q120x2& a, const mod_q120x2& b);
|
||||
|
||||
/** @brief test layout for the VEC_ZNX_DFT */
|
||||
struct ntt120_vec_znx_dft_layout {
|
||||
const uint64_t nn;
|
||||
const uint64_t size;
|
||||
VEC_ZNX_DFT* const data;
|
||||
ntt120_vec_znx_dft_layout(uint64_t n, uint64_t size);
|
||||
mod_q120x2 get_copy_zext(uint64_t idx, uint64_t blk);
|
||||
q120_nttvec get_copy_zext(uint64_t idx);
|
||||
void set(uint64_t idx, const q120_nttvec& v);
|
||||
q120x2b* get_blk(uint64_t idx, uint64_t blk);
|
||||
thash content_hash() const;
|
||||
void fill_random();
|
||||
~ntt120_vec_znx_dft_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the VEC_ZNX_BIG */
|
||||
class ntt120_vec_znx_big_layout {
|
||||
public:
|
||||
const uint64_t nn;
|
||||
const uint64_t size;
|
||||
VEC_ZNX_BIG* const data;
|
||||
ntt120_vec_znx_big_layout(uint64_t n, uint64_t size);
|
||||
|
||||
private:
|
||||
__int128* get_addr(uint64_t index) const;
|
||||
|
||||
public:
|
||||
znx_i128 get_copy(uint64_t index) const;
|
||||
znx_i128 get_copy_zext(uint64_t index) const;
|
||||
void set(uint64_t index, const znx_i128& value);
|
||||
~ntt120_vec_znx_big_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the VMP_PMAT */
|
||||
class ntt120_vmp_pmat_layout {
|
||||
const uint64_t nn;
|
||||
const uint64_t nrows;
|
||||
const uint64_t ncols;
|
||||
VMP_PMAT* const data;
|
||||
ntt120_vmp_pmat_layout(uint64_t n, uint64_t nrows, uint64_t ncols);
|
||||
mod_q120x2 get(uint64_t row, uint64_t col, uint64_t blk) const;
|
||||
~ntt120_vmp_pmat_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the SVP_PPOL */
|
||||
class ntt120_svp_ppol_layout {
|
||||
const uint64_t nn;
|
||||
SVP_PPOL* const data;
|
||||
ntt120_svp_ppol_layout(uint64_t n);
|
||||
~ntt120_svp_ppol_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the CNV_PVEC_L */
|
||||
class ntt120_cnv_left_layout {
|
||||
const uint64_t nn;
|
||||
const uint64_t size;
|
||||
CNV_PVEC_L* const data;
|
||||
ntt120_cnv_left_layout(uint64_t n, uint64_t size);
|
||||
mod_q120x2 get(uint64_t idx, uint64_t blk);
|
||||
~ntt120_cnv_left_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the CNV_PVEC_R */
|
||||
class ntt120_cnv_right_layout {
|
||||
const uint64_t nn;
|
||||
const uint64_t size;
|
||||
CNV_PVEC_R* const data;
|
||||
ntt120_cnv_right_layout(uint64_t n, uint64_t size);
|
||||
mod_q120x2 get(uint64_t idx, uint64_t blk);
|
||||
~ntt120_cnv_right_layout();
|
||||
};
|
||||
|
||||
#endif // SPQLIOS_NTT120_LAYOUTS_H
|
||||
69
spqlios/lib/test/testlib/polynomial_vector.cpp
Normal file
69
spqlios/lib/test/testlib/polynomial_vector.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "polynomial_vector.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifdef VALGRIND_MEM_TESTS
|
||||
#include "valgrind/memcheck.h"
|
||||
#endif
|
||||
|
||||
#define CANARY_PADDING (1024)
|
||||
#define GARBAGE_VALUE (242)
|
||||
|
||||
znx_vec_i64_layout::znx_vec_i64_layout(uint64_t n, uint64_t size, uint64_t slice) : n(n), size(size), slice(slice) {
|
||||
REQUIRE_DRAMATICALLY(is_pow2(n), "not a power of 2" << n);
|
||||
REQUIRE_DRAMATICALLY(slice >= n, "slice too small" << slice << " < " << n);
|
||||
this->region = (uint8_t*)malloc(size * slice * sizeof(int64_t) + 2 * CANARY_PADDING);
|
||||
this->data_start = (int64_t*)(region + CANARY_PADDING);
|
||||
// ensure that any invalid value is kind-of garbage
|
||||
memset(region, GARBAGE_VALUE, size * slice * sizeof(int64_t) + 2 * CANARY_PADDING);
|
||||
// mark inter-slice memory as non accessible
|
||||
#ifdef VALGRIND_MEM_TESTS
|
||||
VALGRIND_MAKE_MEM_NOACCESS(region, CANARY_PADDING);
|
||||
VALGRIND_MAKE_MEM_NOACCESS(region + size * slice * sizeof(int64_t) + CANARY_PADDING, CANARY_PADDING);
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(data_start + i * slice, n * sizeof(int64_t));
|
||||
}
|
||||
if (size != slice) {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
VALGRIND_MAKE_MEM_NOACCESS(data_start + i * slice + n, (slice - n) * sizeof(int64_t));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
znx_vec_i64_layout::~znx_vec_i64_layout() { free(region); }
|
||||
|
||||
znx_i64 znx_vec_i64_layout::get_copy_zext(uint64_t index) const {
|
||||
if (index < size) {
|
||||
return znx_i64(n, data_start + index * slice);
|
||||
} else {
|
||||
return znx_i64::zero(n);
|
||||
}
|
||||
}
|
||||
|
||||
znx_i64 znx_vec_i64_layout::get_copy(uint64_t index) const {
|
||||
REQUIRE_DRAMATICALLY(index < size, "index overflow: " << index << " / " << size);
|
||||
return znx_i64(n, data_start + index * slice);
|
||||
}
|
||||
|
||||
void znx_vec_i64_layout::set(uint64_t index, const znx_i64& elem) {
|
||||
REQUIRE_DRAMATICALLY(index < size, "index overflow: " << index << " / " << size);
|
||||
REQUIRE_DRAMATICALLY(elem.nn() == n, "incompatible ring dimensions: " << elem.nn() << " / " << n);
|
||||
elem.save_as(data_start + index * slice);
|
||||
}
|
||||
|
||||
int64_t* znx_vec_i64_layout::data() { return data_start; }
|
||||
const int64_t* znx_vec_i64_layout::data() const { return data_start; }
|
||||
|
||||
void znx_vec_i64_layout::fill_random(uint64_t bits) {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
set(i, znx_i64::random_log2bound(n, bits));
|
||||
}
|
||||
}
|
||||
__uint128_t znx_vec_i64_layout::content_hash() const {
|
||||
test_hasher hasher;
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
hasher.update(data() + i * slice, n * sizeof(int64_t));
|
||||
}
|
||||
return hasher.hash();
|
||||
}
|
||||
42
spqlios/lib/test/testlib/polynomial_vector.h
Normal file
42
spqlios/lib/test/testlib/polynomial_vector.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef SPQLIOS_POLYNOMIAL_VECTOR_H
|
||||
#define SPQLIOS_POLYNOMIAL_VECTOR_H
|
||||
|
||||
#include "negacyclic_polynomial.h"
|
||||
#include "test_commons.h"
|
||||
|
||||
/** @brief a test memory layout for znx i64 polynomials vectors */
|
||||
class znx_vec_i64_layout {
|
||||
uint64_t n;
|
||||
uint64_t size;
|
||||
uint64_t slice;
|
||||
int64_t* data_start;
|
||||
uint8_t* region;
|
||||
|
||||
public:
|
||||
// NO-COPY structure
|
||||
znx_vec_i64_layout(const znx_vec_i64_layout&) = delete;
|
||||
void operator=(const znx_vec_i64_layout&) = delete;
|
||||
znx_vec_i64_layout(znx_vec_i64_layout&&) = delete;
|
||||
void operator=(znx_vec_i64_layout&&) = delete;
|
||||
/** @brief initialises a memory layout */
|
||||
znx_vec_i64_layout(uint64_t n, uint64_t size, uint64_t slice);
|
||||
/** @brief destructor */
|
||||
~znx_vec_i64_layout();
|
||||
|
||||
/** @brief get a copy of item index index (extended with zeros) */
|
||||
znx_i64 get_copy_zext(uint64_t index) const;
|
||||
/** @brief get a copy of item index index (extended with zeros) */
|
||||
znx_i64 get_copy(uint64_t index) const;
|
||||
/** @brief get a copy of item index index (index<size) */
|
||||
void set(uint64_t index, const znx_i64& elem);
|
||||
/** @brief fill with random values */
|
||||
void fill_random(uint64_t bits = 63);
|
||||
/** @brief raw pointer access */
|
||||
int64_t* data();
|
||||
/** @brief raw pointer access (const version) */
|
||||
const int64_t* data() const;
|
||||
/** @brief content hashcode */
|
||||
__uint128_t content_hash() const;
|
||||
};
|
||||
|
||||
#endif // SPQLIOS_POLYNOMIAL_VECTOR_H
|
||||
55
spqlios/lib/test/testlib/random.cpp
Normal file
55
spqlios/lib/test/testlib/random.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
|
||||
#include "test_commons.h"
|
||||
|
||||
bool is_pow2(uint64_t n) { return !(n & (n - 1)); }
|
||||
|
||||
test_rng& randgen() {
|
||||
static test_rng gen;
|
||||
return gen;
|
||||
}
|
||||
uint64_t uniform_u64() {
|
||||
static std::uniform_int_distribution<uint64_t> dist64(0, UINT64_MAX);
|
||||
return dist64(randgen());
|
||||
}
|
||||
|
||||
uint64_t uniform_u64_bits(uint64_t nbits) {
|
||||
if (nbits >= 64) return uniform_u64();
|
||||
return uniform_u64() >> (64 - nbits);
|
||||
}
|
||||
|
||||
int64_t uniform_i64() {
|
||||
std::uniform_int_distribution<int64_t> dist;
|
||||
return dist(randgen());
|
||||
}
|
||||
|
||||
int64_t uniform_i64_bits(uint64_t nbits) {
|
||||
int64_t bound = int64_t(1) << nbits;
|
||||
std::uniform_int_distribution<int64_t> dist(-bound, bound);
|
||||
return dist(randgen());
|
||||
}
|
||||
|
||||
int64_t uniform_i64_bounds(const int64_t lb, const int64_t ub) {
|
||||
std::uniform_int_distribution<int64_t> dist(lb, ub);
|
||||
return dist(randgen());
|
||||
}
|
||||
|
||||
__int128_t uniform_i128_bounds(const __int128_t lb, const __int128_t ub) {
|
||||
std::uniform_int_distribution<__int128_t> dist(lb, ub);
|
||||
return dist(randgen());
|
||||
}
|
||||
|
||||
double random_f64_gaussian(double stdev) {
|
||||
std::normal_distribution<double> dist(0, stdev);
|
||||
return dist(randgen());
|
||||
}
|
||||
|
||||
double uniform_f64_bounds(const double lb, const double ub) {
|
||||
std::uniform_real_distribution<double> dist(lb, ub);
|
||||
return dist(randgen());
|
||||
}
|
||||
|
||||
double uniform_f64_01() {
|
||||
return uniform_f64_bounds(0, 1);
|
||||
}
|
||||
145
spqlios/lib/test/testlib/reim4_elem.cpp
Normal file
145
spqlios/lib/test/testlib/reim4_elem.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
#include "reim4_elem.h"
|
||||
|
||||
reim4_elem::reim4_elem(const double* re, const double* im) {
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
value[i] = re[i];
|
||||
value[4 + i] = im[i];
|
||||
}
|
||||
}
|
||||
reim4_elem::reim4_elem(const double* layout) {
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
value[i] = layout[i];
|
||||
}
|
||||
}
|
||||
reim4_elem::reim4_elem() {
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
value[i] = 0.;
|
||||
}
|
||||
}
|
||||
void reim4_elem::save_re_im(double* re, double* im) const {
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
re[i] = value[i];
|
||||
im[i] = value[4 + i];
|
||||
}
|
||||
}
|
||||
void reim4_elem::save_as(double* reim4) const {
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
reim4[i] = value[i];
|
||||
}
|
||||
}
|
||||
reim4_elem reim4_elem::zero() { return reim4_elem(); }
|
||||
|
||||
bool operator==(const reim4_elem& x, const reim4_elem& y) {
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
if (x.value[i] != y.value[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
reim4_elem gaussian_reim4() {
|
||||
test_rng& gen = randgen();
|
||||
std::normal_distribution<double> dist(0, 1);
|
||||
reim4_elem res;
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
res.value[i] = dist(gen);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
reim4_array_view::reim4_array_view(uint64_t size, double* data) : size(size), data(data) {}
|
||||
reim4_elem reim4_array_view::get(uint64_t i) const {
|
||||
REQUIRE_DRAMATICALLY(i < size, "reim4 array overflow");
|
||||
return reim4_elem(data + 8 * i);
|
||||
}
|
||||
void reim4_array_view::set(uint64_t i, const reim4_elem& value) {
|
||||
REQUIRE_DRAMATICALLY(i < size, "reim4 array overflow");
|
||||
value.save_as(data + 8 * i);
|
||||
}
|
||||
|
||||
reim_view::reim_view(uint64_t m, double* data) : m(m), data(data) {}
|
||||
reim4_elem reim_view::get_blk(uint64_t i) {
|
||||
REQUIRE_DRAMATICALLY(i < m / 4, "block overflow");
|
||||
return reim4_elem(data + 4 * i, data + m + 4 * i);
|
||||
}
|
||||
void reim_view::set_blk(uint64_t i, const reim4_elem& value) {
|
||||
REQUIRE_DRAMATICALLY(i < m / 4, "block overflow");
|
||||
value.save_re_im(data + 4 * i, data + m + 4 * i);
|
||||
}
|
||||
|
||||
reim_vector_view::reim_vector_view(uint64_t m, uint64_t nrows, double* data) : m(m), nrows(nrows), data(data) {}
|
||||
reim_view reim_vector_view::row(uint64_t row) {
|
||||
REQUIRE_DRAMATICALLY(row < nrows, "row overflow");
|
||||
return reim_view(m, data + 2 * m * row);
|
||||
}
|
||||
|
||||
/** @brief addition */
|
||||
reim4_elem operator+(const reim4_elem& x, const reim4_elem& y) {
|
||||
reim4_elem reps;
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
reps.value[i] = x.value[i] + y.value[i];
|
||||
}
|
||||
return reps;
|
||||
}
|
||||
reim4_elem& operator+=(reim4_elem& x, const reim4_elem& y) {
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
x.value[i] += y.value[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
/** @brief subtraction */
|
||||
reim4_elem operator-(const reim4_elem& x, const reim4_elem& y) {
|
||||
reim4_elem reps;
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
reps.value[i] = x.value[i] + y.value[i];
|
||||
}
|
||||
return reps;
|
||||
}
|
||||
reim4_elem& operator-=(reim4_elem& x, const reim4_elem& y) {
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
x.value[i] -= y.value[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
/** @brief product */
|
||||
reim4_elem operator*(const reim4_elem& x, const reim4_elem& y) {
|
||||
reim4_elem reps;
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
double xre = x.value[i];
|
||||
double yre = y.value[i];
|
||||
double xim = x.value[i + 4];
|
||||
double yim = y.value[i + 4];
|
||||
reps.value[i] = xre * yre - xim * yim;
|
||||
reps.value[i + 4] = xre * yim + xim * yre;
|
||||
}
|
||||
return reps;
|
||||
}
|
||||
/** @brief distance in infty norm */
|
||||
double infty_dist(const reim4_elem& x, const reim4_elem& y) {
|
||||
double dist = 0;
|
||||
for (uint64_t i = 0; i < 8; ++i) {
|
||||
double d = fabs(x.value[i] - y.value[i]);
|
||||
if (d > dist) dist = d;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const reim4_elem& x) {
|
||||
out << "[\n";
|
||||
for (uint64_t i = 0; i < 4; ++i) {
|
||||
out << " re=" << x.value[i] << ", im=" << x.value[i + 4] << "\n";
|
||||
}
|
||||
return out << "]";
|
||||
}
|
||||
|
||||
reim4_matrix_view::reim4_matrix_view(uint64_t nrows, uint64_t ncols, double* data)
|
||||
: nrows(nrows), ncols(ncols), data(data) {}
|
||||
reim4_elem reim4_matrix_view::get(uint64_t row, uint64_t col) const {
|
||||
REQUIRE_DRAMATICALLY(row < nrows, "rows out of bounds" << row << " / " << nrows);
|
||||
REQUIRE_DRAMATICALLY(col < ncols, "cols out of bounds" << col << " / " << ncols);
|
||||
return reim4_elem(data + 8 * (row * ncols + col));
|
||||
}
|
||||
void reim4_matrix_view::set(uint64_t row, uint64_t col, const reim4_elem& value) {
|
||||
REQUIRE_DRAMATICALLY(row < nrows, "rows out of bounds" << row << " / " << nrows);
|
||||
REQUIRE_DRAMATICALLY(col < ncols, "cols out of bounds" << col << " / " << ncols);
|
||||
value.save_as(data + 8 * (row * ncols + col));
|
||||
}
|
||||
95
spqlios/lib/test/testlib/reim4_elem.h
Normal file
95
spqlios/lib/test/testlib/reim4_elem.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifndef SPQLIOS_REIM4_ELEM_H
|
||||
#define SPQLIOS_REIM4_ELEM_H
|
||||
|
||||
#include "test_commons.h"
|
||||
|
||||
/** @brief test class representing one single reim4 element */
|
||||
class reim4_elem {
|
||||
public:
|
||||
/** @brief 8 components (4 real parts followed by 4 imag parts) */
|
||||
double value[8];
|
||||
/** @brief constructs from 4 real parts and 4 imaginary parts */
|
||||
reim4_elem(const double* re, const double* im);
|
||||
/** @brief constructs from 8 components */
|
||||
explicit reim4_elem(const double* layout);
|
||||
/** @brief zero */
|
||||
reim4_elem();
|
||||
/** @brief saves the real parts to re and the 4 imag to im */
|
||||
void save_re_im(double* re, double* im) const;
|
||||
/** @brief saves the 8 components to reim4 */
|
||||
void save_as(double* reim4) const;
|
||||
static reim4_elem zero();
|
||||
};
|
||||
|
||||
/** @brief checks for equality */
|
||||
bool operator==(const reim4_elem& x, const reim4_elem& y);
|
||||
/** @brief random gaussian reim4 of stdev 1 and mean 0 */
|
||||
reim4_elem gaussian_reim4();
|
||||
/** @brief addition */
|
||||
reim4_elem operator+(const reim4_elem& x, const reim4_elem& y);
|
||||
reim4_elem& operator+=(reim4_elem& x, const reim4_elem& y);
|
||||
/** @brief subtraction */
|
||||
reim4_elem operator-(const reim4_elem& x, const reim4_elem& y);
|
||||
reim4_elem& operator-=(reim4_elem& x, const reim4_elem& y);
|
||||
/** @brief product */
|
||||
reim4_elem operator*(const reim4_elem& x, const reim4_elem& y);
|
||||
std::ostream& operator<<(std::ostream& out, const reim4_elem& x);
|
||||
/** @brief distance in infty norm */
|
||||
double infty_dist(const reim4_elem& x, const reim4_elem& y);
|
||||
|
||||
/** @brief test class representing the view of one reim of m complexes */
|
||||
class reim4_array_view {
|
||||
uint64_t size; ///< size of the reim array
|
||||
double* data; ///< pointer to the start of the array
|
||||
public:
|
||||
/** @brief ininitializes a view at an existing given address */
|
||||
reim4_array_view(uint64_t size, double* data);
|
||||
;
|
||||
/** @brief gets the i-th element */
|
||||
reim4_elem get(uint64_t i) const;
|
||||
/** @brief sets the i-th element */
|
||||
void set(uint64_t i, const reim4_elem& value);
|
||||
};
|
||||
|
||||
/** @brief test class representing the view of one matrix of nrowsxncols reim4's */
|
||||
class reim4_matrix_view {
|
||||
uint64_t nrows; ///< number of rows
|
||||
uint64_t ncols; ///< number of columns
|
||||
double* data; ///< pointer to the start of the matrix
|
||||
public:
|
||||
/** @brief ininitializes a view at an existing given address */
|
||||
reim4_matrix_view(uint64_t nrows, uint64_t ncols, double* data);
|
||||
/** @brief gets the i-th element */
|
||||
reim4_elem get(uint64_t row, uint64_t col) const;
|
||||
/** @brief sets the i-th element */
|
||||
void set(uint64_t row, uint64_t col, const reim4_elem& value);
|
||||
};
|
||||
|
||||
/** @brief test class representing the view of one reim of m complexes */
|
||||
class reim_view {
|
||||
uint64_t m; ///< (complex) dimension of the reim polynomial
|
||||
double* data; ///< address of the start of the reim polynomial
|
||||
public:
|
||||
/** @brief ininitializes a view at an existing given address */
|
||||
reim_view(uint64_t m, double* data);
|
||||
;
|
||||
/** @brief extracts the i-th reim4 block (i<m/4) */
|
||||
reim4_elem get_blk(uint64_t i);
|
||||
/** @brief sets the i-th reim4 block (i<m/4) */
|
||||
void set_blk(uint64_t i, const reim4_elem& value);
|
||||
};
|
||||
|
||||
/** @brief view of one contiguous reim vector */
|
||||
class reim_vector_view {
|
||||
uint64_t m; ///< (complex) dimension of the reim polynomial
|
||||
uint64_t nrows; ///< number of reim polynomials
|
||||
double* data; ///< address of the start of the reim polynomial
|
||||
|
||||
public:
|
||||
/** @brief ininitializes a view at an existing given address */
|
||||
reim_vector_view(uint64_t m, uint64_t nrows, double* data);
|
||||
/** @brief view of the given reim */
|
||||
reim_view row(uint64_t row);
|
||||
};
|
||||
|
||||
#endif // SPQLIOS_REIM4_ELEM_H
|
||||
168
spqlios/lib/test/testlib/sha3.c
Normal file
168
spqlios/lib/test/testlib/sha3.c
Normal file
@@ -0,0 +1,168 @@
|
||||
// sha3.c
|
||||
// 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi>
|
||||
// https://github.com/mjosaarinen/tiny_sha3
|
||||
// LICENSE: MIT
|
||||
|
||||
// Revised 07-Aug-15 to match with official release of FIPS PUB 202 "SHA3"
|
||||
// Revised 03-Sep-15 for portability + OpenSSL - style API
|
||||
|
||||
#include "sha3.h"
|
||||
|
||||
// update the state with given number of rounds
|
||||
|
||||
void sha3_keccakf(uint64_t st[25]) {
|
||||
// constants
|
||||
const uint64_t keccakf_rndc[24] = {0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
|
||||
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
|
||||
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
|
||||
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
|
||||
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008};
|
||||
const int keccakf_rotc[24] = {1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44};
|
||||
const int keccakf_piln[24] = {10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1};
|
||||
|
||||
// variables
|
||||
int i, j, r;
|
||||
uint64_t t, bc[5];
|
||||
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
uint8_t* v;
|
||||
|
||||
// endianess conversion. this is redundant on little-endian targets
|
||||
for (i = 0; i < 25; i++) {
|
||||
v = (uint8_t*)&st[i];
|
||||
st[i] = ((uint64_t)v[0]) | (((uint64_t)v[1]) << 8) | (((uint64_t)v[2]) << 16) | (((uint64_t)v[3]) << 24) |
|
||||
(((uint64_t)v[4]) << 32) | (((uint64_t)v[5]) << 40) | (((uint64_t)v[6]) << 48) | (((uint64_t)v[7]) << 56);
|
||||
}
|
||||
#endif
|
||||
|
||||
// actual iteration
|
||||
for (r = 0; r < KECCAKF_ROUNDS; r++) {
|
||||
// Theta
|
||||
for (i = 0; i < 5; i++) bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1);
|
||||
for (j = 0; j < 25; j += 5) st[j + i] ^= t;
|
||||
}
|
||||
|
||||
// Rho Pi
|
||||
t = st[1];
|
||||
for (i = 0; i < 24; i++) {
|
||||
j = keccakf_piln[i];
|
||||
bc[0] = st[j];
|
||||
st[j] = ROTL64(t, keccakf_rotc[i]);
|
||||
t = bc[0];
|
||||
}
|
||||
|
||||
// Chi
|
||||
for (j = 0; j < 25; j += 5) {
|
||||
for (i = 0; i < 5; i++) bc[i] = st[j + i];
|
||||
for (i = 0; i < 5; i++) st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
|
||||
}
|
||||
|
||||
// Iota
|
||||
st[0] ^= keccakf_rndc[r];
|
||||
}
|
||||
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
// endianess conversion. this is redundant on little-endian targets
|
||||
for (i = 0; i < 25; i++) {
|
||||
v = (uint8_t*)&st[i];
|
||||
t = st[i];
|
||||
v[0] = t & 0xFF;
|
||||
v[1] = (t >> 8) & 0xFF;
|
||||
v[2] = (t >> 16) & 0xFF;
|
||||
v[3] = (t >> 24) & 0xFF;
|
||||
v[4] = (t >> 32) & 0xFF;
|
||||
v[5] = (t >> 40) & 0xFF;
|
||||
v[6] = (t >> 48) & 0xFF;
|
||||
v[7] = (t >> 56) & 0xFF;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Initialize the context for SHA3
|
||||
|
||||
int sha3_init(sha3_ctx_t* c, int mdlen) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 25; i++) c->st.q[i] = 0;
|
||||
c->mdlen = mdlen;
|
||||
c->rsiz = 200 - 2 * mdlen;
|
||||
c->pt = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// update state with more data
|
||||
|
||||
int sha3_update(sha3_ctx_t* c, const void* data, size_t len) {
|
||||
size_t i;
|
||||
int j;
|
||||
|
||||
j = c->pt;
|
||||
for (i = 0; i < len; i++) {
|
||||
c->st.b[j++] ^= ((const uint8_t*)data)[i];
|
||||
if (j >= c->rsiz) {
|
||||
sha3_keccakf(c->st.q);
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
c->pt = j;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// finalize and output a hash
|
||||
|
||||
int sha3_final(void* md, sha3_ctx_t* c) {
|
||||
int i;
|
||||
|
||||
c->st.b[c->pt] ^= 0x06;
|
||||
c->st.b[c->rsiz - 1] ^= 0x80;
|
||||
sha3_keccakf(c->st.q);
|
||||
|
||||
for (i = 0; i < c->mdlen; i++) {
|
||||
((uint8_t*)md)[i] = c->st.b[i];
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// compute a SHA-3 hash (md) of given byte length from "in"
|
||||
|
||||
void* sha3(const void* in, size_t inlen, void* md, int mdlen) {
|
||||
sha3_ctx_t sha3;
|
||||
|
||||
sha3_init(&sha3, mdlen);
|
||||
sha3_update(&sha3, in, inlen);
|
||||
sha3_final(md, &sha3);
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
// SHAKE128 and SHAKE256 extensible-output functionality
|
||||
|
||||
void shake_xof(sha3_ctx_t* c) {
|
||||
c->st.b[c->pt] ^= 0x1F;
|
||||
c->st.b[c->rsiz - 1] ^= 0x80;
|
||||
sha3_keccakf(c->st.q);
|
||||
c->pt = 0;
|
||||
}
|
||||
|
||||
void shake_out(sha3_ctx_t* c, void* out, size_t len) {
|
||||
size_t i;
|
||||
int j;
|
||||
|
||||
j = c->pt;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (j >= c->rsiz) {
|
||||
sha3_keccakf(c->st.q);
|
||||
j = 0;
|
||||
}
|
||||
((uint8_t*)out)[i] = c->st.b[j++];
|
||||
}
|
||||
c->pt = j;
|
||||
}
|
||||
56
spqlios/lib/test/testlib/sha3.h
Normal file
56
spqlios/lib/test/testlib/sha3.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// sha3.h
|
||||
// 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi>
|
||||
// https://github.com/mjosaarinen/tiny_sha3
|
||||
// License: MIT
|
||||
|
||||
#ifndef SHA3_H
|
||||
#define SHA3_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef KECCAKF_ROUNDS
|
||||
#define KECCAKF_ROUNDS 24
|
||||
#endif
|
||||
|
||||
#ifndef ROTL64
|
||||
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
|
||||
#endif
|
||||
|
||||
// state context
|
||||
typedef struct {
|
||||
union { // state:
|
||||
uint8_t b[200]; // 8-bit bytes
|
||||
uint64_t q[25]; // 64-bit words
|
||||
} st;
|
||||
int pt, rsiz, mdlen; // these don't overflow
|
||||
} sha3_ctx_t;
|
||||
|
||||
// Compression function.
|
||||
void sha3_keccakf(uint64_t st[25]);
|
||||
|
||||
// OpenSSL - like interfece
|
||||
int sha3_init(sha3_ctx_t* c, int mdlen); // mdlen = hash output in bytes
|
||||
int sha3_update(sha3_ctx_t* c, const void* data, size_t len);
|
||||
int sha3_final(void* md, sha3_ctx_t* c); // digest goes to md
|
||||
|
||||
// compute a sha3 hash (md) of given byte length from "in"
|
||||
void* sha3(const void* in, size_t inlen, void* md, int mdlen);
|
||||
|
||||
// SHAKE128 and SHAKE256 extensible-output functions
|
||||
#define shake128_init(c) sha3_init(c, 16)
|
||||
#define shake256_init(c) sha3_init(c, 32)
|
||||
#define shake_update sha3_update
|
||||
|
||||
void shake_xof(sha3_ctx_t* c);
|
||||
void shake_out(sha3_ctx_t* c, void* out, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SHA3_H
|
||||
10
spqlios/lib/test/testlib/test_commons.cpp
Normal file
10
spqlios/lib/test/testlib/test_commons.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "test_commons.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, __int128_t x) {
|
||||
char c[35] = {0};
|
||||
snprintf(c, 35, "0x%016" PRIx64 "%016" PRIx64, uint64_t(x >> 64), uint64_t(x));
|
||||
return out << c;
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& out, __uint128_t x) { return out << __int128_t(x); }
|
||||
74
spqlios/lib/test/testlib/test_commons.h
Normal file
74
spqlios/lib/test/testlib/test_commons.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef SPQLIOS_TEST_COMMONS_H
|
||||
#define SPQLIOS_TEST_COMMONS_H
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
#include "../../spqlios/commons.h"
|
||||
|
||||
/** @brief macro that crashes if the condition are not met */
|
||||
#define REQUIRE_DRAMATICALLY(req_contition, error_msg) \
|
||||
do { \
|
||||
if (!(req_contition)) { \
|
||||
std::cerr << "REQUIREMENT FAILED at " << __FILE__ << ":" << __LINE__ << ": " << error_msg << std::endl; \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef std::default_random_engine test_rng;
|
||||
/** @brief reference to the default test rng */
|
||||
test_rng& randgen();
|
||||
/** @brief uniformly random 64-bit uint */
|
||||
uint64_t uniform_u64();
|
||||
/** @brief uniformly random number <= 2^nbits-1 */
|
||||
uint64_t uniform_u64_bits(uint64_t nbits);
|
||||
/** @brief uniformly random signed 64-bit number */
|
||||
int64_t uniform_i64();
|
||||
/** @brief uniformly random signed |number| <= 2^nbits */
|
||||
int64_t uniform_i64_bits(uint64_t nbits);
|
||||
/** @brief uniformly random signed lb <= number <= ub */
|
||||
int64_t uniform_i64_bounds(const int64_t lb, const int64_t ub);
|
||||
/** @brief uniformly random signed lb <= number <= ub */
|
||||
__int128_t uniform_i128_bounds(const __int128_t lb, const __int128_t ub);
|
||||
/** @brief uniformly random gaussian float64 */
|
||||
double random_f64_gaussian(double stdev = 1);
|
||||
/** @brief uniformly random signed lb <= number <= ub */
|
||||
double uniform_f64_bounds(const double lb, const double ub);
|
||||
/** @brief uniformly random float64 in [0,1] */
|
||||
double uniform_f64_01();
|
||||
/** @brief random gaussian float64 */
|
||||
double random_f64_gaussian(double stdev);
|
||||
|
||||
bool is_pow2(uint64_t n);
|
||||
|
||||
void* alloc64(uint64_t size);
|
||||
|
||||
typedef __uint128_t thash;
|
||||
/** @brief returns some pseudorandom hash of a contiguous content */
|
||||
thash test_hash(const void* data, uint64_t size);
|
||||
/** @brief class to return a pseudorandom hash of a piecewise-defined content */
|
||||
class test_hasher {
|
||||
void* md;
|
||||
public:
|
||||
test_hasher();
|
||||
test_hasher(const test_hasher&) = delete;
|
||||
void operator=(const test_hasher&) = delete;
|
||||
/**
|
||||
* @brief append input bytes.
|
||||
* The final hash only depends on the concatenation of bytes, not on the
|
||||
* way the content was split into multiple calls to update.
|
||||
*/
|
||||
void update(const void* data, uint64_t size);
|
||||
/**
|
||||
* @brief returns the final hash.
|
||||
* no more calls to update(...) shall be issued after this call.
|
||||
*/
|
||||
thash hash();
|
||||
~test_hasher();
|
||||
};
|
||||
|
||||
// not included by default, since it makes some versions of gtest not compile
|
||||
// std::ostream& operator<<(std::ostream& out, __int128_t x);
|
||||
// std::ostream& operator<<(std::ostream& out, __uint128_t x);
|
||||
|
||||
#endif // SPQLIOS_TEST_COMMONS_H
|
||||
24
spqlios/lib/test/testlib/test_hash.cpp
Normal file
24
spqlios/lib/test/testlib/test_hash.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "sha3.h"
|
||||
#include "test_commons.h"
|
||||
|
||||
/** @brief returns some pseudorandom hash of the content */
|
||||
thash test_hash(const void* data, uint64_t size) {
|
||||
thash res;
|
||||
sha3(data, size, &res, sizeof(res));
|
||||
return res;
|
||||
}
|
||||
/** @brief class to return a pseudorandom hash of the content */
|
||||
test_hasher::test_hasher() {
|
||||
md = malloc(sizeof(sha3_ctx_t));
|
||||
sha3_init((sha3_ctx_t*)md, 16);
|
||||
}
|
||||
|
||||
void test_hasher::update(const void* data, uint64_t size) { sha3_update((sha3_ctx_t*)md, data, size); }
|
||||
|
||||
thash test_hasher::hash() {
|
||||
thash res;
|
||||
sha3_final(&res, (sha3_ctx_t*)md);
|
||||
return res;
|
||||
}
|
||||
|
||||
test_hasher::~test_hasher() { free(md); }
|
||||
182
spqlios/lib/test/testlib/vec_rnx_layout.cpp
Normal file
182
spqlios/lib/test/testlib/vec_rnx_layout.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "vec_rnx_layout.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "../../spqlios/arithmetic/vec_rnx_arithmetic.h"
|
||||
|
||||
#ifdef VALGRIND_MEM_TESTS
|
||||
#include "valgrind/memcheck.h"
|
||||
#endif
|
||||
|
||||
#define CANARY_PADDING (1024)
|
||||
#define GARBAGE_VALUE (242)
|
||||
|
||||
rnx_vec_f64_layout::rnx_vec_f64_layout(uint64_t n, uint64_t size, uint64_t slice) : n(n), size(size), slice(slice) {
|
||||
REQUIRE_DRAMATICALLY(is_pow2(n), "not a power of 2" << n);
|
||||
REQUIRE_DRAMATICALLY(slice >= n, "slice too small" << slice << " < " << n);
|
||||
this->region = (uint8_t*)malloc(size * slice * sizeof(int64_t) + 2 * CANARY_PADDING);
|
||||
this->data_start = (double*)(region + CANARY_PADDING);
|
||||
// ensure that any invalid value is kind-of garbage
|
||||
memset(region, GARBAGE_VALUE, size * slice * sizeof(int64_t) + 2 * CANARY_PADDING);
|
||||
// mark inter-slice memory as not accessible
|
||||
#ifdef VALGRIND_MEM_TESTS
|
||||
VALGRIND_MAKE_MEM_NOACCESS(region, CANARY_PADDING);
|
||||
VALGRIND_MAKE_MEM_NOACCESS(region + size * slice * sizeof(int64_t) + CANARY_PADDING, CANARY_PADDING);
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(data_start + i * slice, n * sizeof(int64_t));
|
||||
}
|
||||
if (size != slice) {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
VALGRIND_MAKE_MEM_NOACCESS(data_start + i * slice + n, (slice - n) * sizeof(int64_t));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
rnx_vec_f64_layout::~rnx_vec_f64_layout() { free(region); }
|
||||
|
||||
rnx_f64 rnx_vec_f64_layout::get_copy_zext(uint64_t index) const {
|
||||
if (index < size) {
|
||||
return rnx_f64(n, data_start + index * slice);
|
||||
} else {
|
||||
return rnx_f64::zero(n);
|
||||
}
|
||||
}
|
||||
|
||||
rnx_f64 rnx_vec_f64_layout::get_copy(uint64_t index) const {
|
||||
REQUIRE_DRAMATICALLY(index < size, "index overflow: " << index << " / " << size);
|
||||
return rnx_f64(n, data_start + index * slice);
|
||||
}
|
||||
|
||||
reim_fft64vec rnx_vec_f64_layout::get_dft_copy_zext(uint64_t index) const {
|
||||
if (index < size) {
|
||||
return reim_fft64vec(n, data_start + index * slice);
|
||||
} else {
|
||||
return reim_fft64vec::zero(n);
|
||||
}
|
||||
}
|
||||
|
||||
reim_fft64vec rnx_vec_f64_layout::get_dft_copy(uint64_t index) const {
|
||||
REQUIRE_DRAMATICALLY(index < size, "index overflow: " << index << " / " << size);
|
||||
return reim_fft64vec(n, data_start + index * slice);
|
||||
}
|
||||
|
||||
void rnx_vec_f64_layout::set(uint64_t index, const rnx_f64& elem) {
|
||||
REQUIRE_DRAMATICALLY(index < size, "index overflow: " << index << " / " << size);
|
||||
REQUIRE_DRAMATICALLY(elem.nn() == n, "incompatible ring dimensions: " << elem.nn() << " / " << n);
|
||||
elem.save_as(data_start + index * slice);
|
||||
}
|
||||
|
||||
double* rnx_vec_f64_layout::data() { return data_start; }
|
||||
const double* rnx_vec_f64_layout::data() const { return data_start; }
|
||||
|
||||
void rnx_vec_f64_layout::fill_random(double log2bound) {
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
set(i, rnx_f64::random_log2bound(n, log2bound));
|
||||
}
|
||||
}
|
||||
|
||||
thash rnx_vec_f64_layout::content_hash() const {
|
||||
test_hasher hasher;
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
hasher.update(data() + i * slice, n * sizeof(int64_t));
|
||||
}
|
||||
return hasher.hash();
|
||||
}
|
||||
|
||||
fft64_rnx_vmp_pmat_layout::fft64_rnx_vmp_pmat_layout(uint64_t n, uint64_t nrows, uint64_t ncols)
|
||||
: nn(n),
|
||||
nrows(nrows),
|
||||
ncols(ncols), //
|
||||
data((RNX_VMP_PMAT*)alloc64(nrows * ncols * nn * 8)) {}
|
||||
|
||||
double* fft64_rnx_vmp_pmat_layout::get_addr(uint64_t row, uint64_t col, uint64_t blk) const {
|
||||
REQUIRE_DRAMATICALLY(row < nrows, "row overflow: " << row << " / " << nrows);
|
||||
REQUIRE_DRAMATICALLY(col < ncols, "col overflow: " << col << " / " << ncols);
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "block overflow: " << blk << " / " << (nn / 8));
|
||||
double* d = (double*)data;
|
||||
if (col == (ncols - 1) && (ncols % 2 == 1)) {
|
||||
// special case: last column out of an odd column number
|
||||
return d + blk * nrows * ncols * 8 // major: blk
|
||||
+ col * nrows * 8 // col == ncols-1
|
||||
+ row * 8;
|
||||
} else {
|
||||
// general case: columns go by pair
|
||||
return d + blk * nrows * ncols * 8 // major: blk
|
||||
+ (col / 2) * (2 * nrows) * 8 // second: col pair index
|
||||
+ row * 2 * 8 // third: row index
|
||||
+ (col % 2) * 8; // minor: col in colpair
|
||||
}
|
||||
}
|
||||
|
||||
reim4_elem fft64_rnx_vmp_pmat_layout::get(uint64_t row, uint64_t col, uint64_t blk) const {
|
||||
return reim4_elem(get_addr(row, col, blk));
|
||||
}
|
||||
reim4_elem fft64_rnx_vmp_pmat_layout::get_zext(uint64_t row, uint64_t col, uint64_t blk) const {
|
||||
REQUIRE_DRAMATICALLY(blk < nn / 8, "block overflow: " << blk << " / " << (nn / 8));
|
||||
if (row < nrows && col < ncols) {
|
||||
return reim4_elem(get_addr(row, col, blk));
|
||||
} else {
|
||||
return reim4_elem::zero();
|
||||
}
|
||||
}
|
||||
void fft64_rnx_vmp_pmat_layout::set(uint64_t row, uint64_t col, uint64_t blk, const reim4_elem& value) const {
|
||||
value.save_as(get_addr(row, col, blk));
|
||||
}
|
||||
|
||||
fft64_rnx_vmp_pmat_layout::~fft64_rnx_vmp_pmat_layout() { spqlios_free(data); }
|
||||
|
||||
reim_fft64vec fft64_rnx_vmp_pmat_layout::get_zext(uint64_t row, uint64_t col) const {
|
||||
if (row >= nrows || col >= ncols) {
|
||||
return reim_fft64vec::zero(nn);
|
||||
}
|
||||
if (nn < 8) {
|
||||
// the pmat is just col major
|
||||
double* addr = (double*)data + (row + col * nrows) * nn;
|
||||
return reim_fft64vec(nn, addr);
|
||||
}
|
||||
// otherwise, reconstruct it block by block
|
||||
reim_fft64vec res(nn);
|
||||
for (uint64_t blk = 0; blk < nn / 8; ++blk) {
|
||||
reim4_elem v = get(row, col, blk);
|
||||
res.set_blk(blk, v);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
void fft64_rnx_vmp_pmat_layout::set(uint64_t row, uint64_t col, const reim_fft64vec& value) {
|
||||
REQUIRE_DRAMATICALLY(row < nrows, "row overflow: " << row << " / " << nrows);
|
||||
REQUIRE_DRAMATICALLY(col < ncols, "row overflow: " << col << " / " << ncols);
|
||||
if (nn < 8) {
|
||||
// the pmat is just col major
|
||||
double* addr = (double*)data + (row + col * nrows) * nn;
|
||||
value.save_as(addr);
|
||||
return;
|
||||
}
|
||||
// otherwise, reconstruct it block by block
|
||||
for (uint64_t blk = 0; blk < nn / 8; ++blk) {
|
||||
reim4_elem v = value.get_blk(blk);
|
||||
set(row, col, blk, v);
|
||||
}
|
||||
}
|
||||
void fft64_rnx_vmp_pmat_layout::fill_random(double log2bound) {
|
||||
for (uint64_t row = 0; row < nrows; ++row) {
|
||||
for (uint64_t col = 0; col < ncols; ++col) {
|
||||
set(row, col, reim_fft64vec::random(nn, log2bound));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fft64_rnx_svp_ppol_layout::fft64_rnx_svp_ppol_layout(uint64_t n)
|
||||
: nn(n), //
|
||||
data((RNX_SVP_PPOL*)alloc64(nn * 8)) {}
|
||||
|
||||
reim_fft64vec fft64_rnx_svp_ppol_layout::get_copy() const { return reim_fft64vec(nn, (double*)data); }
|
||||
|
||||
void fft64_rnx_svp_ppol_layout::set(const reim_fft64vec& value) { value.save_as((double*)data); }
|
||||
|
||||
void fft64_rnx_svp_ppol_layout::fill_dft_random(uint64_t log2bound) { set(reim_fft64vec::dft_random(nn, log2bound)); }
|
||||
|
||||
void fft64_rnx_svp_ppol_layout::fill_random(double log2bound) { set(reim_fft64vec::random(nn, log2bound)); }
|
||||
|
||||
fft64_rnx_svp_ppol_layout::~fft64_rnx_svp_ppol_layout() { spqlios_free(data); }
|
||||
thash fft64_rnx_svp_ppol_layout::content_hash() const { return test_hash(data, nn * sizeof(double)); }
|
||||
85
spqlios/lib/test/testlib/vec_rnx_layout.h
Normal file
85
spqlios/lib/test/testlib/vec_rnx_layout.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef SPQLIOS_EXT_VEC_RNX_LAYOUT_H
|
||||
#define SPQLIOS_EXT_VEC_RNX_LAYOUT_H
|
||||
|
||||
#include "../../spqlios/arithmetic/vec_rnx_arithmetic.h"
|
||||
#include "fft64_dft.h"
|
||||
#include "negacyclic_polynomial.h"
|
||||
#include "reim4_elem.h"
|
||||
#include "test_commons.h"
|
||||
|
||||
/** @brief a test memory layout for rnx i64 polynomials vectors */
|
||||
class rnx_vec_f64_layout {
|
||||
uint64_t n;
|
||||
uint64_t size;
|
||||
uint64_t slice;
|
||||
double* data_start;
|
||||
uint8_t* region;
|
||||
|
||||
public:
|
||||
// NO-COPY structure
|
||||
rnx_vec_f64_layout(const rnx_vec_f64_layout&) = delete;
|
||||
void operator=(const rnx_vec_f64_layout&) = delete;
|
||||
rnx_vec_f64_layout(rnx_vec_f64_layout&&) = delete;
|
||||
void operator=(rnx_vec_f64_layout&&) = delete;
|
||||
/** @brief initialises a memory layout */
|
||||
rnx_vec_f64_layout(uint64_t n, uint64_t size, uint64_t slice);
|
||||
/** @brief destructor */
|
||||
~rnx_vec_f64_layout();
|
||||
|
||||
/** @brief get a copy of item index index (extended with zeros) */
|
||||
rnx_f64 get_copy_zext(uint64_t index) const;
|
||||
/** @brief get a copy of item index index (extended with zeros) */
|
||||
rnx_f64 get_copy(uint64_t index) const;
|
||||
/** @brief get a copy of item index index (extended with zeros) */
|
||||
reim_fft64vec get_dft_copy_zext(uint64_t index) const;
|
||||
/** @brief get a copy of item index index (extended with zeros) */
|
||||
reim_fft64vec get_dft_copy(uint64_t index) const;
|
||||
|
||||
/** @brief get a copy of item index index (index<size) */
|
||||
void set(uint64_t index, const rnx_f64& elem);
|
||||
/** @brief fill with random values */
|
||||
void fill_random(double log2bound = 0);
|
||||
/** @brief raw pointer access */
|
||||
double* data();
|
||||
/** @brief raw pointer access (const version) */
|
||||
const double* data() const;
|
||||
/** @brief content hashcode */
|
||||
thash content_hash() const;
|
||||
};
|
||||
|
||||
/** @brief test layout for the VMP_PMAT */
|
||||
class fft64_rnx_vmp_pmat_layout {
|
||||
public:
|
||||
const uint64_t nn;
|
||||
const uint64_t nrows;
|
||||
const uint64_t ncols;
|
||||
RNX_VMP_PMAT* const data;
|
||||
fft64_rnx_vmp_pmat_layout(uint64_t n, uint64_t nrows, uint64_t ncols);
|
||||
double* get_addr(uint64_t row, uint64_t col, uint64_t blk) const;
|
||||
reim4_elem get(uint64_t row, uint64_t col, uint64_t blk) const;
|
||||
thash content_hash() const;
|
||||
reim4_elem get_zext(uint64_t row, uint64_t col, uint64_t blk) const;
|
||||
reim_fft64vec get_zext(uint64_t row, uint64_t col) const;
|
||||
void set(uint64_t row, uint64_t col, uint64_t blk, const reim4_elem& v) const;
|
||||
void set(uint64_t row, uint64_t col, const reim_fft64vec& value);
|
||||
/** @brief fill with random double values (unstructured) */
|
||||
void fill_random(double log2bound);
|
||||
~fft64_rnx_vmp_pmat_layout();
|
||||
};
|
||||
|
||||
/** @brief test layout for the SVP_PPOL */
|
||||
class fft64_rnx_svp_ppol_layout {
|
||||
public:
|
||||
const uint64_t nn;
|
||||
RNX_SVP_PPOL* const data;
|
||||
fft64_rnx_svp_ppol_layout(uint64_t n);
|
||||
thash content_hash() const;
|
||||
reim_fft64vec get_copy() const;
|
||||
void set(const reim_fft64vec&);
|
||||
/** @brief fill with random double values (unstructured) */
|
||||
void fill_random(double log2bound);
|
||||
/** @brief fill with random ffts of small int polynomials */
|
||||
void fill_dft_random(uint64_t log2bound);
|
||||
~fft64_rnx_svp_ppol_layout();
|
||||
};
|
||||
#endif // SPQLIOS_EXT_VEC_RNX_LAYOUT_H
|
||||
55
spqlios/lib/test/testlib/zn_layouts.cpp
Normal file
55
spqlios/lib/test/testlib/zn_layouts.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "zn_layouts.h"
|
||||
|
||||
zn32_pmat_layout::zn32_pmat_layout(uint64_t nrows, uint64_t ncols)
|
||||
: nrows(nrows), //
|
||||
ncols(ncols), //
|
||||
data((ZN32_VMP_PMAT*)malloc((nrows * ncols + 7) * sizeof(int32_t))) {}
|
||||
|
||||
zn32_pmat_layout::~zn32_pmat_layout() { free(data); }
|
||||
|
||||
int32_t* zn32_pmat_layout::get_addr(uint64_t row, uint64_t col) const {
|
||||
REQUIRE_DRAMATICALLY(row < nrows, "row overflow" << row << " / " << nrows);
|
||||
REQUIRE_DRAMATICALLY(col < ncols, "col overflow" << col << " / " << ncols);
|
||||
const uint64_t nblk = ncols >> 5;
|
||||
const uint64_t rem_ncols = ncols & 31;
|
||||
uint64_t blk = col >> 5;
|
||||
uint64_t col_rem = col & 31;
|
||||
if (blk < nblk) {
|
||||
// column is part of a full block
|
||||
return (int32_t*)data + blk * nrows * 32 + row * 32 + col_rem;
|
||||
} else {
|
||||
// column is part of the last block
|
||||
return (int32_t*)data + blk * nrows * 32 + row * rem_ncols + col_rem;
|
||||
}
|
||||
}
|
||||
int32_t zn32_pmat_layout::get(uint64_t row, uint64_t col) const { return *get_addr(row, col); }
|
||||
int32_t zn32_pmat_layout::get_zext(uint64_t row, uint64_t col) const {
|
||||
if (row >= nrows || col >= ncols) return 0;
|
||||
return *get_addr(row, col);
|
||||
}
|
||||
void zn32_pmat_layout::set(uint64_t row, uint64_t col, int32_t value) { *get_addr(row, col) = value; }
|
||||
void zn32_pmat_layout::fill_random() {
|
||||
int32_t* d = (int32_t*)data;
|
||||
for (uint64_t i = 0; i < nrows * ncols; ++i) d[i] = uniform_i64_bits(32);
|
||||
}
|
||||
thash zn32_pmat_layout::content_hash() const { return test_hash(data, nrows * ncols * sizeof(int32_t)); }
|
||||
|
||||
template <typename T>
|
||||
std::vector<int32_t> vmp_product(const T* vec, uint64_t vec_size, uint64_t out_size, const zn32_pmat_layout& mat) {
|
||||
uint64_t rows = std::min(vec_size, mat.nrows);
|
||||
uint64_t cols = std::min(out_size, mat.ncols);
|
||||
std::vector<int32_t> res(out_size, 0);
|
||||
for (uint64_t j = 0; j < cols; ++j) {
|
||||
for (uint64_t i = 0; i < rows; ++i) {
|
||||
res[j] += vec[i] * mat.get(i, j);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template std::vector<int32_t> vmp_product(const int8_t* vec, uint64_t vec_size, uint64_t out_size,
|
||||
const zn32_pmat_layout& mat);
|
||||
template std::vector<int32_t> vmp_product(const int16_t* vec, uint64_t vec_size, uint64_t out_size,
|
||||
const zn32_pmat_layout& mat);
|
||||
template std::vector<int32_t> vmp_product(const int32_t* vec, uint64_t vec_size, uint64_t out_size,
|
||||
const zn32_pmat_layout& mat);
|
||||
29
spqlios/lib/test/testlib/zn_layouts.h
Normal file
29
spqlios/lib/test/testlib/zn_layouts.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef SPQLIOS_EXT_ZN_LAYOUTS_H
|
||||
#define SPQLIOS_EXT_ZN_LAYOUTS_H
|
||||
|
||||
#include "../../spqlios/arithmetic/zn_arithmetic.h"
|
||||
#include "test_commons.h"
|
||||
|
||||
class zn32_pmat_layout {
|
||||
public:
|
||||
const uint64_t nrows;
|
||||
const uint64_t ncols;
|
||||
ZN32_VMP_PMAT* const data;
|
||||
zn32_pmat_layout(uint64_t nrows, uint64_t ncols);
|
||||
|
||||
private:
|
||||
int32_t* get_addr(uint64_t row, uint64_t col) const;
|
||||
|
||||
public:
|
||||
int32_t get(uint64_t row, uint64_t col) const;
|
||||
int32_t get_zext(uint64_t row, uint64_t col) const;
|
||||
void set(uint64_t row, uint64_t col, int32_t value);
|
||||
void fill_random();
|
||||
thash content_hash() const;
|
||||
~zn32_pmat_layout();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::vector<int32_t> vmp_product(const T* vec, uint64_t vec_size, uint64_t out_size, const zn32_pmat_layout& mat);
|
||||
|
||||
#endif // SPQLIOS_EXT_ZN_LAYOUTS_H
|
||||
Reference in New Issue
Block a user