From f2cf1a739f92c368738ccc48528aeeca2df4d453 Mon Sep 17 00:00:00 2001 From: furze Date: Mon, 12 Jan 2026 20:21:44 +0200 Subject: [PATCH 01/13] nogmp: remove GMP --- c/alt_bn128_test.cpp | 478 ------------------------------------------- c/fft.cpp | 194 +++++++++--------- c/wtns_utils.cpp | 22 +- c/wtns_utils.hpp | 10 +- c/zkey_utils.cpp | 37 ++-- c/zkey_utils.hpp | 8 +- tasksfile.js | 22 -- 7 files changed, 150 insertions(+), 621 deletions(-) delete mode 100644 c/alt_bn128_test.cpp diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp deleted file mode 100644 index ddcc5ad..0000000 --- a/c/alt_bn128_test.cpp +++ /dev/null @@ -1,478 +0,0 @@ -#include -#include - -#include "gtest/gtest.h" -#include "alt_bn128.hpp" -#include "fft.hpp" - -using namespace AltBn128; - -namespace { - -TEST(altBn128, f2_simpleMul) { - - F2Element e1; - F2.fromString(e1, "(2,2)"); - - F2Element e2; - F2.fromString(e2, "(3,3)"); - - F2Element e3; - F2.mul(e3, e1, e2); - - F2Element e33; - F2.fromString(e33, "(0,12)"); - - // std::cout << F2.toString(e3) << std::endl; - - ASSERT_TRUE(F2.eq(e3, e33)); -} - -TEST(altBn128, f6_mulDiv) { - F6Element a; - F6.fromString(a, "((1,2),(3,4),(5,6))"); - - F6Element b; - F6.fromString(b, "((12,11),(10,9),(8,7))"); - - F6Element c,d; - - F6.mul(c,a,b); - F6.div(d,c,b); - - ASSERT_TRUE(F6.eq(a,d)); -} - -TEST(altBn128, f6_inv) { - F6Element a; - F6.fromString(a, "((239487238491,2356249827341)," - "(082659782,182703523765)," - "(978236549263,64893242))"); - - F6Element inv; - F6.inv(inv, a); - - F6Element b; - F6.mul(b, inv, a); - - ASSERT_TRUE(F6.isOne(b)); -} - -TEST(altBn128, f12_mulDiv) { - F12Element a; - F12.fromString(a, "(((1,2),(3,4),(5,6)),((7,8),(9,10),(11,12)))"); - - F12Element b; - F12.fromString(b, "(((12,11),(10,9),(8,7)),((6,5),(4,3),(2,1)))"); - - F12Element c,d; - - F12.mul(c,a,b); - F12.div(d,c,b); - - ASSERT_TRUE(F12.eq(a,d)); -} - -TEST(altBn128, f12_inv) { - F12Element a; - F12.fromString(a, - "(((239846234862342323958623,2359862352529835623)," - "(928836523,9856234)," - "(235635286,5628392833))," - "((252936598265329856238956532167968,23596239865236954178968)," - "(95421692834,236548)," - "(924523,12954623)))" ); - - F12Element inv; - F12.inv(inv, a); - - F12Element b; - F12.mul(b, inv, a); - - ASSERT_TRUE(F12.isOne(b)); -} - -TEST(altBn128, g1_PlusZero) { - G1Point p1; - - G1.add(p1, G1.one(), G1.zero()); - - ASSERT_TRUE(G1.eq(p1, G1.one())); -} - -TEST(altBn128, g1_minus_g1) { - G1Point p1; - - G1.sub(p1, G1.one(), G1.one()); - - ASSERT_TRUE(G1.isZero(p1)); -} - -TEST(altBn128, g1_times_4) { - G1Point p1; - G1.add(p1, G1.one(), G1.one()); - G1.add(p1, p1, G1.one()); - G1.add(p1, p1, G1.one()); - - G1Point p2; - G1.dbl(p2, G1.one()); - G1.dbl(p2, p2); - - ASSERT_TRUE(G1.eq(p1,p2)); -} - - -TEST(altBn128, g1_times_3) { - G1Point p1; - G1.add(p1, G1.one(), G1.one()); - G1.add(p1, p1, G1.one()); - - G1Point p2; - G1.dbl(p2, G1.one()); - G1.dbl(p2, p2); - G1.sub(p2, p2, G1.one()); - - ASSERT_TRUE(G1.eq(p1,p2)); -} - -TEST(altBn128, g1_times_3_exp) { - G1Point p1; - G1.add(p1, G1.one(), G1.one()); - G1.add(p1, p1, G1.one()); - - mpz_t e; - mpz_init_set_str(e, "3", 10); - - uint8_t scalar[32]; - for (int i=0;i<32;i++) scalar[i] = 0; - mpz_export((void *)scalar, NULL, -1, 8, -1, 0, e); - mpz_clear(e); - - G1Point p2; - G1.mulByScalar(p2, G1.one(), scalar, 32); - - ASSERT_TRUE(G1.eq(p1,p2)); -} - -TEST(altBn128, g1_times_5) { - G1Point p1; - G1.dbl(p1, G1.one()); - G1.dbl(p1, p1); - G1.add(p1, p1, p1); - - G1Point p2; - G1Point p3; - G1Point p4; - G1Point p5; - G1Point p6; - G1.dbl(p2, G1.one()); - G1.dbl(p3, p2); - G1.dbl(p4, G1.one()); - G1.dbl(p5, p4); - G1.add(p6, p3, p5); - - ASSERT_TRUE(G1.eq(p1,p6)); -} - -TEST(altBn128, g1_times_65_exp) { - - G1Point p1; - G1.dbl(p1, G1.one()); - G1.dbl(p1, p1); - G1.dbl(p1, p1); - G1.dbl(p1, p1); - G1.dbl(p1, p1); - G1.dbl(p1, p1); - G1.add(p1, p1, G1.one()); - - mpz_t e; - mpz_init_set_str(e, "65", 10); - - uint8_t scalar[32]; - for (int i=0;i<32;i++) scalar[i] = 0; - mpz_export((void *)scalar, NULL, -1, 8, -1, 0, e); - mpz_clear(e); - - G1Point p2; - G1.mulByScalar(p2, G1.one(), scalar, 32); - - ASSERT_TRUE(G1.eq(p1,p2)); -} - -TEST(altBn128, g1_expToOrder) { - mpz_t e; - mpz_init_set_str(e, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10); - - uint8_t scalar[32]; - - for (int i=0;i<32;i++) scalar[i] = 0; - mpz_export((void *)scalar, NULL, -1, 8, -1, 0, e); - mpz_clear(e); - - G1Point p1; - - G1.mulByScalar(p1, G1.one(), scalar, 32); - - ASSERT_TRUE(G1.isZero(p1)); -} - -TEST(altBn128, g2_expToOrder) { - mpz_t e; - mpz_init_set_str(e, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10); - - uint8_t scalar[32]; - - for (int i=0;i<32;i++) scalar[i] = 0; - mpz_export((void *)scalar, NULL, -1, 8, -1, 0, e); - mpz_clear(e); - - Curve>::Point p1; - - G2.mulByScalar(p1, G2.one(), scalar, 32); - - ASSERT_TRUE(G2.isZero(p1)); -} - -TEST(altBn128, multiExp) { - - int NMExp = 40000; - - typedef uint8_t Scalar[32]; - - Scalar *scalars = new Scalar[NMExp]; - G1PointAffine *bases = new G1PointAffine[NMExp]; - - uint64_t acc=0; - for (int i=0; i fft(NMExp); - - fft.fft(a, NMExp); - fft.ifft(a, NMExp); - - AltBn128::FrElement aux; - for (int i=0; i #include +#include +#include +#include + #include "misc.hpp" using namespace std; -// The function we want to execute on the new thread. - template u_int32_t FFT::log2(u_int64_t n) { assert(n!=0); @@ -28,6 +30,36 @@ static inline u_int64_t BR(u_int64_t x, u_int64_t domainPow) #define ROOT(s,j) (rootsOfUnit[(1<<(s))+(j)]) +template +static inline void shr_words(uint64_t (&out)[N64], const uint64_t (&in)[N64], u_int32_t shift) { + if (shift == 0) { + for (size_t i = 0; i < N64; i++) out[i] = in[i]; + return; + } + const u_int32_t wordShift = shift / 64; + const u_int32_t bitShift = shift % 64; + + for (size_t i = 0; i < N64; i++) out[i] = 0; + + for (size_t i = wordShift; i < N64; i++) { + uint64_t low = in[i]; + uint64_t hi = (i + 1 < N64) ? in[i + 1] : 0; + + if (bitShift == 0) { + out[i - wordShift] = low; + } else { + out[i - wordShift] = (low >> bitShift) | (hi << (64 - bitShift)); + } + } +} + +template +static inline void shr_words_inplace(uint64_t (&a)[N64], u_int32_t shift) { + uint64_t tmp[N64]; + shr_words(tmp, a, shift); + for (size_t i = 0; i < N64; i++) a[i] = tmp[i]; +} + template FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) : threadPool(ThreadPool::defaultPool()) @@ -36,82 +68,95 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) u_int32_t domainPow = log2(maxDomainSize); - mpz_t m_qm1d2; - mpz_t m_q; - mpz_t m_nqr; - mpz_t m_aux; - mpz_init(m_qm1d2); - mpz_init(m_q); - mpz_init(m_nqr); - mpz_init(m_aux); - - f.toMpz(m_aux, f.negOne()); - - mpz_add_ui(m_q, m_aux, 1); - mpz_fdiv_q_2exp(m_qm1d2, m_aux, 1); - - mpz_set_ui(m_nqr, 2); - mpz_powm(m_aux, m_nqr, m_qm1d2, m_q); - while (mpz_cmp_ui(m_aux, 1) == 0) { - mpz_add_ui(m_nqr, m_nqr, 1); - mpz_powm(m_aux, m_nqr, m_qm1d2, m_q); +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) + static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, + "This FFT exp-cast expects little-endian machine layout."); +#endif + + // qm1_norm = q-1 in NORMAL (non-montgomery) representation + Element qm1_norm; + f.fromMontgomery(qm1_norm, f.negOne()); + + uint64_t qm1_words[Field::N64]; + std::memcpy(qm1_words, (const void*)qm1_norm.v, sizeof(qm1_words)); + + // qm1d2 = (q-1)/2 + uint64_t qm1d2_words[Field::N64]; + shr_words(qm1d2_words, qm1_words, 1); + + // find nqr: cand^((q-1)/2) != 1 + Element cand, res; + uint64_t cand_ui = 2; + for (;;) { + f.fromUI(cand, cand_ui); + f.exp(res, cand, + reinterpret_cast(qm1d2_words), + (unsigned)sizeof(qm1d2_words)); + if (!f.eq(res, f.one())) { + f.copy(nqr, cand); + break; + } + cand_ui++; } - f.fromMpz(nqr, m_nqr); - - // std::cout << "nqr: " << f.toString(nqr) << std::endl; + // aux_words starts from (q-1)/2 and we divide by 2 until we reach domainPow + // so at the end: aux_words = (q-1)/2^domainPow + uint64_t aux_words[Field::N64]; + for (size_t i = 0; i < (size_t)Field::N64; i++) aux_words[i] = qm1d2_words[i]; - s = 1; - mpz_set(m_aux, m_qm1d2); - while ((!mpz_tstbit(m_aux, 0))&&(s stop + shr_words_inplace(aux_words, 1); + s_tmp++; } - if (s1) { - mpz_powm(m_aux, m_nqr, m_aux, m_q); - f.fromMpz(roots[1], m_aux); - mpz_set_ui(m_aux, 2); - mpz_invert(m_aux, m_aux, m_q); - f.fromMpz(powTwoInv[1], m_aux); + if (nRoots > 1) { + // primitive 2^s root of unity: roots[1] = nqr^(aux_words) + f.exp(roots[1], nqr, + reinterpret_cast(aux_words), + (unsigned)sizeof(aux_words)); + + // powTwoInv[1] = 1/2 + Element two; + f.fromUI(two, 2); + f.inv(powTwoInv[1], two); } threadPool.parallelBlock([&] (uint64_t nThreads, uint64_t idThread) { - uint64_t increment = nRoots / nThreads; uint64_t start = idThread==0 ? 2 : idThread * increment; uint64_t end = idThread==nThreads-1 ? nRoots : (idThread+1) * increment; - if (end>start) { + + if (end > start) { + // roots[start] = roots[1]^start f.exp(roots[start], roots[1], (uint8_t *)(&start), sizeof(start)); } - for (uint64_t i=start+1; i @@ -120,41 +165,6 @@ FFT::~FFT() { delete[] powTwoInv; } -/* -template -void FFT::reversePermutationInnerLoop(Element *a, u_int64_t from, u_int64_t to, u_int32_t domainPow) { - Element tmp; - for (u_int64_t i=from; ir) { - f.copy(tmp, a[i]); - f.copy(a[i], a[r]); - f.copy(a[r], tmp); - } - } -} - - -template -void FFT::reversePermutation(Element *a, u_int64_t n) { - int domainPow = log2(n); - std::vector threads(nThreads-1); - u_int64_t increment = n / nThreads; - if (increment) { - for (u_int64_t i=0; i::reversePermutationInnerLoop, this, a, i*increment, (i+1)*increment, domainPow); - } - } - reversePermutationInnerLoop(a, (nThreads-1)*increment, n, domainPow); - if (increment) { - for (u_int32_t i=0; i void FFT::reversePermutation(Element *a, u_int64_t n) { int domainPow = log2(n); @@ -172,25 +182,25 @@ void FFT::reversePermutation(Element *a, u_int64_t n) { }); } - template void FFT::fft(Element *a, u_int64_t n) { reversePermutation(a, n); - u_int64_t domainPow =log2(n); + u_int64_t domainPow = log2(n); assert(((u_int64_t)1 << domainPow) == n); + for (u_int32_t s=1; s<=domainPow; s++) { u_int64_t m = 1 << s; u_int64_t mdiv2 = m >> 1; threadPool.parallelFor(0, (n>>1), [&] (int begin, int end, int numThread) { - for (u_int64_t i=begin; i< end; i++) { + for (u_int64_t i=begin; i< (u_int64_t)end; i++) { Element t; Element u; u_int64_t k=(i/mdiv2)*m; u_int64_t j=i%mdiv2; f.mul(t, root(s, j), a[k+j+mdiv2]); - f.copy(u,a[k+j]); + f.copy(u, a[k+j]); f.add(a[k+j], t, u); f.sub(a[k+j+mdiv2], u, t); } @@ -201,11 +211,11 @@ void FFT::fft(Element *a, u_int64_t n) { template void FFT::ifft(Element *a, u_int64_t n ) { fft(a, n); - u_int64_t domainPow =log2(n); - u_int64_t nDiv2= n >> 1; + u_int64_t domainPow = log2(n); + u_int64_t nDiv2= n >> 1; threadPool.parallelFor(1, nDiv2, [&] (int begin, int end, int numThread) { - for (u_int64_t i=begin; i::ifft(Element *a, u_int64_t n ) { f.mul(a[r], tmp, powTwoInv[domainPow]); } }); + f.mul(a[0], a[0], powTwoInv[domainPow]); f.mul(a[n >> 1], a[n >> 1], powTwoInv[domainPow]); } - - template void FFT::printVector(Element *a, u_int64_t n ) { cout << "[" << endl; @@ -227,4 +236,3 @@ void FFT::printVector(Element *a, u_int64_t n ) { } cout << "]" << endl; } - diff --git a/c/wtns_utils.cpp b/c/wtns_utils.cpp index 2301c3a..a70bf07 100644 --- a/c/wtns_utils.cpp +++ b/c/wtns_utils.cpp @@ -2,26 +2,30 @@ namespace WtnsUtils { -Header::Header() { +Header::Header() + : n8(0), + nVars(0) +{ } -Header::~Header() { - mpz_clear(prime); -} +Header::~Header() = default; std::unique_ptr
loadHeader(BinFileUtils::BinFile *f) { - Header *h = new Header(); + std::unique_ptr
h(new Header()); + f->startReadSection(1); h->n8 = f->readU32LE(); - mpz_init(h->prime); - mpz_import(h->prime, h->n8, -1, 1, -1, 0, f->read(h->n8)); + { + const uint8_t* p = reinterpret_cast(f->read(h->n8)); + h->prime.assign(p, p + h->n8); + } h->nVars = f->readU32LE(); f->endReadSection(); - return std::unique_ptr
(h); + return h; } -} // NAMESPACE \ No newline at end of file +} // namespace WtnsUtils diff --git a/c/wtns_utils.hpp b/c/wtns_utils.hpp index 70aebc2..1766b2e 100644 --- a/c/wtns_utils.hpp +++ b/c/wtns_utils.hpp @@ -1,7 +1,9 @@ #ifndef WTNS_UTILS #define WTNS_UTILS -#include +#include +#include +#include #include "binfile_utils.hpp" @@ -10,7 +12,7 @@ namespace WtnsUtils { class Header { public: u_int32_t n8; - mpz_t prime; + std::vector prime; u_int32_t nVars; @@ -20,6 +22,6 @@ namespace WtnsUtils { std::unique_ptr
loadHeader(BinFileUtils::BinFile *f); -} +} // namespace WtnsUtils -#endif // ZKEY_UTILS_H \ No newline at end of file +#endif // WTNS_UTILS diff --git a/c/zkey_utils.cpp b/c/zkey_utils.cpp index 52a5629..f8ecbbf 100644 --- a/c/zkey_utils.cpp +++ b/c/zkey_utils.cpp @@ -1,17 +1,26 @@ - #include "zkey_utils.hpp" -namespace ZKeyUtils { - +#include -Header::Header() { -} +namespace ZKeyUtils { -Header::~Header() { - mpz_clear(qPrime); - mpz_clear(rPrime); +Header::Header() + : n8q(0), + n8r(0), + nVars(0), + nPublic(0), + domainSize(0), + nCoefs(0), + vk_alpha1(nullptr), + vk_beta1(nullptr), + vk_beta2(nullptr), + vk_gamma2(nullptr), + vk_delta1(nullptr), + vk_delta2(nullptr) +{ } +Header::~Header() = default; std::unique_ptr
loadHeader(BinFileUtils::BinFile *f) { auto h = new Header(); @@ -26,12 +35,16 @@ std::unique_ptr
loadHeader(BinFileUtils::BinFile *f) { f->startReadSection(2); h->n8q = f->readU32LE(); - mpz_init(h->qPrime); - mpz_import(h->qPrime, h->n8q, -1, 1, -1, 0, f->read(h->n8q)); + { + const uint8_t* p = reinterpret_cast(f->read(h->n8q)); + h->qPrime.assign(p, p + h->n8q); + } h->n8r = f->readU32LE(); - mpz_init(h->rPrime); - mpz_import(h->rPrime, h->n8r , -1, 1, -1, 0, f->read(h->n8r)); + { + const uint8_t* p = reinterpret_cast(f->read(h->n8r)); + h->rPrime.assign(p, p + h->n8r); + } h->nVars = f->readU32LE(); h->nPublic = f->readU32LE(); diff --git a/c/zkey_utils.hpp b/c/zkey_utils.hpp index 5bd5e67..f3503d0 100644 --- a/c/zkey_utils.hpp +++ b/c/zkey_utils.hpp @@ -1,8 +1,9 @@ #ifndef ZKEY_UTILS_H #define ZKEY_UTILS_H -#include #include +#include +#include #include "binfile_utils.hpp" @@ -13,9 +14,10 @@ namespace ZKeyUtils { public: u_int32_t n8q; - mpz_t qPrime; + std::vector qPrime; + u_int32_t n8r; - mpz_t rPrime; + std::vector rPrime; u_int32_t nVars; u_int32_t nPublic; diff --git a/tasksfile.js b/tasksfile.js index 1f6883b..bd6031f 100644 --- a/tasksfile.js +++ b/tasksfile.js @@ -46,28 +46,6 @@ function testSplitParStr() { sh("./splitparsestr_test", {cwd: "build", nopipe: true}); } -function testAltBn128() { - sh("g++" + - " -Igoogletest-release-1.10.0/googletest/include"+ - " -I."+ - " -I../c"+ - " ../c/naf.cpp"+ - " ../c/splitparstr.cpp"+ - " ../c/alt_bn128.cpp"+ - " ../c/alt_bn128_test.cpp"+ - " ../c/misc.cpp"+ - " fq.cpp"+ - " fq.o"+ - " fr.cpp"+ - " fr.o"+ - " googletest-release-1.10.0/libgtest.a"+ - " -o altbn128_test" + - " -fmax-errors=5 -pthread -std=c++11 -fopenmp -lgmp -g", {cwd: "build", nopipe: true} - ); - sh("./altbn128_test", {cwd: "build", nopipe: true}); -} - - function benchMultiExpG1() { sh("g++ -O3 -g" + " -Igoogletest-release-1.10.0/googletest/include"+ From d2c2449753ec28a2527460edd98a5ab7ffed2843 Mon Sep 17 00:00:00 2001 From: furze Date: Sun, 18 Jan 2026 23:57:37 +0200 Subject: [PATCH 02/13] nogmp: update fft.cpp, alt_bn128_test.cpp w/ U256 API --- c/alt_bn128_test.cpp | 427 +++++++++++++++++++++++++++++++++++++++++++ c/fft.cpp | 114 ++++-------- 2 files changed, 464 insertions(+), 77 deletions(-) create mode 100644 c/alt_bn128_test.cpp diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp new file mode 100644 index 0000000..fd28aab --- /dev/null +++ b/c/alt_bn128_test.cpp @@ -0,0 +1,427 @@ +#include +#include +#include + +#include "gtest/gtest.h" +#include "alt_bn128.hpp" +#include "fft.hpp" +#include "u256.hpp" + +using namespace AltBn128; + +namespace { + +TEST(altBn128, f2_simpleMul) { + F2Element e1; + F2.fromString(e1, "(2,2)"); + + F2Element e2; + F2.fromString(e2, "(3,3)"); + + F2Element e3; + F2.mul(e3, e1, e2); + + F2Element e33; + F2.fromString(e33, "(0,12)"); + + ASSERT_TRUE(F2.eq(e3, e33)); +} + +TEST(altBn128, f6_mulDiv) { + F6Element a; + F6.fromString(a, "((1,2),(3,4),(5,6))"); + + F6Element b; + F6.fromString(b, "((12,11),(10,9),(8,7))"); + + F6Element c,d; + + F6.mul(c,a,b); + F6.div(d,c,b); + + ASSERT_TRUE(F6.eq(a,d)); +} + +TEST(altBn128, f6_inv) { + F6Element a; + F6.fromString(a, "((239487238491,2356249827341)," + "(082659782,182703523765)," + "(978236549263,64893242))"); + + F6Element inv; + F6.inv(inv, a); + + F6Element b; + F6.mul(b, inv, a); + + ASSERT_TRUE(F6.isOne(b)); +} + +TEST(altBn128, f12_mulDiv) { + F12Element a; + F12.fromString(a, "(((1,2),(3,4),(5,6)),((7,8),(9,10),(11,12)))"); + + F12Element b; + F12.fromString(b, "(((12,11),(10,9),(8,7)),((6,5),(4,3),(2,1)))"); + + F12Element c,d; + + F12.mul(c,a,b); + F12.div(d,c,b); + + ASSERT_TRUE(F12.eq(a,d)); +} + +TEST(altBn128, f12_inv) { + F12Element a; + F12.fromString(a, + "(((239846234862342323958623,2359862352529835623)," + "(928836523,9856234)," + "(235635286,5628392833))," + "((252936598265329856238956532167968,23596239865236954178968)," + "(95421692834,236548)," + "(924523,12954623)))" ); + + F12Element inv; + F12.inv(inv, a); + + F12Element b; + F12.mul(b, inv, a); + + ASSERT_TRUE(F12.isOne(b)); +} + +TEST(altBn128, g1_PlusZero) { + G1Point p1; + G1.add(p1, G1.one(), G1.zero()); + ASSERT_TRUE(G1.eq(p1, G1.one())); +} + +TEST(altBn128, g1_minus_g1) { + G1Point p1; + G1.sub(p1, G1.one(), G1.one()); + ASSERT_TRUE(G1.isZero(p1)); +} + +TEST(altBn128, g1_times_4) { + G1Point p1; + G1.add(p1, G1.one(), G1.one()); + G1.add(p1, p1, G1.one()); + G1.add(p1, p1, G1.one()); + + G1Point p2; + G1.dbl(p2, G1.one()); + G1.dbl(p2, p2); + + ASSERT_TRUE(G1.eq(p1,p2)); +} + +TEST(altBn128, g1_times_3) { + G1Point p1; + G1.add(p1, G1.one(), G1.one()); + G1.add(p1, p1, G1.one()); + + G1Point p2; + G1.dbl(p2, G1.one()); + G1.dbl(p2, p2); + G1.sub(p2, p2, G1.one()); + + ASSERT_TRUE(G1.eq(p1,p2)); +} + +TEST(altBn128, g1_times_3_exp) { + G1Point p1; + G1.add(p1, G1.one(), G1.one()); + G1.add(p1, p1, G1.one()); + + uint8_t scalar[32]; + u256_scalar32_from_u64(scalar, 3); + + G1Point p2; + G1.mulByScalar(p2, G1.one(), scalar, 32); + + ASSERT_TRUE(G1.eq(p1,p2)); +} + +TEST(altBn128, g1_times_5) { + G1Point p1; + G1.dbl(p1, G1.one()); + G1.dbl(p1, p1); + G1.add(p1, p1, p1); + + G1Point p2; + G1Point p3; + G1Point p4; + G1Point p5; + G1Point p6; + G1.dbl(p2, G1.one()); + G1.dbl(p3, p2); + G1.dbl(p4, G1.one()); + G1.dbl(p5, p4); + G1.add(p6, p3, p5); + + ASSERT_TRUE(G1.eq(p1,p6)); +} + +TEST(altBn128, g1_times_65_exp) { + G1Point p1; + G1.dbl(p1, G1.one()); + G1.dbl(p1, p1); + G1.dbl(p1, p1); + G1.dbl(p1, p1); + G1.dbl(p1, p1); + G1.dbl(p1, p1); + G1.add(p1, p1, G1.one()); + + uint8_t scalar[32]; + u256_scalar32_from_u64(scalar, 65); + + G1Point p2; + G1.mulByScalar(p2, G1.one(), scalar, 32); + + ASSERT_TRUE(G1.eq(p1,p2)); +} + +TEST(altBn128, g1_expToOrder) { + uint8_t scalar[32]; + ASSERT_EQ(u256_scalar32_from_dec(scalar, + "21888242871839275222246405745257275088548364400416034343698204186575808495617" + ), 0); + + G1Point p1; + G1.mulByScalar(p1, G1.one(), scalar, 32); + + ASSERT_TRUE(G1.isZero(p1)); +} + +TEST(altBn128, g2_expToOrder) { + uint8_t scalar[32]; + ASSERT_EQ(u256_scalar32_from_dec(scalar, + "21888242871839275222246405745257275088548364400416034343698204186575808495617" + ), 0); + + Curve>::Point p1; + G2.mulByScalar(p1, G2.one(), scalar, 32); + + ASSERT_TRUE(G2.isZero(p1)); +} + +TEST(altBn128, multiExp) { + int NMExp = 40000; + + typedef uint8_t Scalar[32]; + + Scalar *scalars = new Scalar[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + + uint64_t acc = 0; + for (int i = 0; i < NMExp; i++) { + if (i == 0) { + G1.copy(bases[0], G1.one()); + } else { + G1.add(bases[i], bases[i-1], G1.one()); + } + + u256_scalar32_from_u64(scalars[i], (uint64_t)(i + 1)); + acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); + } + + G1Point p1; + G1.multiMulByScalar(p1, bases, (uint8_t *)scalars, 32, NMExp); + + uint8_t sAcc[32]; + u256_scalar32_from_u64(sAcc, acc); + + G1Point p2; + G1.mulByScalar(p2, G1.one(), sAcc, 32); + + ASSERT_TRUE(G1.eq(p1, p2)); + + delete[] bases; + delete[] scalars; +} + +TEST(altBn128, multiExpMSM) { + int NMExp = 40000; + + typedef uint8_t Scalar[32]; + + Scalar *scalars = new Scalar[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + + uint64_t acc = 0; + for (int i = 0; i < NMExp; i++) { + if (i == 0) { + G1.copy(bases[0], G1.one()); + } else { + G1.add(bases[i], bases[i-1], G1.one()); + } + + u256_scalar32_from_u64(scalars[i], (uint64_t)(i + 1)); + acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); + } + + G1Point p1; + G1.multiMulByScalarMSM(p1, bases, (uint8_t *)scalars, 32, NMExp); + + uint8_t sAcc[32]; + u256_scalar32_from_u64(sAcc, acc); + + G1Point p2; + G1.mulByScalar(p2, G1.one(), sAcc, 32); + + ASSERT_TRUE(G1.eq(p1, p2)); + + delete[] bases; + delete[] scalars; +} + +TEST(altBn128, multiExp2) { + int NMExp = 2; + + AltBn128::FrElement *scalars = new AltBn128::FrElement[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + + F1.fromString(bases[0].x, "1626275109576878988287730541908027724405348106427831594181487487855202143055"); + F1.fromString(bases[0].y, "18706364085805828895917702468512381358405767972162700276238017959231481018884"); + F1.fromString(bases[1].x, "17245156998235704504461341147511350131061011207199931581281143511105381019978"); + F1.fromString(bases[1].y, "3858908536032228066651712470282632925312300188207189106507111128103204506804"); + + Fr.fromString(scalars[0], "1"); + Fr.fromString(scalars[1], "20187316456970436521602619671088988952475789765726813868033071292105413408473"); + Fr.fromMontgomery(scalars[0], scalars[0]); + Fr.fromMontgomery(scalars[1], scalars[1]); + + G1Point r; + G1PointAffine ra; + G1PointAffine ref; + + F1.fromString(ref.x, "9163953212624378696742080269971059027061360176019470242548968584908855004282"); + F1.fromString(ref.y, "20922060990592511838374895951081914567856345629513259026540392951012456141360"); + + G1.multiMulByScalar(r, bases, (uint8_t *)scalars, 32, 2); + G1.copy(ra, r); + + ASSERT_TRUE(G1.eq(ra, ref)); + + delete[] bases; + delete[] scalars; +} + +TEST(altBn128, multiExp2MSM) { + int NMExp = 2; + + AltBn128::FrElement *scalars = new AltBn128::FrElement[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + + F1.fromString(bases[0].x, "1626275109576878988287730541908027724405348106427831594181487487855202143055"); + F1.fromString(bases[0].y, "18706364085805828895917702468512381358405767972162700276238017959231481018884"); + F1.fromString(bases[1].x, "17245156998235704504461341147511350131061011207199931581281143511105381019978"); + F1.fromString(bases[1].y, "3858908536032228066651712470282632925312300188207189106507111128103204506804"); + + Fr.fromString(scalars[0], "1"); + Fr.fromString(scalars[1], "20187316456970436521602619671088988952475789765726813868033071292105413408473"); + Fr.fromMontgomery(scalars[0], scalars[0]); + Fr.fromMontgomery(scalars[1], scalars[1]); + + G1Point r; + G1PointAffine ra; + G1PointAffine ref; + + F1.fromString(ref.x, "9163953212624378696742080269971059027061360176019470242548968584908855004282"); + F1.fromString(ref.y, "20922060990592511838374895951081914567856345629513259026540392951012456141360"); + + G1.multiMulByScalarMSM(r, bases, (uint8_t *)scalars, 32, 2); + G1.copy(ra, r); + + ASSERT_TRUE(G1.eq(ra, ref)); + + delete[] bases; + delete[] scalars; +} + +TEST(altBn128, multiExp8MSM) { + int NMExp = 8; + + AltBn128::FrElement *scalars = new AltBn128::FrElement[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + + F1.fromString(bases[0].x, "1"); + F1.fromString(bases[0].y, "2"); + F1.fromString(bases[1].x, "1368015179489954701390400359078579693043519447331113978918064868415326638035"); + F1.fromString(bases[1].y, "9918110051302171585080402603319702774565515993150576347155970296011118125764"); + F1.fromString(bases[2].x, "3353031288059533942658390886683067124040920775575537747144343083137631628272"); + F1.fromString(bases[2].y, "19321533766552368860946552437480515441416830039777911637913418824951667761761"); + F1.fromString(bases[3].x, "10744596414106452074759370245733544594153395043370666422502510773307029471145"); + F1.fromString(bases[3].y, "848677436511517736191562425154572367705380862894644942948681172815252343932"); + F1.fromString(bases[4].x, "10415861484417082502655338383609494480414113902179649885744799961447382638712"); + F1.fromString(bases[4].y, "10196215078179488638353184030336251401353352596818396260819493263908881608606"); + F1.fromString(bases[5].x, "4444740815889402603535294170722302758225367627362056425101568584910268024244"); + F1.fromString(bases[5].y, "10537263096529483164618820017164668921386457028564663708352735080900270541420"); + F1.fromString(bases[6].x, "12852522211178622728088728121177131998585782282560100422041774753646305409836"); + F1.fromString(bases[6].y, "15918672909255108529698304535345707578139606904951176064731093256171019744261"); + F1.fromString(bases[7].x, "5841054468380737358126901601208759440531393618333695939860021859990434602332"); + F1.fromString(bases[7].y, "14496198492936799798866613472708085382638873795843716312315177042738122955744"); + + Fr.fromString(scalars[0], "1"); + Fr.fromString(scalars[1], "4"); + Fr.fromString(scalars[2], "4"); + Fr.fromString(scalars[3], "4"); + Fr.fromString(scalars[4], "8"); + Fr.fromString(scalars[5], "10"); + Fr.fromString(scalars[6], "1"); + Fr.fromString(scalars[7], "5"); + Fr.fromMontgomery(scalars[0], scalars[0]); + Fr.fromMontgomery(scalars[1], scalars[1]); + Fr.fromMontgomery(scalars[2], scalars[2]); + Fr.fromMontgomery(scalars[3], scalars[3]); + Fr.fromMontgomery(scalars[4], scalars[4]); + Fr.fromMontgomery(scalars[5], scalars[5]); + Fr.fromMontgomery(scalars[6], scalars[6]); + Fr.fromMontgomery(scalars[7], scalars[7]); + + G1Point r; + G1PointAffine ra; + G1PointAffine ref; + + F1.fromString(ref.x, "17747920359253913546551417160303297937542312574889904290131615776238588901697"); + F1.fromString(ref.y, "8815119438581789680513912776342567599606944899217792926373871775002956510503"); + + G1.multiMulByScalarMSM(r, bases, (uint8_t *)scalars, 32, NMExp); + G1.copy(ra, r); + + ASSERT_TRUE(G1.eq(ra, ref)); + + delete[] bases; + delete[] scalars; +} + +TEST(altBn128, fft) { + int NMExp = 1<<10; + + AltBn128::FrElement *a = new AltBn128::FrElement[NMExp]; + + for (int i=0; i fft(NMExp); + + fft.fft(a, NMExp); + fft.ifft(a, NMExp); + + AltBn128::FrElement aux; + for (int i=0; i #include "misc.hpp" +#include "u256.hpp" using namespace std; @@ -30,68 +31,32 @@ static inline u_int64_t BR(u_int64_t x, u_int64_t domainPow) #define ROOT(s,j) (rootsOfUnit[(1<<(s))+(j)]) -template -static inline void shr_words(uint64_t (&out)[N64], const uint64_t (&in)[N64], u_int32_t shift) { - if (shift == 0) { - for (size_t i = 0; i < N64; i++) out[i] = in[i]; - return; - } - const u_int32_t wordShift = shift / 64; - const u_int32_t bitShift = shift % 64; - - for (size_t i = 0; i < N64; i++) out[i] = 0; - - for (size_t i = wordShift; i < N64; i++) { - uint64_t low = in[i]; - uint64_t hi = (i + 1 < N64) ? in[i + 1] : 0; - - if (bitShift == 0) { - out[i - wordShift] = low; - } else { - out[i - wordShift] = (low >> bitShift) | (hi << (64 - bitShift)); - } - } -} - -template -static inline void shr_words_inplace(uint64_t (&a)[N64], u_int32_t shift) { - uint64_t tmp[N64]; - shr_words(tmp, a, shift); - for (size_t i = 0; i < N64; i++) a[i] = tmp[i]; -} - template FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) : threadPool(ThreadPool::defaultPool()) { f = Field::field; - u_int32_t domainPow = log2(maxDomainSize); + static_assert(Field::N64 == 4, "FFT expects 256-bit field elements (4x64 limbs)."); -#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) - static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, - "This FFT exp-cast expects little-endian machine layout."); -#endif + u_int32_t domainPow = log2(maxDomainSize); - // qm1_norm = q-1 in NORMAL (non-montgomery) representation Element qm1_norm; f.fromMontgomery(qm1_norm, f.negOne()); - uint64_t qm1_words[Field::N64]; - std::memcpy(qm1_words, (const void*)qm1_norm.v, sizeof(qm1_words)); + U256 qm1; + std::memcpy(qm1.limb, (const void*)qm1_norm.v, sizeof(qm1.limb)); - // qm1d2 = (q-1)/2 - uint64_t qm1d2_words[Field::N64]; - shr_words(qm1d2_words, qm1_words, 1); + U256 qm1d2; + u256_fdiv_q_2exp(&qm1d2, &qm1, 1); - // find nqr: cand^((q-1)/2) != 1 Element cand, res; uint64_t cand_ui = 2; for (;;) { f.fromUI(cand, cand_ui); f.exp(res, cand, - reinterpret_cast(qm1d2_words), - (unsigned)sizeof(qm1d2_words)); + reinterpret_cast(qm1d2.limb), + (unsigned)sizeof(qm1d2.limb)); if (!f.eq(res, f.one())) { f.copy(nqr, cand); break; @@ -99,15 +64,13 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) cand_ui++; } - // aux_words starts from (q-1)/2 and we divide by 2 until we reach domainPow - // so at the end: aux_words = (q-1)/2^domainPow - uint64_t aux_words[Field::N64]; - for (size_t i = 0; i < (size_t)Field::N64; i++) aux_words[i] = qm1d2_words[i]; + U256 aux; + u256_copy(&aux, &qm1d2); u_int32_t s_tmp = 1; while (s_tmp < domainPow) { - if (aux_words[0] & 1ULL) break; // odd => stop - shr_words_inplace(aux_words, 1); + if (u256_tstbit(&aux, 0)) break; + u256_fdiv_q_2exp(&aux, &aux, 1); s_tmp++; } @@ -125,12 +88,10 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) f.copy(powTwoInv[0], f.one()); if (nRoots > 1) { - // primitive 2^s root of unity: roots[1] = nqr^(aux_words) f.exp(roots[1], nqr, - reinterpret_cast(aux_words), - (unsigned)sizeof(aux_words)); + reinterpret_cast(aux.limb), + (unsigned)sizeof(aux.limb)); - // powTwoInv[1] = 1/2 Element two; f.fromUI(two, 2); f.inv(powTwoInv[1], two); @@ -138,11 +99,10 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) threadPool.parallelBlock([&] (uint64_t nThreads, uint64_t idThread) { uint64_t increment = nRoots / nThreads; - uint64_t start = idThread==0 ? 2 : idThread * increment; - uint64_t end = idThread==nThreads-1 ? nRoots : (idThread+1) * increment; + uint64_t start = (idThread == 0) ? 2 : idThread * increment; + uint64_t end = (idThread == nThreads - 1) ? nRoots : (idThread + 1) * increment; if (end > start) { - // roots[start] = roots[1]^start f.exp(roots[start], roots[1], (uint8_t *)(&start), sizeof(start)); } for (uint64_t i = start + 1; i < end; i++) { @@ -150,9 +110,9 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) } }); - Element aux; - f.mul(aux, roots[nRoots - 1], roots[1]); - assert(f.eq(aux, f.one())); + Element check; + f.mul(check, roots[nRoots - 1], roots[1]); + assert(f.eq(check, f.one())); for (uint64_t i = 2; i <= s; i++) { f.mul(powTwoInv[i], powTwoInv[i-1], powTwoInv[1]); @@ -170,10 +130,10 @@ void FFT::reversePermutation(Element *a, u_int64_t n) { int domainPow = log2(n); threadPool.parallelFor(0, n, [&] (int begin, int end, int numThread) { - for (u_int64_t i=begin; ir) { + if (i > r) { f.copy(tmp, a[i]); f.copy(a[i], a[r]); f.copy(a[r], tmp); @@ -188,36 +148,36 @@ void FFT::fft(Element *a, u_int64_t n) { u_int64_t domainPow = log2(n); assert(((u_int64_t)1 << domainPow) == n); - for (u_int32_t s=1; s<=domainPow; s++) { + for (u_int32_t s = 1; s <= domainPow; s++) { u_int64_t m = 1 << s; u_int64_t mdiv2 = m >> 1; - threadPool.parallelFor(0, (n>>1), [&] (int begin, int end, int numThread) { - for (u_int64_t i=begin; i< (u_int64_t)end; i++) { + threadPool.parallelFor(0, (n >> 1), [&] (int begin, int end, int numThread) { + for (u_int64_t i = (u_int64_t)begin; i < (u_int64_t)end; i++) { Element t; Element u; - u_int64_t k=(i/mdiv2)*m; - u_int64_t j=i%mdiv2; + u_int64_t k = (i / mdiv2) * m; + u_int64_t j = i % mdiv2; - f.mul(t, root(s, j), a[k+j+mdiv2]); - f.copy(u, a[k+j]); - f.add(a[k+j], t, u); - f.sub(a[k+j+mdiv2], u, t); + f.mul(t, root(s, j), a[k + j + mdiv2]); + f.copy(u, a[k + j]); + f.add(a[k + j], t, u); + f.sub(a[k + j + mdiv2], u, t); } }); } } template -void FFT::ifft(Element *a, u_int64_t n ) { +void FFT::ifft(Element *a, u_int64_t n) { fft(a, n); u_int64_t domainPow = log2(n); - u_int64_t nDiv2= n >> 1; + u_int64_t nDiv2 = n >> 1; threadPool.parallelFor(1, nDiv2, [&] (int begin, int end, int numThread) { - for (u_int64_t i=begin; i<(u_int64_t)end; i++) { + for (u_int64_t i = (u_int64_t)begin; i < (u_int64_t)end; i++) { Element tmp; - u_int64_t r = n-i; + u_int64_t r = n - i; f.copy(tmp, a[i]); f.mul(a[i], a[r], powTwoInv[domainPow]); f.mul(a[r], tmp, powTwoInv[domainPow]); @@ -229,9 +189,9 @@ void FFT::ifft(Element *a, u_int64_t n ) { } template -void FFT::printVector(Element *a, u_int64_t n ) { +void FFT::printVector(Element *a, u_int64_t n) { cout << "[" << endl; - for (u_int64_t i=0; i Date: Mon, 19 Jan 2026 01:36:44 +0200 Subject: [PATCH 03/13] nogmp: add testAltBn128 to taskfile.js --- tasksfile.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tasksfile.js b/tasksfile.js index bd6031f..1f6883b 100644 --- a/tasksfile.js +++ b/tasksfile.js @@ -46,6 +46,28 @@ function testSplitParStr() { sh("./splitparsestr_test", {cwd: "build", nopipe: true}); } +function testAltBn128() { + sh("g++" + + " -Igoogletest-release-1.10.0/googletest/include"+ + " -I."+ + " -I../c"+ + " ../c/naf.cpp"+ + " ../c/splitparstr.cpp"+ + " ../c/alt_bn128.cpp"+ + " ../c/alt_bn128_test.cpp"+ + " ../c/misc.cpp"+ + " fq.cpp"+ + " fq.o"+ + " fr.cpp"+ + " fr.o"+ + " googletest-release-1.10.0/libgtest.a"+ + " -o altbn128_test" + + " -fmax-errors=5 -pthread -std=c++11 -fopenmp -lgmp -g", {cwd: "build", nopipe: true} + ); + sh("./altbn128_test", {cwd: "build", nopipe: true}); +} + + function benchMultiExpG1() { sh("g++ -O3 -g" + " -Igoogletest-release-1.10.0/googletest/include"+ From 73ce049755e4421f80ac247777bbf1b166fc4ff1 Mon Sep 17 00:00:00 2001 From: furze Date: Tue, 27 Jan 2026 15:27:46 +0200 Subject: [PATCH 04/13] nogmp: update --- c/alt_bn128_test.cpp | 46 ++++++++++++++++++++++++++++++-------------- c/fft.cpp | 8 ++++---- tasksfile.js | 8 ++++---- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp index fd28aab..fdf1591 100644 --- a/c/alt_bn128_test.cpp +++ b/c/alt_bn128_test.cpp @@ -129,13 +129,15 @@ TEST(altBn128, g1_times_3) { ASSERT_TRUE(G1.eq(p1,p2)); } -TEST(altBn128, g1_times_3_exp) { + TEST(altBn128, g1_times_3_exp) { G1Point p1; G1.add(p1, G1.one(), G1.one()); G1.add(p1, p1, G1.one()); uint8_t scalar[32]; - u256_scalar32_from_u64(scalar, 3); + U256 x; + mp_set_ui(&x, 3); + mp_export(scalar, &x); G1Point p2; G1.mulByScalar(p2, G1.one(), scalar, 32); @@ -163,7 +165,7 @@ TEST(altBn128, g1_times_5) { ASSERT_TRUE(G1.eq(p1,p6)); } -TEST(altBn128, g1_times_65_exp) { + TEST(altBn128, g1_times_65_exp) { G1Point p1; G1.dbl(p1, G1.one()); G1.dbl(p1, p1); @@ -174,7 +176,9 @@ TEST(altBn128, g1_times_65_exp) { G1.add(p1, p1, G1.one()); uint8_t scalar[32]; - u256_scalar32_from_u64(scalar, 65); + U256 x; + mp_set_ui(&x, 65); + mp_export(scalar, &x); G1Point p2; G1.mulByScalar(p2, G1.one(), scalar, 32); @@ -182,11 +186,14 @@ TEST(altBn128, g1_times_65_exp) { ASSERT_TRUE(G1.eq(p1,p2)); } -TEST(altBn128, g1_expToOrder) { + TEST(altBn128, g1_expToOrder) { uint8_t scalar[32]; - ASSERT_EQ(u256_scalar32_from_dec(scalar, - "21888242871839275222246405745257275088548364400416034343698204186575808495617" + U256 x; + ASSERT_EQ(mp_set_str(&x, + "21888242871839275222246405745257275088548364400416034343698204186575808495617", + 10 ), 0); + mp_export(scalar, &x); G1Point p1; G1.mulByScalar(p1, G1.one(), scalar, 32); @@ -194,11 +201,14 @@ TEST(altBn128, g1_expToOrder) { ASSERT_TRUE(G1.isZero(p1)); } -TEST(altBn128, g2_expToOrder) { + TEST(altBn128, g2_expToOrder) { uint8_t scalar[32]; - ASSERT_EQ(u256_scalar32_from_dec(scalar, - "21888242871839275222246405745257275088548364400416034343698204186575808495617" + U256 x; + ASSERT_EQ(mp_set_str(&x, + "21888242871839275222246405745257275088548364400416034343698204186575808495617", + 10 ), 0); + mp_export(scalar, &x); Curve>::Point p1; G2.mulByScalar(p1, G2.one(), scalar, 32); @@ -222,7 +232,9 @@ TEST(altBn128, multiExp) { G1.add(bases[i], bases[i-1], G1.one()); } - u256_scalar32_from_u64(scalars[i], (uint64_t)(i + 1)); + U256 x; + mp_set_ui(&x, (uint64_t)(i + 1)); + mp_export(scalars[i], &x); acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); } @@ -230,7 +242,9 @@ TEST(altBn128, multiExp) { G1.multiMulByScalar(p1, bases, (uint8_t *)scalars, 32, NMExp); uint8_t sAcc[32]; - u256_scalar32_from_u64(sAcc, acc); + U256 x; + mp_set_ui(&x, acc); + mp_export(sAcc, &x); G1Point p2; G1.mulByScalar(p2, G1.one(), sAcc, 32); @@ -257,7 +271,9 @@ TEST(altBn128, multiExpMSM) { G1.add(bases[i], bases[i-1], G1.one()); } - u256_scalar32_from_u64(scalars[i], (uint64_t)(i + 1)); + U256 x; + mp_set_ui(&x, (uint64_t)(i + 1)); + mp_export(scalars[i], &x); acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); } @@ -265,7 +281,9 @@ TEST(altBn128, multiExpMSM) { G1.multiMulByScalarMSM(p1, bases, (uint8_t *)scalars, 32, NMExp); uint8_t sAcc[32]; - u256_scalar32_from_u64(sAcc, acc); + U256 x; + mp_set_ui(&x, acc); + mp_export(sAcc, &x); G1Point p2; G1.mulByScalar(p2, G1.one(), sAcc, 32); diff --git a/c/fft.cpp b/c/fft.cpp index 5d50894..04206d0 100644 --- a/c/fft.cpp +++ b/c/fft.cpp @@ -48,7 +48,7 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) std::memcpy(qm1.limb, (const void*)qm1_norm.v, sizeof(qm1.limb)); U256 qm1d2; - u256_fdiv_q_2exp(&qm1d2, &qm1, 1); + mp_fdiv_q_2exp(&qm1d2, &qm1, 1); Element cand, res; uint64_t cand_ui = 2; @@ -65,12 +65,12 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) } U256 aux; - u256_copy(&aux, &qm1d2); + mp_copy(&aux, &qm1d2); u_int32_t s_tmp = 1; while (s_tmp < domainPow) { - if (u256_tstbit(&aux, 0)) break; - u256_fdiv_q_2exp(&aux, &aux, 1); + if (mp_tstbit(&aux, 0)) break; + mp_fdiv_q_2exp(&aux, &aux, 1); s_tmp++; } diff --git a/tasksfile.js b/tasksfile.js index 1f6883b..af91d4f 100644 --- a/tasksfile.js +++ b/tasksfile.js @@ -41,7 +41,7 @@ function testSplitParStr() { " ../c/splitparstr.cpp"+ " ../c/splitparstr_test.cpp"+ " googletest-release-1.10.0/libgtest.a"+ - " -pthread -std=c++11 -o splitparsestr_test", {cwd: "build", nopipe: true} + " -pthread -std=c++17 -o splitparsestr_test", {cwd: "build", nopipe: true} ); sh("./splitparsestr_test", {cwd: "build", nopipe: true}); } @@ -62,7 +62,7 @@ function testAltBn128() { " fr.o"+ " googletest-release-1.10.0/libgtest.a"+ " -o altbn128_test" + - " -fmax-errors=5 -pthread -std=c++11 -fopenmp -lgmp -g", {cwd: "build", nopipe: true} + " -fmax-errors=5 -pthread -std=c++17 -fopenmp -lgmp -g", {cwd: "build", nopipe: true} ); sh("./altbn128_test", {cwd: "build", nopipe: true}); } @@ -84,7 +84,7 @@ function benchMultiExpG1() { " fr.o"+ // " googletest-release-1.10.0/libgtest.a"+ " -o multiexp_g1_benchmark" + - " -lgmp -pthread -std=c++11 -fopenmp" , {cwd: "build", nopipe: true} + " -lgmp -pthread -std=c++17 -fopenmp" , {cwd: "build", nopipe: true} ); sh("./multiexp_g1_benchmark 16777216", {cwd: "build", nopipe: true}); } @@ -105,7 +105,7 @@ function benchMultiExpG2() { " fr.o"+ // " googletest-release-1.10.0/libgtest.a"+ " -o multiexp_g2_benchmark" + - " -lgmp -pthread -std=c++11 -fopenmp" , {cwd: "build", nopipe: true} + " -lgmp -pthread -std=c++17 -fopenmp" , {cwd: "build", nopipe: true} ); sh("./multiexp_g2_benchmark 16777216", {cwd: "build", nopipe: true}); } From 9eaffd0042b1714e46f5a35ad3caec3486486ea6 Mon Sep 17 00:00:00 2001 From: furze Date: Mon, 2 Feb 2026 12:08:16 +0200 Subject: [PATCH 05/13] U256 API removed, using Limb API instead --- c/alt_bn128_test.cpp | 50 ++++++++++++++++++++++---------------------- c/fft.cpp | 26 +++++++++++------------ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp index fdf1591..9b965bc 100644 --- a/c/alt_bn128_test.cpp +++ b/c/alt_bn128_test.cpp @@ -5,7 +5,7 @@ #include "gtest/gtest.h" #include "alt_bn128.hpp" #include "fft.hpp" -#include "u256.hpp" +#include "mp.hpp" using namespace AltBn128; @@ -135,9 +135,9 @@ TEST(altBn128, g1_times_3) { G1.add(p1, p1, G1.one()); uint8_t scalar[32]; - U256 x; - mp_set_ui(&x, 3); - mp_export(scalar, &x); + mp_limb_t x[MP_N64]; + mp_set_ui(x, 3); + mp_export(scalar, x); G1Point p2; G1.mulByScalar(p2, G1.one(), scalar, 32); @@ -176,9 +176,9 @@ TEST(altBn128, g1_times_5) { G1.add(p1, p1, G1.one()); uint8_t scalar[32]; - U256 x; - mp_set_ui(&x, 65); - mp_export(scalar, &x); + mp_limb_t x[MP_N64]; + mp_set_ui(x, 65); + mp_export(scalar, x); G1Point p2; G1.mulByScalar(p2, G1.one(), scalar, 32); @@ -188,12 +188,12 @@ TEST(altBn128, g1_times_5) { TEST(altBn128, g1_expToOrder) { uint8_t scalar[32]; - U256 x; - ASSERT_EQ(mp_set_str(&x, + mp_limb_t x[MP_N64]; + ASSERT_EQ(mp_set_str(x, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10 ), 0); - mp_export(scalar, &x); + mp_export(scalar, x); G1Point p1; G1.mulByScalar(p1, G1.one(), scalar, 32); @@ -203,12 +203,12 @@ TEST(altBn128, g1_times_5) { TEST(altBn128, g2_expToOrder) { uint8_t scalar[32]; - U256 x; - ASSERT_EQ(mp_set_str(&x, + mp_limb_t x[MP_N64]; + ASSERT_EQ(mp_set_str(x, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10 ), 0); - mp_export(scalar, &x); + mp_export(scalar, x); Curve>::Point p1; G2.mulByScalar(p1, G2.one(), scalar, 32); @@ -232,9 +232,9 @@ TEST(altBn128, multiExp) { G1.add(bases[i], bases[i-1], G1.one()); } - U256 x; - mp_set_ui(&x, (uint64_t)(i + 1)); - mp_export(scalars[i], &x); + mp_limb_t x[MP_N64]; + mp_set_ui(x, (uint64_t)(i + 1)); + mp_export(scalars[i], x); acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); } @@ -242,9 +242,9 @@ TEST(altBn128, multiExp) { G1.multiMulByScalar(p1, bases, (uint8_t *)scalars, 32, NMExp); uint8_t sAcc[32]; - U256 x; - mp_set_ui(&x, acc); - mp_export(sAcc, &x); + mp_limb_t x[MP_N64]; + mp_set_ui(x, acc); + mp_export(sAcc, x); G1Point p2; G1.mulByScalar(p2, G1.one(), sAcc, 32); @@ -271,9 +271,9 @@ TEST(altBn128, multiExpMSM) { G1.add(bases[i], bases[i-1], G1.one()); } - U256 x; - mp_set_ui(&x, (uint64_t)(i + 1)); - mp_export(scalars[i], &x); + mp_limb_t x[MP_N64]; + mp_set_ui(x, (uint64_t)(i + 1)); + mp_export(scalars[i], x); acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); } @@ -281,9 +281,9 @@ TEST(altBn128, multiExpMSM) { G1.multiMulByScalarMSM(p1, bases, (uint8_t *)scalars, 32, NMExp); uint8_t sAcc[32]; - U256 x; - mp_set_ui(&x, acc); - mp_export(sAcc, &x); + mp_limb_t x[MP_N64]; + mp_set_ui(x, acc); + mp_export(sAcc, x); G1Point p2; G1.mulByScalar(p2, G1.one(), sAcc, 32); diff --git a/c/fft.cpp b/c/fft.cpp index 04206d0..29c6cd9 100644 --- a/c/fft.cpp +++ b/c/fft.cpp @@ -5,7 +5,7 @@ #include #include "misc.hpp" -#include "u256.hpp" +#include "mp.hpp" using namespace std; @@ -44,19 +44,19 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) Element qm1_norm; f.fromMontgomery(qm1_norm, f.negOne()); - U256 qm1; - std::memcpy(qm1.limb, (const void*)qm1_norm.v, sizeof(qm1.limb)); + mp_limb_t qm1[4]; + std::memcpy(qm1, (const void*)qm1_norm.v, sizeof(qm1)); - U256 qm1d2; - mp_fdiv_q_2exp(&qm1d2, &qm1, 1); + mp_limb_t qm1d2[4]; + mp_fdiv_q_2exp(qm1d2, qm1, 1); Element cand, res; uint64_t cand_ui = 2; for (;;) { f.fromUI(cand, cand_ui); f.exp(res, cand, - reinterpret_cast(qm1d2.limb), - (unsigned)sizeof(qm1d2.limb)); + reinterpret_cast(qm1d2), + (unsigned)sizeof(qm1d2)); if (!f.eq(res, f.one())) { f.copy(nqr, cand); break; @@ -64,13 +64,13 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) cand_ui++; } - U256 aux; - mp_copy(&aux, &qm1d2); + mp_limb_t aux[4]; + mp_copy(aux, qm1d2); u_int32_t s_tmp = 1; while (s_tmp < domainPow) { - if (mp_tstbit(&aux, 0)) break; - mp_fdiv_q_2exp(&aux, &aux, 1); + if (mp_tstbit(aux, 0)) break; + mp_fdiv_q_2exp(aux, aux, 1); s_tmp++; } @@ -89,8 +89,8 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) if (nRoots > 1) { f.exp(roots[1], nqr, - reinterpret_cast(aux.limb), - (unsigned)sizeof(aux.limb)); + reinterpret_cast(aux), + (unsigned)sizeof(aux)); Element two; f.fromUI(two, 2); From 68f2a463985822b252e1858e81f40fe6a6c373e6 Mon Sep 17 00:00:00 2001 From: furze Date: Sun, 8 Feb 2026 13:44:52 +0200 Subject: [PATCH 06/13] nogmp: alt_bn128_test, fft fix up to new version of mp API --- c/alt_bn128_test.cpp | 70 ++++++++++++++++++++++---------------------- c/fft.cpp | 13 ++++---- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp index 9b965bc..5fdf1ff 100644 --- a/c/alt_bn128_test.cpp +++ b/c/alt_bn128_test.cpp @@ -134,13 +134,13 @@ TEST(altBn128, g1_times_3) { G1.add(p1, G1.one(), G1.one()); G1.add(p1, p1, G1.one()); - uint8_t scalar[32]; - mp_limb_t x[MP_N64]; - mp_set_ui(x, 3); + uint8_t scalar[MP_N]; + mp_uint_t x; + mp_set(x, 3); mp_export(scalar, x); G1Point p2; - G1.mulByScalar(p2, G1.one(), scalar, 32); + G1.mulByScalar(p2, G1.one(), scalar, MP_N); ASSERT_TRUE(G1.eq(p1,p2)); } @@ -175,43 +175,43 @@ TEST(altBn128, g1_times_5) { G1.dbl(p1, p1); G1.add(p1, p1, G1.one()); - uint8_t scalar[32]; - mp_limb_t x[MP_N64]; - mp_set_ui(x, 65); + uint8_t scalar[MP_N]; + mp_uint_t x; + mp_set(x, 65); mp_export(scalar, x); G1Point p2; - G1.mulByScalar(p2, G1.one(), scalar, 32); + G1.mulByScalar(p2, G1.one(), scalar, MP_N); ASSERT_TRUE(G1.eq(p1,p2)); } TEST(altBn128, g1_expToOrder) { - uint8_t scalar[32]; - mp_limb_t x[MP_N64]; - ASSERT_EQ(mp_set_str(x, + uint8_t scalar[MP_N]; + mp_uint_t x; + ASSERT_EQ(mp_set(x, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10 ), 0); mp_export(scalar, x); G1Point p1; - G1.mulByScalar(p1, G1.one(), scalar, 32); + G1.mulByScalar(p1, G1.one(), scalar, MP_N); ASSERT_TRUE(G1.isZero(p1)); } TEST(altBn128, g2_expToOrder) { - uint8_t scalar[32]; - mp_limb_t x[MP_N64]; - ASSERT_EQ(mp_set_str(x, + uint8_t scalar[MP_N]; + mp_uint_t x; + ASSERT_EQ(mp_set(x, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10 ), 0); mp_export(scalar, x); Curve>::Point p1; - G2.mulByScalar(p1, G2.one(), scalar, 32); + G2.mulByScalar(p1, G2.one(), scalar, MP_N); ASSERT_TRUE(G2.isZero(p1)); } @@ -219,7 +219,7 @@ TEST(altBn128, g1_times_5) { TEST(altBn128, multiExp) { int NMExp = 40000; - typedef uint8_t Scalar[32]; + typedef uint8_t Scalar[MP_N]; Scalar *scalars = new Scalar[NMExp]; G1PointAffine *bases = new G1PointAffine[NMExp]; @@ -232,22 +232,22 @@ TEST(altBn128, multiExp) { G1.add(bases[i], bases[i-1], G1.one()); } - mp_limb_t x[MP_N64]; - mp_set_ui(x, (uint64_t)(i + 1)); + mp_uint_t x; + mp_set(x, (uint64_t)(i + 1)); mp_export(scalars[i], x); acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); } G1Point p1; - G1.multiMulByScalar(p1, bases, (uint8_t *)scalars, 32, NMExp); + G1.multiMulByScalar(p1, bases, (uint8_t *)scalars, MP_N, NMExp); - uint8_t sAcc[32]; - mp_limb_t x[MP_N64]; - mp_set_ui(x, acc); + uint8_t sAcc[MP_N]; + mp_uint_t x; + mp_set(x, acc); mp_export(sAcc, x); G1Point p2; - G1.mulByScalar(p2, G1.one(), sAcc, 32); + G1.mulByScalar(p2, G1.one(), sAcc, MP_N); ASSERT_TRUE(G1.eq(p1, p2)); @@ -258,7 +258,7 @@ TEST(altBn128, multiExp) { TEST(altBn128, multiExpMSM) { int NMExp = 40000; - typedef uint8_t Scalar[32]; + typedef uint8_t Scalar[MP_N]; Scalar *scalars = new Scalar[NMExp]; G1PointAffine *bases = new G1PointAffine[NMExp]; @@ -271,22 +271,22 @@ TEST(altBn128, multiExpMSM) { G1.add(bases[i], bases[i-1], G1.one()); } - mp_limb_t x[MP_N64]; - mp_set_ui(x, (uint64_t)(i + 1)); + mp_uint_t x; + mp_set(x, (uint64_t)(i + 1)); mp_export(scalars[i], x); acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); } G1Point p1; - G1.multiMulByScalarMSM(p1, bases, (uint8_t *)scalars, 32, NMExp); + G1.multiMulByScalarMSM(p1, bases, (uint8_t *)scalars, MP_N, NMExp); - uint8_t sAcc[32]; - mp_limb_t x[MP_N64]; - mp_set_ui(x, acc); + uint8_t sAcc[MP_N]; + mp_uint_t x; + mp_set(x, acc); mp_export(sAcc, x); G1Point p2; - G1.mulByScalar(p2, G1.one(), sAcc, 32); + G1.mulByScalar(p2, G1.one(), sAcc, MP_N); ASSERT_TRUE(G1.eq(p1, p2)); @@ -317,7 +317,7 @@ TEST(altBn128, multiExp2) { F1.fromString(ref.x, "9163953212624378696742080269971059027061360176019470242548968584908855004282"); F1.fromString(ref.y, "20922060990592511838374895951081914567856345629513259026540392951012456141360"); - G1.multiMulByScalar(r, bases, (uint8_t *)scalars, 32, 2); + G1.multiMulByScalar(r, bases, (uint8_t *)scalars, MP_N, 2); G1.copy(ra, r); ASSERT_TRUE(G1.eq(ra, ref)); @@ -349,7 +349,7 @@ TEST(altBn128, multiExp2MSM) { F1.fromString(ref.x, "9163953212624378696742080269971059027061360176019470242548968584908855004282"); F1.fromString(ref.y, "20922060990592511838374895951081914567856345629513259026540392951012456141360"); - G1.multiMulByScalarMSM(r, bases, (uint8_t *)scalars, 32, 2); + G1.multiMulByScalarMSM(r, bases, (uint8_t *)scalars, MP_N, 2); G1.copy(ra, r); ASSERT_TRUE(G1.eq(ra, ref)); @@ -405,7 +405,7 @@ TEST(altBn128, multiExp8MSM) { F1.fromString(ref.x, "17747920359253913546551417160303297937542312574889904290131615776238588901697"); F1.fromString(ref.y, "8815119438581789680513912776342567599606944899217792926373871775002956510503"); - G1.multiMulByScalarMSM(r, bases, (uint8_t *)scalars, 32, NMExp); + G1.multiMulByScalarMSM(r, bases, (uint8_t *)scalars, MP_N, NMExp); G1.copy(ra, r); ASSERT_TRUE(G1.eq(ra, ref)); diff --git a/c/fft.cpp b/c/fft.cpp index 29c6cd9..82193d2 100644 --- a/c/fft.cpp +++ b/c/fft.cpp @@ -6,6 +6,7 @@ #include "misc.hpp" #include "mp.hpp" +#include "../../../build/mp.hpp" using namespace std; @@ -26,7 +27,7 @@ static inline u_int64_t BR(u_int64_t x, u_int64_t domainPow) x = ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8); x = ((x & 0xF0F0F0F0) >> 4) | ((x & 0x0F0F0F0F) << 4); x = ((x & 0xCCCCCCCC) >> 2) | ((x & 0x33333333) << 2); - return (((x & 0xAAAAAAAA) >> 1) | ((x & 0x55555555) << 1)) >> (32-domainPow); + return (((x & 0xAAAAAAAA) >> 1) | ((x & 0x55555555) << 1)) >> (MP_N-domainPow); } #define ROOT(s,j) (rootsOfUnit[(1<<(s))+(j)]) @@ -44,11 +45,11 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) Element qm1_norm; f.fromMontgomery(qm1_norm, f.negOne()); - mp_limb_t qm1[4]; + mp_uint_t qm1; std::memcpy(qm1, (const void*)qm1_norm.v, sizeof(qm1)); - mp_limb_t qm1d2[4]; - mp_fdiv_q_2exp(qm1d2, qm1, 1); + mp_uint_t qm1d2; + mp_shr(qm1d2, qm1, 1); Element cand, res; uint64_t cand_ui = 2; @@ -64,13 +65,13 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) cand_ui++; } - mp_limb_t aux[4]; + mp_uint_t aux; mp_copy(aux, qm1d2); u_int32_t s_tmp = 1; while (s_tmp < domainPow) { if (mp_tstbit(aux, 0)) break; - mp_fdiv_q_2exp(aux, aux, 1); + mp_shr(aux, aux, 1); s_tmp++; } From 387a902de6f4e400db1e9e3917fbebdb41e8e2ab Mon Sep 17 00:00:00 2001 From: furze Date: Fri, 20 Feb 2026 02:05:24 +0200 Subject: [PATCH 07/13] nogmp: mp-API fix --- c/alt_bn128_test.cpp | 46 ++++++++++++++++++++++---------------------- c/fft.cpp | 1 - 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp index 5fdf1ff..93d8e6c 100644 --- a/c/alt_bn128_test.cpp +++ b/c/alt_bn128_test.cpp @@ -5,7 +5,7 @@ #include "gtest/gtest.h" #include "alt_bn128.hpp" #include "fft.hpp" -#include "mp.hpp" +#include "../../../build/mp.hpp" using namespace AltBn128; @@ -134,13 +134,13 @@ TEST(altBn128, g1_times_3) { G1.add(p1, G1.one(), G1.one()); G1.add(p1, p1, G1.one()); - uint8_t scalar[MP_N]; + mp_uint_t scalar; mp_uint_t x; mp_set(x, 3); - mp_export(scalar, x); + mp_copy(scalar, x); G1Point p2; - G1.mulByScalar(p2, G1.one(), scalar, MP_N); + G1.mulByScalar(p2, G1.one(), (uint8_t*)scalar, MP_N); ASSERT_TRUE(G1.eq(p1,p2)); } @@ -175,43 +175,43 @@ TEST(altBn128, g1_times_5) { G1.dbl(p1, p1); G1.add(p1, p1, G1.one()); - uint8_t scalar[MP_N]; + mp_uint_t scalar; mp_uint_t x; mp_set(x, 65); - mp_export(scalar, x); + mp_copy(scalar, x); G1Point p2; - G1.mulByScalar(p2, G1.one(), scalar, MP_N); + G1.mulByScalar(p2, G1.one(), (uint8_t*)scalar, MP_N); ASSERT_TRUE(G1.eq(p1,p2)); } TEST(altBn128, g1_expToOrder) { - uint8_t scalar[MP_N]; + mp_uint_t scalar; mp_uint_t x; ASSERT_EQ(mp_set(x, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10 ), 0); - mp_export(scalar, x); + mp_copy(scalar, x); G1Point p1; - G1.mulByScalar(p1, G1.one(), scalar, MP_N); + G1.mulByScalar(p1, G1.one(), (uint8_t *)scalar, MP_N); ASSERT_TRUE(G1.isZero(p1)); } TEST(altBn128, g2_expToOrder) { - uint8_t scalar[MP_N]; + mp_uint_t scalar; mp_uint_t x; ASSERT_EQ(mp_set(x, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10 ), 0); - mp_export(scalar, x); + mp_copy(scalar, x); Curve>::Point p1; - G2.mulByScalar(p1, G2.one(), scalar, MP_N); + G2.mulByScalar(p1, G2.one(), (uint8_t *)scalar, MP_N); ASSERT_TRUE(G2.isZero(p1)); } @@ -219,7 +219,7 @@ TEST(altBn128, g1_times_5) { TEST(altBn128, multiExp) { int NMExp = 40000; - typedef uint8_t Scalar[MP_N]; + typedef mp_uint_t Scalar; Scalar *scalars = new Scalar[NMExp]; G1PointAffine *bases = new G1PointAffine[NMExp]; @@ -234,20 +234,20 @@ TEST(altBn128, multiExp) { mp_uint_t x; mp_set(x, (uint64_t)(i + 1)); - mp_export(scalars[i], x); + mp_copy(scalars[i], x); acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); } G1Point p1; G1.multiMulByScalar(p1, bases, (uint8_t *)scalars, MP_N, NMExp); - uint8_t sAcc[MP_N]; + mp_uint_t sAcc; mp_uint_t x; mp_set(x, acc); - mp_export(sAcc, x); + mp_copy(sAcc, x); G1Point p2; - G1.mulByScalar(p2, G1.one(), sAcc, MP_N); + G1.mulByScalar(p2, G1.one(), (uint8_t *)sAcc, MP_N); ASSERT_TRUE(G1.eq(p1, p2)); @@ -258,7 +258,7 @@ TEST(altBn128, multiExp) { TEST(altBn128, multiExpMSM) { int NMExp = 40000; - typedef uint8_t Scalar[MP_N]; + typedef mp_uint_t Scalar; Scalar *scalars = new Scalar[NMExp]; G1PointAffine *bases = new G1PointAffine[NMExp]; @@ -273,20 +273,20 @@ TEST(altBn128, multiExpMSM) { mp_uint_t x; mp_set(x, (uint64_t)(i + 1)); - mp_export(scalars[i], x); + mp_copy(scalars[i], x); acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); } G1Point p1; G1.multiMulByScalarMSM(p1, bases, (uint8_t *)scalars, MP_N, NMExp); - uint8_t sAcc[MP_N]; + mp_uint_t sAcc; mp_uint_t x; mp_set(x, acc); - mp_export(sAcc, x); + mp_copy(sAcc, x); G1Point p2; - G1.mulByScalar(p2, G1.one(), sAcc, MP_N); + G1.mulByScalar(p2, G1.one(), (uint8_t *)sAcc, MP_N); ASSERT_TRUE(G1.eq(p1, p2)); diff --git a/c/fft.cpp b/c/fft.cpp index 82193d2..2a62c0a 100644 --- a/c/fft.cpp +++ b/c/fft.cpp @@ -5,7 +5,6 @@ #include #include "misc.hpp" -#include "mp.hpp" #include "../../../build/mp.hpp" using namespace std; From c760c0b91b0262999a7b03d7ff236d4d193e4ef4 Mon Sep 17 00:00:00 2001 From: furze Date: Thu, 12 Mar 2026 15:38:00 +0200 Subject: [PATCH 08/13] nogmp: update --- c/alt_bn128_test.cpp | 324 ++++++- c/curve.cpp | 190 +++- c/curve.hpp | 3 + c/fft.cpp | 205 +++-- c/fft.hpp | 1 + src/armbuilder.js | 52 +- src/buildzqfield.js | 2 +- src/fr.cpp.ejs | 329 +++---- src/fr.hpp.ejs | 98 +- src/fr_generic.cpp.ejs | 50 +- src/fr_raw_generic.cpp.ejs | 287 +++--- src/mp.cpp.ejs | 1385 +++++++++++++++++++++++++++++ src/mp.hpp.ejs | 54 ++ tasksfile.js | 300 +++++-- test/fieldasm.js | 10 +- test/tester/buildzqfieldtester.js | 100 ++- test/tester/tester.cpp | 51 +- 17 files changed, 2713 insertions(+), 728 deletions(-) create mode 100644 src/mp.cpp.ejs create mode 100644 src/mp.hpp.ejs diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp index 93d8e6c..7018729 100644 --- a/c/alt_bn128_test.cpp +++ b/c/alt_bn128_test.cpp @@ -5,8 +5,12 @@ #include "gtest/gtest.h" #include "alt_bn128.hpp" #include "fft.hpp" -#include "../../../build/mp.hpp" - +/* +//perf +#include +#include +#include +*/ using namespace AltBn128; namespace { @@ -189,10 +193,10 @@ TEST(altBn128, g1_times_5) { TEST(altBn128, g1_expToOrder) { mp_uint_t scalar; mp_uint_t x; - ASSERT_EQ(mp_set(x, + ASSERT_TRUE(mp_set(x, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10 - ), 0); + )); mp_copy(scalar, x); G1Point p1; @@ -204,10 +208,10 @@ TEST(altBn128, g1_times_5) { TEST(altBn128, g2_expToOrder) { mp_uint_t scalar; mp_uint_t x; - ASSERT_EQ(mp_set(x, + ASSERT_TRUE(mp_set(x, "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10 - ), 0); + )); mp_copy(scalar, x); Curve>::Point p1; @@ -215,7 +219,7 @@ TEST(altBn128, g1_times_5) { ASSERT_TRUE(G2.isZero(p1)); } - +/* TEST(altBn128, multiExp) { int NMExp = 40000; @@ -250,11 +254,56 @@ TEST(altBn128, multiExp) { G1.mulByScalar(p2, G1.one(), (uint8_t *)sAcc, MP_N); ASSERT_TRUE(G1.eq(p1, p2)); + //G1.printCounters(); delete[] bases; delete[] scalars; } +*/ +TEST(altBn128, multiExp) { + int NMExp = 40000; + + typedef mp_uint_t Scalar; + + Scalar *scalars = new Scalar[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + G1Point *basesJac = new G1Point[NMExp]; + + uint64_t acc = 0; + + G1.copy(basesJac[0], G1.one()); + for (int i = 1; i < NMExp; i++) { + G1.add(basesJac[i], basesJac[i - 1], G1.one()); + } + + G1.batchToAffine(bases, basesJac, NMExp); + + for (int i = 0; i < NMExp; i++) { + mp_uint_t x; + mp_set(x, (uint64_t)(i + 1)); + mp_copy(scalars[i], x); + acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); + } + + G1Point p1; + G1.multiMulByScalar(p1, bases, (uint8_t *)scalars, MP_N, NMExp); + + mp_uint_t sAcc; + mp_uint_t x; + mp_set(x, acc); + mp_copy(sAcc, x); + + G1Point p2; + G1.mulByScalar(p2, G1.one(), (uint8_t *)sAcc, MP_N); + + ASSERT_TRUE(G1.eq(p1, p2)); + //G1.printCounters(); + delete[] basesJac; + delete[] bases; + delete[] scalars; +} +/* TEST(altBn128, multiExpMSM) { int NMExp = 40000; @@ -289,10 +338,54 @@ TEST(altBn128, multiExpMSM) { G1.mulByScalar(p2, G1.one(), (uint8_t *)sAcc, MP_N); ASSERT_TRUE(G1.eq(p1, p2)); + //G1.printCounters(); delete[] bases; delete[] scalars; } +*/ +TEST(altBn128, multiExpMSM) { + int NMExp = 40000; + + typedef mp_uint_t Scalar; + + Scalar *scalars = new Scalar[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + G1Point *basesJac = new G1Point[NMExp]; + + uint64_t acc = 0; + + G1.copy(basesJac[0], G1.one()); + for (int i = 1; i < NMExp; i++) { + G1.add(basesJac[i], basesJac[i - 1], G1.one()); + } + + G1.batchToAffine(bases, basesJac, NMExp); + + for (int i = 0; i < NMExp; i++) { + mp_uint_t x; + mp_set(x, (uint64_t)(i + 1)); + mp_copy(scalars[i], x); + acc += (uint64_t)(i + 1) * (uint64_t)(i + 1); + } + + G1Point p1; + G1.multiMulByScalarMSM(p1, bases, (uint8_t *)scalars, MP_N, NMExp); + + mp_uint_t sAcc; + mp_uint_t x; + mp_set(x, acc); + mp_copy(sAcc, x); + + G1Point p2; + G1.mulByScalar(p2, G1.one(), (uint8_t *)sAcc, MP_N); + + ASSERT_TRUE(G1.eq(p1, p2)); + + delete[] basesJac; + delete[] bases; + delete[] scalars; +} TEST(altBn128, multiExp2) { int NMExp = 2; @@ -436,10 +529,225 @@ TEST(altBn128, fft) { delete[] a; } +/* +using perf_clock = std::chrono::steady_clock; + +static inline double bench_ms(std::function fn) { + auto t0 = perf_clock::now(); + fn(); + auto t1 = perf_clock::now(); + return std::chrono::duration(t1 - t0).count(); +} + +static inline double ns_per_op(double ms, size_t n) { + return (ms * 1e6) / (double)n; +} + +TEST(altBn128Perf, g1_curve_ops_timing_nogmp) { + G1PointAffine a_aff, b_aff, c_aff; + G1.copy(a_aff, G1.one()); // P + G1.add(b_aff, a_aff, G1.one()); // 2P + G1.add(c_aff, b_aff, G1.one()); // 3P + + G1Point a_jac, b_jac, c_jac; + G1.copy(a_jac, a_aff); + G1.copy(b_jac, b_aff); + G1.copy(c_jac, c_aff); + + const size_t N_MIXED = 300000; + const size_t N_FULL = 200000; + const size_t N_DBL = 300000; + + G1Point acc_mixed; + G1.copy(acc_mixed, a_jac); + double ms_mixed = bench_ms([&]() { + for (size_t i = 0; i < N_MIXED; i++) { + G1.add(acc_mixed, acc_mixed, b_aff); + } + }); + + G1Point acc_full; + G1.copy(acc_full, a_jac); + double ms_full = bench_ms([&]() { + for (size_t i = 0; i < N_FULL; i++) { + G1.add(acc_full, acc_full, b_jac); + } + }); + + G1Point acc_dbl; + G1.copy(acc_dbl, c_jac); + double ms_dbl = bench_ms([&]() { + for (size_t i = 0; i < N_DBL; i++) { + G1.dbl(acc_dbl, acc_dbl); + } + }); + + ASSERT_FALSE(G1.isZero(acc_mixed)); + ASSERT_FALSE(G1.isZero(acc_full)); + ASSERT_FALSE(G1.isZero(acc_dbl)); + + std::cout << std::fixed << std::setprecision(3); + std::cout << "[perf][nogmp] G1.add mixed : " << ms_mixed << " ms (N=" << N_MIXED + << ") => " << ns_per_op(ms_mixed, N_MIXED) << " ns/op\n"; + std::cout << "[perf][nogmp] G1.add full : " << ms_full << " ms (N=" << N_FULL + << ") => " << ns_per_op(ms_full, N_FULL) << " ns/op\n"; + std::cout << "[perf][nogmp] G1.dbl : " << ms_dbl << " ms (N=" << N_DBL + << ") => " << ns_per_op(ms_dbl, N_DBL) << " ns/op\n"; +} + +TEST(altBn128Perf, multiexp_phases_timing_nogmp) { + const int NMExp = 40000; + + typedef mp_uint_t Scalar; + + Scalar *scalars = new Scalar[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + + double ms_prepare = bench_ms([&]() { + for (int i = 0; i < NMExp; i++) { + if (i == 0) { + G1.copy(bases[0], G1.one()); + } else { + G1.add(bases[i], bases[i - 1], G1.one()); + } + + mp_uint_t x; + mp_set(x, (uint64_t)(i + 1)); + mp_copy(scalars[i], x); + } + }); + + G1Point r1; + double ms_multiexp = bench_ms([&]() { + G1.multiMulByScalar(r1, bases, (uint8_t *)scalars, MP_N, NMExp); + }); + + G1Point r2; + double ms_msm = bench_ms([&]() { + G1.multiMulByScalarMSM(r2, bases, (uint8_t *)scalars, MP_N, NMExp); + }); + + ASSERT_FALSE(G1.isZero(r1)); + ASSERT_FALSE(G1.isZero(r2)); + + std::cout << std::fixed << std::setprecision(3); + std::cout << "[perf][nogmp] prepare bases/scalars : " << ms_prepare << " ms (N=" << NMExp << ")\n"; + std::cout << "[perf][nogmp] G1.multiMulByScalar : " << ms_multiexp << " ms (N=" << NMExp << ")\n"; + std::cout << "[perf][nogmp] G1.multiMulByScalarMSM: " << ms_msm << " ms (N=" << NMExp << ")\n"; + + delete[] bases; + delete[] scalars; +} + + using perf_clock2 = std::chrono::steady_clock; + +static inline double bench_ms2(std::function fn) { + auto t0 = perf_clock2::now(); + fn(); + auto t1 = perf_clock2::now(); + return std::chrono::duration(t1 - t0).count(); +} + +static inline double ns_per_op2(double ms, size_t n) { + return (ms * 1e6) / (double)n; +} + +TEST(altBn128Perf, affine_build_path_timing_nogmp) { + G1PointAffine p1_aff, p2_aff, out_aff; + G1.copy(p1_aff, G1.one()); + G1.add(p2_aff, p1_aff, G1.one()); // 2P as affine + + G1Point out_jac; + G1Point tmp_jac; + + const size_t N_ADD_AFFINE = 200000; + const size_t N_TO_AFFINE = 200000; + const size_t N_BUILD_STEP = 100000; + + // 1) pure affine+affine -> Jacobian + double ms_add_affine = bench_ms2([&]() { + for (size_t i = 0; i < N_ADD_AFFINE; i++) { + G1.add(out_jac, p1_aff, p2_aff); + } + }); + + // 2) pure Jacobian -> affine + G1.add(tmp_jac, p1_aff, p2_aff); + double ms_to_affine = bench_ms2([&]() { + for (size_t i = 0; i < N_TO_AFFINE; i++) { + G1.copy(out_aff, tmp_jac); + } + }); + + // 3) exact prepare-step shape: + // add(affine, affine) + copy(PointAffine <- Point) + G1PointAffine cur_aff; + G1.copy(cur_aff, p1_aff); + + double ms_build_step = bench_ms2([&]() { + for (size_t i = 0; i < N_BUILD_STEP; i++) { + G1.add(tmp_jac, cur_aff, p1_aff); + G1.copy(cur_aff, tmp_jac); + } + }); + + ASSERT_FALSE(G1.isZero(out_aff)); + ASSERT_FALSE(G1.isZero(cur_aff)); + + std::cout << std::fixed << std::setprecision(3); + std::cout << "[perf][nogmp] G1.add affine+affine -> jac : " + << ms_add_affine << " ms (N=" << N_ADD_AFFINE + << ") => " << ns_per_op2(ms_add_affine, N_ADD_AFFINE) << " ns/op\n"; + + std::cout << "[perf][nogmp] G1.copy jac -> affine : " + << ms_to_affine << " ms (N=" << N_TO_AFFINE + << ") => " << ns_per_op2(ms_to_affine, N_TO_AFFINE) << " ns/op\n"; + + std::cout << "[perf][nogmp] build step (add_aff + toAff): " + << ms_build_step << " ms (N=" << N_BUILD_STEP + << ") => " << ns_per_op2(ms_build_step, N_BUILD_STEP) << " ns/op\n"; +} + +TEST(altBn128Perf, prepare_breakdown_nogmp) { + const int NMExp = 40000; + + typedef mp_uint_t Scalar; + + Scalar *scalars = new Scalar[NMExp]; + G1PointAffine *bases = new G1PointAffine[NMExp]; + + double ms_scalars = bench_ms2([&]() { + for (int i = 0; i < NMExp; i++) { + mp_uint_t x; + mp_set(x, (uint64_t)(i + 1)); + mp_copy(scalars[i], x); + } + }); + + double ms_bases = bench_ms2([&]() { + for (int i = 0; i < NMExp; i++) { + if (i == 0) { + G1.copy(bases[0], G1.one()); + } else { + G1.add(bases[i], bases[i - 1], G1.one()); + } + } + }); + + ASSERT_FALSE(G1.isZero(bases[NMExp - 1])); + + std::cout << std::fixed << std::setprecision(3); + std::cout << "[perf][nogmp] prepare scalars only : " << ms_scalars << " ms (N=" << NMExp << ")\n"; + std::cout << "[perf][nogmp] prepare bases only : " << ms_bases << " ms (N=" << NMExp << ")\n"; + + delete[] bases; + delete[] scalars; +} +*/ } // namespace int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} +} \ No newline at end of file diff --git a/c/curve.cpp b/c/curve.cpp index af35ebf..81b9fa6 100644 --- a/c/curve.cpp +++ b/c/curve.cpp @@ -558,7 +558,7 @@ void Curve::copy(Point &r, PointAffine &a) { F.copy(r.zz, F.one()); F.copy(r.zzz, F.one()); } - +/* template void Curve::copy(PointAffine &r, Point &a) { #ifdef COUNT_OPS @@ -572,6 +572,103 @@ void Curve::copy(PointAffine &r, Point &a) { F.div(r.x, a.x, a.zz); F.div(r.y, a.y, a.zzz); } +*/ + +template +void Curve::batchToAffine(PointAffine *out, Point *in, size_t n) { + if (n == 0) return; + + typename BaseField::Element *prod = new typename BaseField::Element[n]; + typename BaseField::Element *prefix = new typename BaseField::Element[n]; + bool *is_nonzero = new bool[n]; + + typename BaseField::Element acc; + F.copy(acc, F.one()); + + for (size_t i = 0; i < n; i++) { + if (isZero(in[i])) { + is_nonzero[i] = false; + continue; + } + + is_nonzero[i] = true; + + // prod[i] = zz * zzz + F.mul(prod[i], in[i].zz, in[i].zzz); + + // prefix[i] = previous accumulated product + F.copy(prefix[i], acc); + + // acc *= prod[i] + F.mul(acc, acc, prod[i]); + } + + typename BaseField::Element acc_inv; + F.inv(acc_inv, acc); // single inversion for all points + + for (size_t k = n; k-- > 0;) { + if (!is_nonzero[k]) { + F.copy(out[k].x, F.zero()); + F.copy(out[k].y, F.zero()); + continue; + } + + typename BaseField::Element inv_prod; + typename BaseField::Element inv_zz; + typename BaseField::Element inv_zzz; + + // inv_prod = 1 / (zz * zzz) + F.mul(inv_prod, prefix[k], acc_inv); + + // acc_inv *= prod[k] for next iteration + F.mul(acc_inv, acc_inv, prod[k]); + + // inv_zz = zzz * inv_prod + // inv_zzz = zz * inv_prod + F.mul(inv_zz, in[k].zzz, inv_prod); + F.mul(inv_zzz, in[k].zz, inv_prod); + + F.mul(out[k].x, in[k].x, inv_zz); + F.mul(out[k].y, in[k].y, inv_zzz); + } + + delete[] prod; + delete[] prefix; + delete[] is_nonzero; +} + +template +void Curve::copy(PointAffine &r, Point &a) { +#ifdef COUNT_OPS + cntToAffine++; +#endif // COUNT_OPS + + if (isZero(a)) { + F.copy(r.x, F.zero()); + F.copy(r.y, F.zero()); + return; + } + + typename BaseField::Element t; + typename BaseField::Element t_inv; + typename BaseField::Element inv_zz; + typename BaseField::Element inv_zzz; + + // t = zz * zzz = Z^5 + F.mul(t, a.zz, a.zzz); + + // t_inv = 1 / (zz * zzz) + F.inv(t_inv, t); + + // inv_zz = 1 / zz + // inv_zzz = 1 / zzz + F.mul(inv_zz, a.zzz, t_inv); + F.mul(inv_zzz, a.zz, t_inv); + + // affine coordinates + F.mul(r.x, a.x, inv_zz); + F.mul(r.y, a.y, inv_zzz); +} template void Curve::copy(PointAffine &r, PointAffine &a) { @@ -594,7 +691,7 @@ void Curve::neg(Point &r, PointAffine &a) { F.copy(r.zz, F.one()); F.copy(r.zzz, F.one()); } - +/* template void Curve::neg(PointAffine &r, Point &a) { #ifdef COUNT_OPS @@ -609,6 +706,95 @@ void Curve::neg(PointAffine &r, Point &a) { F.div(r.y, a.y, a.zzz); F.neg(r.y, r.y); } +*/ +template +void Curve::batchNegToAffine(PointAffine *out, Point *in, size_t n) { + if (n == 0) return; + + typename BaseField::Element *prod = new typename BaseField::Element[n]; + typename BaseField::Element *prefix = new typename BaseField::Element[n]; + bool *is_nonzero = new bool[n]; + + typename BaseField::Element acc; + F.copy(acc, F.one()); + + for (size_t i = 0; i < n; i++) { + if (isZero(in[i])) { + is_nonzero[i] = false; + continue; + } + + is_nonzero[i] = true; + + F.mul(prod[i], in[i].zz, in[i].zzz); + F.copy(prefix[i], acc); + F.mul(acc, acc, prod[i]); + } + + typename BaseField::Element acc_inv; + F.inv(acc_inv, acc); // single inversion for all points + + for (size_t k = n; k-- > 0;) { + if (!is_nonzero[k]) { + F.copy(out[k].x, F.zero()); + F.copy(out[k].y, F.zero()); + continue; + } + + typename BaseField::Element inv_prod; + typename BaseField::Element inv_zz; + typename BaseField::Element inv_zzz; + typename BaseField::Element y_aff; + + F.mul(inv_prod, prefix[k], acc_inv); + F.mul(acc_inv, acc_inv, prod[k]); + + F.mul(inv_zz, in[k].zzz, inv_prod); + F.mul(inv_zzz, in[k].zz, inv_prod); + + F.mul(out[k].x, in[k].x, inv_zz); + F.mul(y_aff, in[k].y, inv_zzz); + F.neg(out[k].y, y_aff); + } + + delete[] prod; + delete[] prefix; + delete[] is_nonzero; +} + +template +void Curve::neg(PointAffine &r, Point &a) { +#ifdef COUNT_OPS + cntToAffine++; +#endif // COUNT_OPS + if (isZero(a)) { + F.copy(r.x, F.zero()); + F.copy(r.y, F.zero()); + return; + } + + typename BaseField::Element t; + typename BaseField::Element t_inv; + typename BaseField::Element inv_zz; + typename BaseField::Element inv_zzz; + typename BaseField::Element y_aff; + + // t = zz * zzz + F.mul(t, a.zz, a.zzz); + + // one inversion + F.inv(t_inv, t); + + // 1/zz and 1/zzz + F.mul(inv_zz, a.zzz, t_inv); + F.mul(inv_zzz, a.zz, t_inv); + + // x = X / zz + // y = -(Y / zzz) + F.mul(r.x, a.x, inv_zz); + F.mul(y_aff, a.y, inv_zzz); + F.neg(r.y, y_aff); +} template void Curve::neg(PointAffine &r, PointAffine &a) { diff --git a/c/curve.hpp b/c/curve.hpp index f6efb92..4bb6488 100644 --- a/c/curve.hpp +++ b/c/curve.hpp @@ -87,6 +87,7 @@ class Curve { void dbl(PointAffine &r, Point &a) { Point tmp; dbl(tmp, a); copy(r, tmp); } void dbl(PointAffine &r, PointAffine &a) { Point tmp; dbl(tmp, a); copy(r, tmp); } + void batchNegToAffine(PointAffine *out, Point *in, size_t n); void neg(Point &r, Point &a); void neg(PointAffine &r, PointAffine &a); @@ -103,6 +104,8 @@ class Curve { std::string toString(Point &r, uint32_t radix = 10); + void batchToAffine(PointAffine *out, Point *in, size_t n); + void copy(Point &r, Point &a); void copy(Point &r, PointAffine &a); void copy(PointAffine &r, Point &a); diff --git a/c/fft.cpp b/c/fft.cpp index 2a62c0a..f8d1c26 100644 --- a/c/fft.cpp +++ b/c/fft.cpp @@ -1,14 +1,11 @@ #include #include -#include -#include -#include - #include "misc.hpp" -#include "../../../build/mp.hpp" using namespace std; +// The function we want to execute on the new thread. + template u_int32_t FFT::log2(u_int64_t n) { assert(n!=0); @@ -26,7 +23,7 @@ static inline u_int64_t BR(u_int64_t x, u_int64_t domainPow) x = ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8); x = ((x & 0xF0F0F0F0) >> 4) | ((x & 0x0F0F0F0F) << 4); x = ((x & 0xCCCCCCCC) >> 2) | ((x & 0x33333333) << 2); - return (((x & 0xAAAAAAAA) >> 1) | ((x & 0x55555555) << 1)) >> (MP_N-domainPow); + return (((x & 0xAAAAAAAA) >> 1) | ((x & 0x55555555) << 1)) >> (32-domainPow); } #define ROOT(s,j) (rootsOfUnit[(1<<(s))+(j)]) @@ -37,86 +34,102 @@ FFT::FFT(u_int64_t maxDomainSize, uint32_t _nThreads) { f = Field::field; - static_assert(Field::N64 == 4, "FFT expects 256-bit field elements (4x64 limbs)."); - u_int32_t domainPow = log2(maxDomainSize); - Element qm1_norm; - f.fromMontgomery(qm1_norm, f.negOne()); - - mp_uint_t qm1; - std::memcpy(qm1, (const void*)qm1_norm.v, sizeof(qm1)); - - mp_uint_t qm1d2; - mp_shr(qm1d2, qm1, 1); - - Element cand, res; - uint64_t cand_ui = 2; - for (;;) { - f.fromUI(cand, cand_ui); - f.exp(res, cand, - reinterpret_cast(qm1d2), - (unsigned)sizeof(qm1d2)); - if (!f.eq(res, f.one())) { - f.copy(nqr, cand); - break; - } - cand_ui++; + typedef uint64_t mp_uint_t[Field::N64]; + + mp_uint_t m_qm1d2; + mp_uint_t m_q; + mp_uint_t m_nqr; + mp_uint_t m_aux; + + // mpz_init(...) + mp_set(m_qm1d2, 0); + mp_set(m_q, 0); + mp_set(m_nqr, 0); + mp_set(m_aux, 0); + + // f.toMpz(m_aux, f.negOne()); + // mpz_add_ui(m_q, m_aux, 1); + // mpz_fdiv_q_2exp(m_qm1d2, m_aux, 1); + f.toMP(m_aux, f.negOne()); // generator должен дать toMp/fromMp (аналог toMpz/fromMpz) + mp_add(m_q, m_aux, 1); + mp_shr(m_qm1d2, m_aux, 1); + + // mpz_set_ui(m_nqr, 2); + // mpz_powm(m_aux, m_nqr, m_qm1d2, m_q); + // while (mpz_cmp_ui(m_aux, 1) == 0) { ... } + mp_set(m_nqr, 2); + + mp_uint_t one; + mp_set(one, 1); + + mp_pow_mod(m_aux, m_nqr, m_qm1d2, m_q); + while (mp_cmp(m_aux, one) == 0) { + mp_add(m_nqr, m_nqr, 1); + mp_pow_mod(m_aux, m_nqr, m_qm1d2, m_q); } - mp_uint_t aux; - mp_copy(aux, qm1d2); - - u_int32_t s_tmp = 1; - while (s_tmp < domainPow) { - if (mp_tstbit(aux, 0)) break; - mp_shr(aux, aux, 1); - s_tmp++; + // f.fromMpz(nqr, m_nqr); + f.fromMP(nqr, m_nqr); + + // s = 1; + // mpz_set(m_aux, m_qm1d2); + // while ((!mpz_tstbit(m_aux, 0))&&(s 1) { - f.exp(roots[1], nqr, - reinterpret_cast(aux), - (unsigned)sizeof(aux)); - - Element two; - f.fromUI(two, 2); - f.inv(powTwoInv[1], two); + // mpz_powm(m_aux, m_nqr, m_aux, m_q); + // f.fromMpz(roots[1], m_aux); + mp_pow_mod(m_aux, m_nqr, m_aux, m_q); + f.fromMP(roots[1], m_aux); + + // mpz_set_ui(m_aux, 2); + // mpz_invert(m_aux, m_aux, m_q); + // f.fromMpz(powTwoInv[1], m_aux); + mp_set(m_aux, 2); + mp_inv_mod(m_aux, m_aux, m_q); + f.fromMP(powTwoInv[1], m_aux); } threadPool.parallelBlock([&] (uint64_t nThreads, uint64_t idThread) { - uint64_t increment = nRoots / nThreads; - uint64_t start = (idThread == 0) ? 2 : idThread * increment; - uint64_t end = (idThread == nThreads - 1) ? nRoots : (idThread + 1) * increment; + uint64_t increment = nRoots / nThreads; + uint64_t start = idThread==0 ? 2 : idThread * increment; + uint64_t end = idThread==nThreads-1 ? nRoots : (idThread+1) * increment; if (end > start) { f.exp(roots[start], roots[1], (uint8_t *)(&start), sizeof(start)); } - for (uint64_t i = start + 1; i < end; i++) { + for (uint64_t i=start+1; i @@ -125,15 +138,50 @@ FFT::~FFT() { delete[] powTwoInv; } +/* +template +void FFT::reversePermutationInnerLoop(Element *a, u_int64_t from, u_int64_t to, u_int32_t domainPow) { + Element tmp; + for (u_int64_t i=from; ir) { + f.copy(tmp, a[i]); + f.copy(a[i], a[r]); + f.copy(a[r], tmp); + } + } +} + + +template +void FFT::reversePermutation(Element *a, u_int64_t n) { + int domainPow = log2(n); + std::vector threads(nThreads-1); + u_int64_t increment = n / nThreads; + if (increment) { + for (u_int64_t i=0; i::reversePermutationInnerLoop, this, a, i*increment, (i+1)*increment, domainPow); + } + } + reversePermutationInnerLoop(a, (nThreads-1)*increment, n, domainPow); + if (increment) { + for (u_int32_t i=0; i void FFT::reversePermutation(Element *a, u_int64_t n) { int domainPow = log2(n); threadPool.parallelFor(0, n, [&] (int begin, int end, int numThread) { - for (u_int64_t i = (u_int64_t)begin; i < (u_int64_t)end; i++) { + for (u_int64_t i=begin; i r) { + if (i>r) { f.copy(tmp, a[i]); f.copy(a[i], a[r]); f.copy(a[r], tmp); @@ -142,56 +190,57 @@ void FFT::reversePermutation(Element *a, u_int64_t n) { }); } + template void FFT::fft(Element *a, u_int64_t n) { reversePermutation(a, n); - u_int64_t domainPow = log2(n); + u_int64_t domainPow =log2(n); assert(((u_int64_t)1 << domainPow) == n); - - for (u_int32_t s = 1; s <= domainPow; s++) { + for (u_int32_t s=1; s<=domainPow; s++) { u_int64_t m = 1 << s; u_int64_t mdiv2 = m >> 1; - threadPool.parallelFor(0, (n >> 1), [&] (int begin, int end, int numThread) { - for (u_int64_t i = (u_int64_t)begin; i < (u_int64_t)end; i++) { + threadPool.parallelFor(0, (n>>1), [&] (int begin, int end, int numThread) { + for (u_int64_t i=begin; i< end; i++) { Element t; Element u; - u_int64_t k = (i / mdiv2) * m; - u_int64_t j = i % mdiv2; + u_int64_t k=(i/mdiv2)*m; + u_int64_t j=i%mdiv2; - f.mul(t, root(s, j), a[k + j + mdiv2]); - f.copy(u, a[k + j]); - f.add(a[k + j], t, u); - f.sub(a[k + j + mdiv2], u, t); + f.mul(t, root(s, j), a[k+j+mdiv2]); + f.copy(u,a[k+j]); + f.add(a[k+j], t, u); + f.sub(a[k+j+mdiv2], u, t); } }); } } template -void FFT::ifft(Element *a, u_int64_t n) { +void FFT::ifft(Element *a, u_int64_t n ) { fft(a, n); - u_int64_t domainPow = log2(n); - u_int64_t nDiv2 = n >> 1; + u_int64_t domainPow =log2(n); + u_int64_t nDiv2= n >> 1; threadPool.parallelFor(1, nDiv2, [&] (int begin, int end, int numThread) { - for (u_int64_t i = (u_int64_t)begin; i < (u_int64_t)end; i++) { + for (u_int64_t i=begin; i> 1], a[n >> 1], powTwoInv[domainPow]); } + + template -void FFT::printVector(Element *a, u_int64_t n) { +void FFT::printVector(Element *a, u_int64_t n ) { cout << "[" << endl; - for (u_int64_t i = 0; i < n; i++) { + for (u_int64_t i=0; i class FFT { Field f; typedef typename Field::Element Element; + typedef uint64_t mp_uint_t[Field::N64]; u_int32_t s; Element nqr; diff --git a/src/armbuilder.js b/src/armbuilder.js index 57ad918..a981d7a 100644 --- a/src/armbuilder.js +++ b/src/armbuilder.js @@ -1,7 +1,7 @@ const bigInt = require("big-integer"); const assert = require("assert"); -module.exports.genFuncs = genFuncs +module.exports.genFuncs = genFuncs; class Reg { constructor(number) { @@ -211,7 +211,7 @@ class GenBase { if (size % 2) { this.op("ldr", saved[size-1], "[sp], #16"); - size--; + size--; } for (let i =size; i > 0; i -= 2) { @@ -336,7 +336,7 @@ class GenBase { class Gen_rawIsZero extends GenBase { constructor(width, space) { - super(width, space, "rawIsZero", 1, 16) + super(width, space, "rawIsZero", 1, 16); this.generate(); } @@ -363,7 +363,7 @@ class Gen_rawIsZero extends GenBase { } genChunk(width, i) { - let r = this.genStep(width, i * 4); + let r = this.genStep(width, i * 4); if (this.accum === undefined) { this.accum = r; @@ -466,7 +466,7 @@ class Gen_rawIsEq extends GenBase { this.op_empty(); } - genStep(width, i) { + genStep(width, i) { assert(width > 0); if (width === 1) { @@ -906,9 +906,9 @@ class Gen_rawNeg extends GenBase { this.genSubtraction(); if (this.hasSavedRegs()) - this.op("b", this.makeLabel("out")); + this.op("b", this.makeLabel("out")); else - this.op("ret"); + this.op("ret"); this.op_empty(); this.op_label(doneLabel); @@ -1010,7 +1010,7 @@ class Gen_rawNegLS extends GenBase { } if (this.width % 2) this.op_empty(); - this.op("cset", "x2", "cs"); + this.op("cset", "x2", "cc"); //sub fix } genSubtractionOpA() { @@ -1021,7 +1021,7 @@ class Gen_rawNegLS extends GenBase { } if (this.width % 2) this.op_empty(); - this.op("cset", "x3", "cs"); + this.op("cset", "x3", "cc"); //sub fix this.op("orr", "x3", "x3", "x2"); this.op_empty(); this.op("cbz", "x3", this.makeLabel("done")); @@ -1081,9 +1081,11 @@ class Gen_rawShr extends GenBase { genCalcJump() { if (this.width > 2) { this.op("lsr", "x2", "x2", "#6"); - this.op("adr", "x5", this.makeLabel("word_shift")); - this.op("ldr", "x5", "[x5, x2, lsl 3]"); - this.op("br", "x5"); + for (let i = 0; i < this.width - 1; i++) { + this.op("cmp", "x2", "#" + i); + this.op("b.eq", this.makeWordLabel(i)); + } + this.op("b", this.makeWordLabel(this.width - 1)); } else { this.op("tbnz", "x2", "6", this.makeLabel("word_shift_1")); @@ -1092,11 +1094,6 @@ class Gen_rawShr extends GenBase { } genJumpTable() { - this.op_label(this.makeLabel("word_shift")); - - for (let i = 0; i < this.width; i++) { - this.op(".quad", this.makeWordLabel(i)); - } } genShift() { @@ -1122,7 +1119,7 @@ class Gen_rawShr extends GenBase { if (!this.hasSavedRegs()) this.op("ret"); - this.op_empty(); + this.op_empty(); } genBitShift(wordShift) { @@ -1215,9 +1212,11 @@ class Gen_rawShl extends GenBase { genCalcJump() { if (this.width > 2) { this.op("lsr", "x2", "x2", "#6"); - this.op("adr", "x5", this.makeLabel("word_shift")); - this.op("ldr", "x5", "[x5, x2, lsl 3]"); - this.op("br", "x5"); + for (let i = 0; i < this.width - 1; i++) { + this.op("cmp", "x2", "#" + i); + this.op("b.eq", this.makeWordLabel(i)); + } + this.op("b", this.makeWordLabel(this.width - 1)); } else { this.op("tbnz", "x2", "6", this.makeLabel("word_shift_1")); @@ -1226,11 +1225,6 @@ class Gen_rawShl extends GenBase { } genJumpTable() { - this.op_label(this.makeLabel("word_shift")); - - for (let i = 0; i < this.width; i++) { - this.op(".quad", this.makeWordLabel(i)); - } } genShift() { @@ -1415,11 +1409,11 @@ class Gen_rawMul extends GenBase { let t1 = cyclicCopy(new RegVar(7, 5), this.width); for(i = 0; i < this.width; i++) { - let iAdd = (i === this.width - 1) ? "adc" : "adcs"; + let iAdd = (i === this.width - 1) ? "adc" : "adcs"; - if (!this.canOptimizeConsensys) { + if (!this.canOptimizeConsensys) { iAdd = (i) ? "adcs" : "adds"; - } + } if (!this.isShort) this.genLoadVarWord(this.b1, i, 2); this.op("umulh", t1[i], this.b1[i], ra); diff --git a/src/buildzqfield.js b/src/buildzqfield.js index 6fbc2e0..3cb3b47 100755 --- a/src/buildzqfield.js +++ b/src/buildzqfield.js @@ -54,7 +54,7 @@ async function buildField(q, name) { const raw_generic_cpp = await renderFile(path.join(__dirname, "fr_raw_generic.cpp.ejs"), builder); const raw_arm64_s = await renderFile(path.join(__dirname, "fr_raw_arm64.s.ejs"), builder); - return {asm: asm, hpp: hpp, cpp: cpp, element_hpp: element_hpp, generic_cpp: generic_cpp, raw_generic_cpp: raw_generic_cpp, raw_arm64_s: raw_arm64_s}; + return {n64: builder.n64, asm: asm, hpp: hpp, cpp: cpp, element_hpp: element_hpp, generic_cpp: generic_cpp, raw_generic_cpp: raw_generic_cpp, raw_arm64_s: raw_arm64_s}; } if (runningAsScript) { diff --git a/src/fr.cpp.ejs b/src/fr.cpp.ejs index 9d3f1e2..b739126 100644 --- a/src/fr.cpp.ejs +++ b/src/fr.cpp.ejs @@ -1,159 +1,85 @@ #include "<%=name.toLowerCase()+".hpp"%>" -#include -#include -#include -#include +#include "mp.hpp" +#include #include +#include +#include - -static mpz_t q; -static mpz_t zero; -static mpz_t one; -static mpz_t mask; -static size_t nBits; -static bool initialized = false; - - -void <%=name%>_toMpz(mpz_t r, P<%=name%>Element pE) { +void <%=name%>_toMP(mp_uint_t out, P<%=name%>Element pE) { <%=name%>Element tmp; <%=name%>_toNormal(&tmp, pE); if (!(tmp.type & <%=name%>_LONG)) { - mpz_set_si(r, tmp.shortVal); - if (tmp.shortVal<0) { - mpz_add(r, r, q); - } + mp_set_mod(out, tmp.shortVal, <%=name%>_q.longVal); } else { - mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)tmp.longVal); + mp_copy(out, tmp.longVal); } } -void <%=name%>_fromMpz(P<%=name%>Element pE, mpz_t v) { - if (mpz_fits_sint_p(v)) { +void <%=name%>_fromMP(P<%=name%>Element pE, const mp_uint_t v) { + if (mp_fits_int32(v)) { pE->type = <%=name%>_SHORT; - pE->shortVal = mpz_get_si(v); + pE->shortVal = mp_get_int32(v); } else { pE->type = <%=name%>_LONG; - for (int i=0; i<<%=name%>_N64; i++) pE->longVal[i] = 0; - mpz_export((void *)(pE->longVal), NULL, -1, 8, -1, 0, v); + mp_copy(pE->longVal, v); } } - -bool <%=name%>_init() { - if (initialized) return false; - initialized = true; - mpz_init(q); - mpz_import(q, <%=name%>_N64, -1, 8, -1, 0, (const void *)<%=name%>_q.longVal); - mpz_init_set_ui(zero, 0); - mpz_init_set_ui(one, 1); - nBits = mpz_sizeinbase (q, 2); - mpz_init(mask); - mpz_mul_2exp(mask, one, nBits); - mpz_sub(mask, mask, one); - return true; -} - -void <%=name%>_str2element(P<%=name%>Element pE, char const *s) { - mpz_t mr; - mpz_init_set_str(mr, s, 10); - mpz_fdiv_r(mr, mr, q); - <%=name%>_fromMpz(pE, mr); - mpz_clear(mr); +void <%=name%>_str2element(P<%=name%>Element pE, char const* s, int base) { + mp_uint_t v; + if (!mp_set_mod(v, s, base, <%=name%>_q.longVal)) { + mp_set(v, 0); + } + <%=name%>_fromMP(pE, v); } -char *<%=name%>_element2str(P<%=name%>Element pE) { - <%=name%>Element tmp; - mpz_t r; - if (!(pE->type & <%=name%>_LONG)) { - if (pE->shortVal>=0) { - char *r = new char[32]; - sprintf(r, "%d", pE->shortVal); - return r; - } else { - mpz_init_set_si(r, pE->shortVal); - mpz_add(r, r, q); - } - } else { - <%=name%>_toNormal(&tmp, pE); - mpz_init(r); - mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)tmp.longVal); - } - char *res = mpz_get_str (0, 10, r); - mpz_clear(r); - return res; +std::string <%=name%>_element2str(P<%=name%>Element pE, uint32_t base) { + mp_uint_t v; + <%=name%>_toMP(v, pE); + return mp_get_str(v, base); } void <%=name%>_idiv(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - mpz_t ma; - mpz_t mb; - mpz_t mr; - mpz_init(ma); - mpz_init(mb); - mpz_init(mr); - - <%=name%>_toMpz(ma, a); - // char *s1 = mpz_get_str (0, 10, ma); - // printf("s1 %s\n", s1); - <%=name%>_toMpz(mb, b); - // char *s2 = mpz_get_str (0, 10, mb); - // printf("s2 %s\n", s2); - mpz_fdiv_q(mr, ma, mb); - // char *sr = mpz_get_str (0, 10, mr); - // printf("r %s\n", sr); - <%=name%>_fromMpz(r, mr); - - mpz_clear(ma); - mpz_clear(mb); - mpz_clear(mr); + mp_uint_t ma, mb; + <%=name%>_toMP(ma, a); + <%=name%>_toMP(mb, b); + + mp_uint_t q, rem; + mp_div(q, rem, ma, mb); + <%=name%>_fromMP(r, q); } void <%=name%>_mod(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - mpz_t ma; - mpz_t mb; - mpz_t mr; - mpz_init(ma); - mpz_init(mb); - mpz_init(mr); - - <%=name%>_toMpz(ma, a); - <%=name%>_toMpz(mb, b); - mpz_fdiv_r(mr, ma, mb); - <%=name%>_fromMpz(r, mr); - - mpz_clear(ma); - mpz_clear(mb); - mpz_clear(mr); + mp_uint_t ma, mb; + <%=name%>_toMP(ma, a); + <%=name%>_toMP(mb, b); + + mp_uint_t q, rem; + mp_div(q, rem, ma, mb); + <%=name%>_fromMP(r, rem); } void <%=name%>_pow(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - mpz_t ma; - mpz_t mb; - mpz_t mr; - mpz_init(ma); - mpz_init(mb); - mpz_init(mr); - - <%=name%>_toMpz(ma, a); - <%=name%>_toMpz(mb, b); - mpz_powm(mr, ma, mb, q); - <%=name%>_fromMpz(r, mr); - - mpz_clear(ma); - mpz_clear(mb); - mpz_clear(mr); + mp_uint_t mb; + <%=name%>_toMP(mb, b); + + mp_uint_t base; + <%=name%>_toMP(base, a); + + mp_uint_t res; + mp_pow_mod(res, base, mb, <%=name%>_q.longVal); + + <%=name%>_fromMP(r, res); } void <%=name%>_inv(P<%=name%>Element r, P<%=name%>Element a) { - mpz_t ma; - mpz_t mr; - mpz_init(ma); - mpz_init(mr); - - <%=name%>_toMpz(ma, a); - mpz_invert(mr, ma, q); - <%=name%>_fromMpz(r, mr); - mpz_clear(ma); - mpz_clear(mr); + mp_uint_t base; + <%=name%>_toMP(base, a); + + mp_uint_t res; + mp_inv_mod(res, base, <%=name%>_q.longVal); + + <%=name%>_fromMP(r, res); } void <%=name%>_div(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { @@ -163,7 +89,7 @@ void <%=name%>_div(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b } void <%=name%>_fail() { - assert(false); + throw std::runtime_error("<%=name%> error"); } void <%=name%>_longErr() { @@ -171,104 +97,70 @@ void <%=name%>_longErr() { } Raw<%=name%>::Raw<%=name%>() { - <%=name%>_init(); set(fZero, 0); set(fOne, 1); neg(fNegOne, fOne); } -Raw<%=name%>::~Raw<%=name%>() { -} +Raw<%=name%>::~Raw<%=name%>() {} -void Raw<%=name%>::fromString(Element &r, const std::string &s, uint32_t radix) { - mpz_t mr; - mpz_init_set_str(mr, s.c_str(), radix); - mpz_fdiv_r(mr, mr, q); - for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0; - mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr); - <%=name%>_rawToMontgomery(r.v,r.v); - mpz_clear(mr); +void Raw<%=name%>::fromString(Element& r, const std::string& s, uint32_t radix) { + if (!mp_set_mod(r.v, s.c_str(), radix, <%=name%>_q.longVal)) { + mp_set(r.v, 0); + } + <%=name%>_rawToMontgomery(r.v, r.v); } -void Raw<%=name%>::fromUI(Element &r, unsigned long int v) { - mpz_t mr; - mpz_init(mr); - mpz_set_ui(mr, v); - for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0; - mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr); - <%=name%>_rawToMontgomery(r.v,r.v); - mpz_clear(mr); +void Raw<%=name%>::fromUI(Element& r, unsigned long int v) { + mp_set(r.v, v); + <%=name%>_rawToMontgomery(r.v, r.v); } Raw<%=name%>::Element Raw<%=name%>::set(int value) { - Element r; - set(r, value); - return r; + Element r; + set(r, value); + return r; } -void Raw<%=name%>::set(Element &r, int value) { - mpz_t mr; - mpz_init(mr); - mpz_set_si(mr, value); - if (value < 0) { - mpz_add(mr, mr, q); - } - - mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr); - - for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0; - mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr); - <%=name%>_rawToMontgomery(r.v,r.v); - mpz_clear(mr); +void Raw<%=name%>::set(Element& r, int value) { + mp_set_mod(r.v, value, <%=name%>_q.longVal); + <%=name%>_rawToMontgomery(r.v, r.v); } -std::string Raw<%=name%>::toString(const Element &a, uint32_t radix) { +std::string Raw<%=name%>::toString(const Element& a, uint32_t radix) { Element tmp; - mpz_t r; <%=name%>_rawFromMontgomery(tmp.v, a.v); - mpz_init(r); - mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)(tmp.v)); - char *res = mpz_get_str (0, radix, r); - mpz_clear(r); - std::string resS(res); - free(res); - return resS; + return mp_get_str(tmp.v, radix); } -void Raw<%=name%>::inv(Element &r, const Element &a) { - mpz_t mr; - mpz_init(mr); - mpz_import(mr, <%=name%>_N64, -1, 8, -1, 0, (const void *)(a.v)); - mpz_invert(mr, mr, q); - - - for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0; - mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, mr); - - <%=name%>_rawMMul(r.v, r.v,<%=name%>_R3.longVal); - mpz_clear(mr); +void Raw<%=name%>::inv(Element& r, const Element& a) { + Element t; + <%=name%>_rawFromMontgomery(t.v, a.v); + mp_inv_mod(r.v, t.v, <%=name%>_q.longVal); + <%=name%>_rawMMul(r.v, r.v, <%=name%>_R2.longVal); } -void Raw<%=name%>::div(Element &r, const Element &a, const Element &b) { +void Raw<%=name%>::div(Element& r, const Element& a, const Element& b) { Element tmp; inv(tmp, b); mul(r, a, tmp); } -#define BIT_IS_SET(s, p) (s[p>>3] & (1 << (p & 0x7))) -void Raw<%=name%>::exp(Element &r, const Element &base, uint8_t* scalar, unsigned int scalarSize) { +#define BIT_IS_SET(s, p) (s[(p)>>3] & (1 << ((p) & 0x7))) +void Raw<%=name%>::exp(Element& r, const Element& base, uint8_t* scalar, unsigned int scalarSize) { bool oneFound = false; Element copyBase; copy(copyBase, base); - for (int i=scalarSize*8-1; i>=0; i--) { + + for (int i = (int)scalarSize * 8 - 1; i >= 0; i--) { if (!oneFound) { - if ( !BIT_IS_SET(scalar, i) ) continue; + if (!BIT_IS_SET(scalar, i)) continue; copy(r, copyBase); oneFound = true; continue; } square(r, r); - if ( BIT_IS_SET(scalar, i) ) { + if (BIT_IS_SET(scalar, i)) { mul(r, r, copyBase); } } @@ -277,48 +169,37 @@ void Raw<%=name%>::exp(Element &r, const Element &base, uint8_t* scalar, unsigne } } -void Raw<%=name%>::toMpz(mpz_t r, const Element &a) { - Element tmp; - <%=name%>_rawFromMontgomery(tmp.v, a.v); - mpz_import(r, <%=name%>_N64, -1, 8, -1, 0, (const void *)tmp.v); +void Raw<%=name%>::toMP(mp_uint_t r, const Element &a) { + <%=name%>RawElement tmp; + <%=name%>_rawFromMontgomery(tmp, a.v); + mp_copy(r, tmp); } -void Raw<%=name%>::fromMpz(Element &r, const mpz_t a) { - for (int i=0; i<<%=name%>_N64; i++) r.v[i] = 0; - mpz_export((void *)(r.v), NULL, -1, 8, -1, 0, a); - <%=name%>_rawToMontgomery(r.v, r.v); +void Raw<%=name%>::fromMP(Element &a, const mp_uint_t r) { + mp_copy(a.v, r); + <%=name%>_rawToMontgomery(a.v, a.v); } -int Raw<%=name%>::toRprBE(const Element &element, uint8_t *data, int bytes) -{ - if (bytes < <%=name%>_N64 * 8) { - return -(<%=name%>_N64 * 8); - } - - mpz_t r; - mpz_init(r); +int Raw<%=name%>::toRprBE(const Element& element, uint8_t* data, int bytes) { + const int need = <%=name%>_N64 * 8; + if (bytes < need) return -need; - toMpz(r, element); - - mpz_export(data, NULL, 1, bytes, 1, 0, r); + mp_uint_t v; + toMP(v, element); + mp_export_be(data, v); - return <%=name%>_N64 * 8; + return need; } -int Raw<%=name%>::fromRprBE(Element &element, const uint8_t *data, int bytes) -{ - if (bytes < <%=name%>_N64 * 8) { - return -(<%=name%>_N64* 8); - } - mpz_t r; - mpz_init(r); - - mpz_import(r, <%=name%>_N64 * 8, 0, 1, 0, 0, data); - fromMpz(element, r); - return <%=name%>_N64 * 8; -} +int Raw<%=name%>::fromRprBE(Element& element, const uint8_t* data, int bytes) { + const int need = <%=name%>_N64 * 8; + if (bytes < need) return -need; -static bool init = <%=name%>_init(); + mp_uint_t v; + mp_import_be(v, data); + fromMP(element, v); -Raw<%=name%> Raw<%=name%>::field; + return need; +} +Raw<%=name%> Raw<%=name%>::field; \ No newline at end of file diff --git a/src/fr.hpp.ejs b/src/fr.hpp.ejs index 1bbe388..27cbca3 100644 --- a/src/fr.hpp.ejs +++ b/src/fr.hpp.ejs @@ -4,7 +4,11 @@ #include "<%=name.toLowerCase()%>_element.hpp" #include #include -#include +#include "mp.hpp" + +#ifdef __APPLE__ +#include // typedef unsigned int uint; +#endif // __APPLE__ extern <%=name%>Element <%=name%>_q; extern <%=name%>Element <%=name%>_R2; @@ -51,10 +55,10 @@ extern "C" void <%=name%>_rawNeg(<%=name%>RawElement pRawResult, const <%=name%> extern "C" void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB); extern "C" void <%=name%>_rawMSquare(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); extern "C" void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, uint64_t pRawB); -extern "C" void <%=name%>_rawToMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); -extern "C" void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); -extern "C" int <%=name%>_rawIsEq(<%=name%>RawElement const pRawA, const <%=name%>RawElement pRawB); -extern "C" int <%=name%>_rawIsZero(const <%=name%>RawElement pRawB); +extern "C" void <%=name%>_rawToMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA); +extern "C" void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA); +extern "C" int <%=name%>_rawIsEq(const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB); +extern "C" int <%=name%>_rawIsZero(const <%=name%>RawElement pRawB); extern "C" void <%=name%>_rawShl(<%=name%>RawElement r, <%=name%>RawElement a, uint64_t b); extern "C" void <%=name%>_rawShr(<%=name%>RawElement r, <%=name%>RawElement a, uint64_t b); @@ -99,8 +103,8 @@ extern "C" void <%=name%>_rawNeg(<%=name%>RawElement pRawResult, const <%=name%> extern "C" void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB); void <%=name%>_rawMSquare(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); extern "C" void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, uint64_t pRawB); - void <%=name%>_rawToMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); -extern "C" void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); + void <%=name%>_rawToMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA); +extern "C" void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA); extern "C" int <%=name%>_rawIsEq(const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB); extern "C" int <%=name%>_rawIsZero(const <%=name%>RawElement pRawB); void <%=name%>_rawZero(<%=name%>RawElement pRawResult); @@ -118,8 +122,8 @@ extern "C" void <%=name%>_rawShr(<%=name%>RawElement r, <%=name%>RawElement a, u extern "C" void <%=name%>_rawNot(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA); extern "C" void <%=name%>_rawSubRegular(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, <%=name%>RawElement pRawB); - void <%=name%>_fail(); - void <%=name%>_longErr(); +void <%=name%>_fail(); +void <%=name%>_longErr(); #endif @@ -163,8 +167,8 @@ void <%=name%>_rawNeg(<%=name%>RawElement pRawResult, const <%=name%>RawElement void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB); void <%=name%>_rawMSquare(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, uint64_t pRawB); -void <%=name%>_rawToMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); -void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA); +void <%=name%>_rawToMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA); +void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA); int <%=name%>_rawIsEq(const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB); int <%=name%>_rawIsZero(const <%=name%>RawElement pRawB); void <%=name%>_rawZero(<%=name%>RawElement pRawResult); @@ -187,10 +191,11 @@ void <%=name%>_longErr(); #endif -// Pending functions to convert -void <%=name%>_str2element(P<%=name%>Element pE, char const*s); -char *<%=name%>_element2str(P<%=name%>Element pE); +void <%=name%>_toMP(mp_uint_t out, P<%=name%>Element a); +void <%=name%>_fromMP(P<%=name%>Element out, const mp_uint_t v); +void <%=name%>_str2element(P<%=name%>Element pE, char const* s, int base); +std::string <%=name%>_element2str(P<%=name%>Element pE, uint32_t base = 10); void <%=name%>_idiv(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b); void <%=name%>_mod(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b); void <%=name%>_inv(P<%=name%>Element r, P<%=name%>Element a); @@ -203,7 +208,6 @@ public: const static int N64 = <%=name%>_N64; const static int MaxBits = <%= q.bitLength() %>; - struct Element { <%=name%>RawElement v; }; @@ -214,65 +218,65 @@ private: Element fNegOne; public: - Raw<%=name%>(); ~Raw<%=name%>(); - const Element &zero() { return fZero; } - const Element &one() { return fOne; } - const Element &negOne() { return fNegOne; } + const Element &zero() { return fZero; }; + const Element &one() { return fOne; }; + const Element &negOne() { return fNegOne; }; Element set(int value); void set(Element &r, int value); void fromString(Element &r, const std::string &n, uint32_t radix = 10); std::string toString(const Element &a, uint32_t radix = 10); - void inline copy(Element &r, const Element &a) { <%=name%>_rawCopy(r.v, a.v); } - void inline swap(Element &a, Element &b) { <%=name%>_rawSwap(a.v, b.v); } - void inline add(Element &r, const Element &a, const Element &b) { <%=name%>_rawAdd(r.v, a.v, b.v); } - void inline sub(Element &r, const Element &a, const Element &b) { <%=name%>_rawSub(r.v, a.v, b.v); } - void inline mul(Element &r, const Element &a, const Element &b) { <%=name%>_rawMMul(r.v, a.v, b.v); } + void inline copy(Element &r, const Element &a) { <%=name%>_rawCopy(r.v, a.v); }; + void inline swap(Element &a, Element &b) { <%=name%>_rawSwap(a.v, b.v); }; + void inline add(Element &r, const Element &a, const Element &b) { <%=name%>_rawAdd(r.v, a.v, b.v); }; + void inline sub(Element &r, const Element &a, const Element &b) { <%=name%>_rawSub(r.v, a.v, b.v); }; + void inline mul(Element &r, const Element &a, const Element &b) { <%=name%>_rawMMul(r.v, a.v, b.v); }; - Element inline add(const Element &a, const Element &b) { Element r; <%=name%>_rawAdd(r.v, a.v, b.v); return r;} - Element inline sub(const Element &a, const Element &b) { Element r; <%=name%>_rawSub(r.v, a.v, b.v); return r;} - Element inline mul(const Element &a, const Element &b) { Element r; <%=name%>_rawMMul(r.v, a.v, b.v); return r;} + Element inline add(const Element &a, const Element &b) { Element r; <%=name%>_rawAdd(r.v, a.v, b.v); return r;}; + Element inline sub(const Element &a, const Element &b) { Element r; <%=name%>_rawSub(r.v, a.v, b.v); return r;}; + Element inline mul(const Element &a, const Element &b) { Element r; <%=name%>_rawMMul(r.v, a.v, b.v); return r;}; - Element inline neg(const Element &a) { Element r; <%=name%>_rawNeg(r.v, a.v); return r; } - Element inline square(const Element &a) { Element r; <%=name%>_rawMSquare(r.v, a.v); return r; } + Element inline neg(const Element &a) { Element r; <%=name%>_rawNeg(r.v, a.v); return r; }; + Element inline square(const Element &a) { Element r; <%=name%>_rawMSquare(r.v, a.v); return r; }; - Element inline add(int a, const Element &b) { return add(set(a), b);} - Element inline sub(int a, const Element &b) { return sub(set(a), b);} - Element inline mul(int a, const Element &b) { return mul(set(a), b);} + Element inline add(int a, const Element &b) { return add(set(a), b);}; + Element inline sub(int a, const Element &b) { return sub(set(a), b);}; + Element inline mul(int a, const Element &b) { return mul(set(a), b);}; - Element inline add(const Element &a, int b) { return add(a, set(b));} - Element inline sub(const Element &a, int b) { return sub(a, set(b));} - Element inline mul(const Element &a, int b) { return mul(a, set(b));} + Element inline add(const Element &a, int b) { return add(a, set(b));}; + Element inline sub(const Element &a, int b) { return sub(a, set(b));}; + Element inline mul(const Element &a, int b) { return mul(a, set(b));}; - void inline mul1(Element &r, const Element &a, uint64_t b) { <%=name%>_rawMMul1(r.v, a.v, b); } - void inline neg(Element &r, const Element &a) { <%=name%>_rawNeg(r.v, a.v); } - void inline square(Element &r, const Element &a) { <%=name%>_rawMSquare(r.v, a.v); } + void inline mul1(Element &r, const Element &a, uint64_t b) { <%=name%>_rawMMul1(r.v, a.v, b); }; + void inline neg(Element &r, const Element &a) { <%=name%>_rawNeg(r.v, a.v); }; + void inline square(Element &r, const Element &a) { <%=name%>_rawMSquare(r.v, a.v); }; void inv(Element &r, const Element &a); void div(Element &r, const Element &a, const Element &b); void exp(Element &r, const Element &base, uint8_t* scalar, unsigned int scalarSize); - void inline toMontgomery(Element &r, const Element &a) { <%=name%>_rawToMontgomery(r.v, a.v); } - void inline fromMontgomery(Element &r, const Element &a) { <%=name%>_rawFromMontgomery(r.v, a.v); } - int inline eq(const Element &a, const Element &b) { return <%=name%>_rawIsEq(a.v, b.v); } - int inline isZero(const Element &a) { return <%=name%>_rawIsZero(a.v); } + void inline toMontgomery(Element &r, const Element &a) { <%=name%>_rawToMontgomery(r.v, a.v); }; + void inline fromMontgomery(Element &r, const Element &a) { <%=name%>_rawFromMontgomery(r.v, a.v); }; + int inline eq(const Element &a, const Element &b) { return <%=name%>_rawIsEq(a.v, b.v); }; + int inline isZero(const Element &a) { return <%=name%>_rawIsZero(a.v); }; - void toMpz(mpz_t r, const Element &a); - void fromMpz(Element &a, const mpz_t r); + void toMP(mp_uint_t r, const Element &a); + void fromMP(Element &a, const mp_uint_t r); int toRprBE(const Element &element, uint8_t *data, int bytes); int fromRprBE(Element &element, const uint8_t *data, int bytes); - int bytes ( void ) { return <%=name%>_N64 * 8; } - + int bytes ( void ) { return <%=name%>_N64 * 8; }; void fromUI(Element &r, unsigned long int v); static Raw<%=name%> field; - }; #endif // __<%=name.toUpperCase()%>_H + + + diff --git a/src/fr_generic.cpp.ejs b/src/fr_generic.cpp.ejs index 2cd5a6d..ccd5668 100644 --- a/src/fr_generic.cpp.ejs +++ b/src/fr_generic.cpp.ejs @@ -1,15 +1,23 @@ #include "<%=name.toLowerCase()+".hpp"%>" #include #include -#include <%=name%>Element <%=name%>_q = {0, 0x80000000, {<%= constantElement(q) %>}}; <%=name%>Element <%=name%>_R2 = {0, 0x80000000, {<%= constantElement(bigInt.one.shiftLeft(n64*64*2).mod(q)) %>}}; <%=name%>Element <%=name%>_R3 = {0, 0x80000000, {<%= constantElement(bigInt.one.shiftLeft(n64*64*3).mod(q)) %>}}; static <%=name%>RawElement half = {<%= constantElement(q.shiftRight(1)) %>}; -static <%=name%>RawElement zero = {0}; +static <%=name%>RawElement <%=name%>_rawR2 = {<%= constantElement(bigInt.one.shiftLeft(n64*64*2).mod(q)) %>}; +void <%=name%>_rawMSquare(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA) +{ + <%=name%>_rawMMul(pRawResult, pRawA, pRawA); +} + +void <%=name%>_rawToMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA) +{ + <%=name%>_rawMMul(pRawResult, pRawA, <%=name%>_rawR2); +} void <%=name%>_copy(P<%=name%>Element r, const P<%=name%>Element a) { @@ -271,6 +279,7 @@ void <%=name%>_mul(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b } } + void <%=name%>_toLongNormal(P<%=name%>Element r, P<%=name%>Element a) { if (a->type & <%=name%>_LONG) @@ -309,7 +318,7 @@ void <%=name%>_toMontgomery(P<%=name%>Element r, P<%=name%>Element a) } else if (a->shortVal < 0) { - int64_t a_shortVal = a->shortVal; + int64_t a_shortVal = a->shortVal; <%=name%>_rawMMul1(r->longVal, <%=name%>_R2.longVal, -a_shortVal); <%=name%>_rawNeg(r->longVal, r->longVal); @@ -328,6 +337,7 @@ void <%=name%>_copyn(P<%=name%>Element r, P<%=name%>Element a, int n) std::memcpy(r, a, n * sizeof(<%=name%>Element)); } + static inline int has_add32_overflow(int64_t val) { int64_t signs = (val >> 31) & 0x3; @@ -769,7 +779,7 @@ int <%=name%>_longNormal(P<%=name%>Element pE) return <%=name%>_longNeg(pE); } - if (memcmp(&pE->longVal[1], zero, (sizeof(pE->longVal) - sizeof(pE->longVal[0])))) + if (pE->longVal[1] || pE->longVal[2] || pE->longVal[3]) { return <%=name%>_longNeg(pE); } @@ -1499,7 +1509,7 @@ static inline void and_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal}}; + a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; } else { @@ -1520,7 +1530,7 @@ static inline void and_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal}}; + b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; } else { @@ -1541,7 +1551,7 @@ static inline void and_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal}}; + a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; } else { @@ -1559,7 +1569,7 @@ static inline void and_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal}}; + b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; } else { @@ -1791,7 +1801,7 @@ static inline void do_shr(P<%=name%>Element r, P<%=name%>Element a, uint64_t b) static inline void <%=name%>_shr_big_shift(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - static <%=name%>RawElement max_shift = {<%= q.bitLength() %>}; + static <%=name%>RawElement max_shift = {<%= q.bitLength() %>, 0, 0, 0}; <%=name%>RawElement shift; @@ -1809,7 +1819,7 @@ static inline void <%=name%>_shr_big_shift(P<%=name%>Element r, P<%=name%>Elemen static inline void <%=name%>_shr_long(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - static <%=name%>RawElement max_shift = {<%= q.bitLength() %>}; + static <%=name%>RawElement max_shift = {<%= q.bitLength() %>, 0, 0, 0}; if (<%=name%>_rawCmp(b->longVal, max_shift) >= 0) { @@ -1867,7 +1877,7 @@ void <%=name%>_shr(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b static inline void <%=name%>_shl_big_shift(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - static <%=name%>RawElement max_shift = {<%= q.bitLength() %>}; + static <%=name%>RawElement max_shift = {<%= q.bitLength() %>, 0, 0, 0}; <%=name%>RawElement shift; @@ -1885,7 +1895,7 @@ static inline void <%=name%>_shl_big_shift(P<%=name%>Element r, P<%=name%>Elemen static inline void <%=name%>_shl_long(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - static <%=name%>RawElement max_shift = {<%= q.bitLength() %>}; + static <%=name%>RawElement max_shift = {<%= q.bitLength() %>, 0, 0, 0}; if (<%=name%>_rawCmp(b->longVal, max_shift) >= 0) { @@ -2014,7 +2024,7 @@ static inline void or_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%> if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal}}; + a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; } else { @@ -2032,7 +2042,7 @@ static inline void or_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%> if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal}}; + a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; } else { @@ -2050,7 +2060,7 @@ static inline void or_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%> if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal}}; + b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; } else { @@ -2071,7 +2081,7 @@ static inline void or_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%> if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal}}; + b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; } else { @@ -2201,7 +2211,7 @@ static inline void xor_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal}}; + a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; } else { @@ -2222,7 +2232,7 @@ static inline void xor_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal}}; + a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; } else { @@ -2240,7 +2250,7 @@ static inline void xor_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal}}; + b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; } else { @@ -2261,7 +2271,7 @@ static inline void xor_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal}}; + b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; } else { diff --git a/src/fr_raw_generic.cpp.ejs b/src/fr_raw_generic.cpp.ejs index e13bbb1..f14fb20 100644 --- a/src/fr_raw_generic.cpp.ejs +++ b/src/fr_raw_generic.cpp.ejs @@ -1,362 +1,301 @@ -#include "<%=name.toLowerCase()+"_element.hpp"%>" -#include +#include "<%=name.toLowerCase()%>_element.hpp" +#include "mp.hpp" #include static uint64_t <%=name%>_rawq[] = {<%= constantElement(q) %>, 0}; -static <%=name%>RawElement <%=name%>_rawR2 = {<%= constantElement(bigInt.one.shiftLeft(n64*64*2).mod(q)) %>}; -static uint64_t <%=name%>_np = 0x<%= (bigInt.one.shiftLeft(64)).minus(q.modInv(bigInt.one.shiftLeft(64))).toString(16) %>; -static uint64_t lboMask = 0x<%= bigInt("10000000000000000",16).shiftRight(n64*64 - q.bitLength()).minus(bigInt.one).toString(16) %>; -static <%=name%>RawElement zero = {0}; +static const uint64_t <%=name%>_np = 0x<%= (bigInt.one.shiftLeft(64)).minus(q.modInv(bigInt.one.shiftLeft(64))).toString(16) %>ULL; +static uint64_t lboMask = 0x<%= bigInt("10000000000000000",16).shiftRight(n64*64 - q.bitLength()).minus(bigInt.one).toString(16) %>; void <%=name%>_rawAdd(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB) { - uint64_t carry = mpn_add_n(pRawResult, pRawA, pRawB, <%=name%>_N64); + uint64_t carry = mp_add(pRawResult, pRawA, pRawB); - if(carry || mpn_cmp(pRawResult, <%=name%>_rawq, <%=name%>_N64) >= 0) + if(carry || mp_cmp(pRawResult, <%=name%>_rawq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawAddLS(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, uint64_t rawB) { - uint64_t carry = mpn_add_1(pRawResult, pRawA, <%=name%>_N64, rawB); + uint64_t carry = mp_add(pRawResult, pRawA, rawB); - if(carry || mpn_cmp(pRawResult, <%=name%>_rawq, <%=name%>_N64) >= 0) + if(carry || mp_cmp(pRawResult, <%=name%>_rawq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawSub(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB) { - uint64_t carry = mpn_sub_n(pRawResult, pRawA, pRawB, <%=name%>_N64); + uint64_t carry = mp_sub(pRawResult, pRawA, pRawB); if(carry) { - mpn_add_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_add(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawSubRegular(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, <%=name%>RawElement pRawB) { - mpn_sub_n(pRawResult, pRawA, pRawB, <%=name%>_N64); + mp_sub(pRawResult, pRawA, pRawB); } void <%=name%>_rawSubSL(<%=name%>RawElement pRawResult, uint64_t rawA, <%=name%>RawElement pRawB) { - <%=name%>RawElement pRawA = {rawA}; + <%=name%>RawElement pRawA = {rawA, 0, 0, 0}; - uint64_t carry = mpn_sub_n(pRawResult, pRawA, pRawB, <%=name%>_N64); + uint64_t carry = mp_sub(pRawResult, pRawA, pRawB); if(carry) { - mpn_add_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_add(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawSubLS(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, uint64_t rawB) { - uint64_t carry = mpn_sub_1(pRawResult, pRawA, <%=name%>_N64, rawB); + uint64_t carry = mp_sub(pRawResult, pRawA, rawB); if(carry) { - mpn_add_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_add(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawNeg(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA) { - if (mpn_cmp(pRawA, zero, <%=name%>_N64) != 0) + if (!mp_is_zero(pRawA)) { - mpn_sub_n(pRawResult, <%=name%>_rawq, pRawA, <%=name%>_N64); + mp_sub(pRawResult, <%=name%>_rawq, pRawA); } else { - mpn_copyi(pRawResult, zero, <%=name%>_N64); + mp_set(pRawResult, 0); } } // Substracts a long element and a short element form 0 void <%=name%>_rawNegLS(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, uint64_t rawB) { - uint64_t carry1 = mpn_sub_1(pRawResult, <%=name%>_rawq, <%=name%>_N64, rawB); - uint64_t carry2 = mpn_sub_n(pRawResult, pRawResult, pRawA, <%=name%>_N64); + uint64_t carry1 = mp_sub(pRawResult, <%=name%>_rawq, rawB); + uint64_t carry2 = mp_sub(pRawResult, pRawResult, pRawA); if (carry1 || carry2) { - mpn_add_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_add(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawCopy(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA) { - memcpy(pRawResult, pRawA, sizeof(<%=name%>RawElement)); +<% for (let i=0; i + pRawResult[<%=i%>] = pRawA[<%=i%>]; +<% } %> } int <%=name%>_rawIsEq(const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB) { - return mpn_cmp(pRawA, pRawB, <%=name%>_N64) == 0; + return mp_cmp(pRawA, pRawB) == 0; } void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB) { - const mp_size_t N = <%=name%>_N64+1; + constexpr size_t N = <%=name%>_N64 + 1; const uint64_t *mq = <%=name%>_rawq; -<% if (!canOptimizeConsensys) { %> - uint64_t c = 0; -<% } -%> + uint64_t np0; -<% for (let i=0; i + +<% for (let i=0; i uint64_t product<%=i%>[N] = {0}; <% } %> - product0[N-1] = mpn_mul_1(product0, pRawB, <%=name%>_N64, pRawA[0]); -<% if (!canOptimizeConsensys) { -%> -<% for (let i=0; i - np0 = <%=name%>_np * product<%=i%>[0]; - product<%=i+1%>[N-1] += mpn_addmul_1(product<%=i%>, mq, N, np0); - - product<%=i+1%>[N-1] += mpn_addmul_1(product<%=i+1%>, pRawB, <%=name%>_N64, pRawA[<%=i+1%>]); -<% if (i == n64-2) { -%> - c = mpn_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); -<% } else { -%> - product<%=i+2%>[N-1] = mpn_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); -<% } -%> -<% } %> -<% } else { -%> + + product0[<%=n64%>] = mp_mul(product0, pRawB, pRawA[0]); + <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; - product<%=i+1%>[1] = mpn_addmul_1(product<%=i%>, mq, N, np0); + product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, np0); - product<%=i+1%>[N-1] = mpn_addmul_1(product<%=i+1%>, pRawB, <%=name%>_N64, pRawA[<%=i+1%>]); - mpn_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); + product<%=i+1%>[<%=n64%>] = mp_addmul(product<%=i+1%>, pRawB, pRawA[<%=i+1%>]); + mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); <% } %> -<% } -%> - np0 = <%=name%>_np * product<%=n64-1%>[0]; -<% if (!canOptimizeConsensys) { -%> - c += mpn_addmul_1(product<%=n64-1%>, mq, N, np0); - - mpn_copyi(pRawResult, product<%=n64-1%>+1, <%=name%>_N64); - if (c || mpn_cmp(pRawResult, mq, <%=name%>_N64) >= 0) -<% } else { -%> - mpn_addmul_1(product<%=n64-1%>, mq, N, np0); + np0 = <%=name%>_np * product<%=n64-1%>[0]; + mp_addmul(product<%=n64-1%>, mq, np0); - mpn_copyi(pRawResult, product<%=n64-1%>+1, <%=name%>_N64); + mp_copy(pRawResult, product<%=n64-1%>+1); - if (mpn_cmp(pRawResult, mq, <%=name%>_N64) >= 0) -<% } -%> + if (mp_cmp(pRawResult, mq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, mq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, mq); } } -void <%=name%>_rawMSquare(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA) -{ - <%=name%>_rawMMul(pRawResult, pRawA, pRawA); -} - void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, uint64_t pRawB) { - const mp_size_t N = <%=name%>_N64+1; + constexpr size_t N = <%=name%>_N64 + 1; const uint64_t *mq = <%=name%>_rawq; -<% if (!canOptimizeConsensys) { %> - uint64_t c = 0; -<% } -%> + uint64_t np0; -<% for (let i=0; i + +<% for (let i=0; i uint64_t product<%=i%>[N] = {0}; <% } %> - product0[N-1] = mpn_mul_1(product0, pRawA, <%=name%>_N64, pRawB); + + product0[<%=n64%>] = mp_mul(product0, pRawA, pRawB); + <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; -<% if (!canOptimizeConsensys) { -%> - product<%=i+1%>[N-1] = mpn_addmul_1(product<%=i%>, mq, N, np0); -<% } else { -%> - product<%=i+1%>[1] = mpn_addmul_1(product<%=i%>, mq, N, np0); -<% } -%> - mpn_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); + product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, np0); + mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); <% } %> - np0 = <%=name%>_np * product<%=n64-1%>[0]; -<% if (!canOptimizeConsensys) { -%> - c = mpn_addmul_1(product<%=n64-1%>, mq, N, np0); - mpn_copyi(pRawResult, product<%=n64-1%>+1, <%=name%>_N64); - - if (c || mpn_cmp(pRawResult, mq, <%=name%>_N64) >= 0) -<% } else { -%> - mpn_addmul_1(product<%=n64-1%>, mq, N, np0); + np0 = <%=name%>_np * product<%=n64-1%>[0]; + mp_addmul(product<%=n64-1%>, mq, np0); - mpn_copyi(pRawResult, product<%=n64-1%>+1, <%=name%>_N64); + mp_copy(pRawResult, product<%=n64-1%>+1); - if (mpn_cmp(pRawResult, mq, <%=name%>_N64) >= 0) -<% } -%> + if (mp_cmp(pRawResult, mq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, mq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, mq); } } -void <%=name%>_rawToMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA) -{ - <%=name%>_rawMMul(pRawResult, pRawA, <%=name%>_rawR2); -} - -void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA) +void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA) { - const mp_size_t N = <%=name%>_N64+1; + constexpr size_t N = <%=name%>_N64 + 1; const uint64_t *mq = <%=name%>_rawq; -<% if (!canOptimizeConsensys) { %> - uint64_t c = 0; -<% } -%> + uint64_t np0; + uint64_t product0[N]; -<% for (let i=1; i +<% for (let i=1; i uint64_t product<%=i%>[N] = {0}; <% } %> - mpn_copyi(product0, pRawA, <%=name%>_N64); product0[N-1] = 0; + + mp_copy(product0, pRawA); product0[<%=n64%>] = 0; + <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; -<% if (!canOptimizeConsensys) { -%> - product<%=i+1%>[N-1] = mpn_addmul_1(product<%=i%>, mq, N, np0); -<% } else { -%> - product<%=i+1%>[1] = mpn_addmul_1(product<%=i%>, mq, N, np0); -<% } -%> - mpn_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); + product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, np0); + mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); <% } %> - np0 = <%=name%>_np * product<%=n64-1%>[0]; -<% if (!canOptimizeConsensys) { -%> - c = mpn_addmul_1(product<%=n64-1%>, mq, N, np0); - - mpn_copyi(pRawResult, product<%=n64-1%>+1, <%=name%>_N64); - if (c || mpn_cmp(pRawResult, mq, <%=name%>_N64) >= 0) -<% } else { -%> - mpn_addmul_1(product<%=n64-1%>, mq, N, np0); + np0 = <%=name%>_np * product<%=n64-1%>[0]; + mp_addmul(product<%=n64-1%>, mq, np0); - mpn_copyi(pRawResult, product<%=n64-1%>+1, <%=name%>_N64); + mp_copy(pRawResult, product<%=n64-1%>+1); - if (mpn_cmp(pRawResult, mq, <%=name%>_N64) >= 0) -<% } -%> + if (mp_cmp(pRawResult, mq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, mq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, mq); } } int <%=name%>_rawIsZero(const <%=name%>RawElement rawA) { - return mpn_zero_p(rawA, <%=name%>_N64) ? 1 : 0; + return mp_is_zero(rawA) ? 1 : 0; } int <%=name%>_rawCmp(<%=name%>RawElement pRawA, <%=name%>RawElement pRawB) { - return mpn_cmp(pRawA, pRawB, <%=name%>_N64); + return mp_cmp(pRawA, pRawB); } void <%=name%>_rawSwap(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA) { <%=name%>RawElement temp; - <%=name%>_rawCopy(temp, pRawResult); - <%=name%>_rawCopy(pRawResult, pRawA); - <%=name%>_rawCopy(pRawA, temp); +<% for (let i=0; i + temp[<%=i%>] = pRawResult[<%=i%>]; +<% } %> + +<% for (let i=0; i + pRawResult[<%=i%>] = pRawA[<%=i%>]; +<% } %> + +<% for (let i=0; i + pRawA[<%=i%>] = temp[<%=i%>]; +<% } %> } void <%=name%>_rawCopyS2L(<%=name%>RawElement pRawResult, int64_t val) { pRawResult[0] = val; <% for (let i=1; i - pRawResult[<%= i %>] = 0;<% } %> + pRawResult[<%=i%>] = 0; +<% } %> - if (val < 0) { + if (val < 0) + { <% for (let i=1; i - pRawResult[<%= i %>] = -1;<% } %> + pRawResult[<%=i%>] = -1; +<% } %> - mpn_add_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_add(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawAnd(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, <%=name%>RawElement pRawB) { - mpn_and_n(pRawResult, pRawA, pRawB, <%=name%>_N64); + mp_and(pRawResult, pRawA, pRawB); - pRawResult[<%= n64 - 1 %>] &= lboMask; + pRawResult[<%=n64-1%>] &= lboMask; - if (mpn_cmp(pRawResult, <%=name%>_rawq, <%=name%>_N64) >= 0) + if (mp_cmp(pRawResult, <%=name%>_rawq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawOr(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, <%=name%>RawElement pRawB) { - mpn_ior_n(pRawResult, pRawA, pRawB, <%=name%>_N64); + mp_or(pRawResult, pRawA, pRawB); - pRawResult[<%= n64 - 1 %>] &= lboMask; + pRawResult[<%=n64-1%>] &= lboMask; - if (mpn_cmp(pRawResult, <%=name%>_rawq, <%=name%>_N64) >= 0) + if (mp_cmp(pRawResult, <%=name%>_rawq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawXor(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, <%=name%>RawElement pRawB) { - mpn_xor_n(pRawResult, pRawA, pRawB, <%=name%>_N64); + mp_xor(pRawResult, pRawA, pRawB); - pRawResult[<%= n64 - 1 %>] &= lboMask; + pRawResult[<%=n64-1%>] &= lboMask; - if (mpn_cmp(pRawResult, <%=name%>_rawq, <%=name%>_N64) >= 0) + if (mp_cmp(pRawResult, <%=name%>_rawq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, <%=name%>_rawq); } } void <%=name%>_rawShl(<%=name%>RawElement r, <%=name%>RawElement a, uint64_t b) { - uint64_t bit_shift = b % 64; - uint64_t word_shift = b / 64; - uint64_t word_count = <%=name%>_N64 - word_shift; - - mpn_copyi(r + word_shift, a, word_count); - std::memset(r, 0, word_shift * sizeof(uint64_t)); - - if (bit_shift) - { - mpn_lshift(r, r, <%=name%>_N64, bit_shift); - } + mp_shl(r, a, (uint32_t)b); - r[<%= n64 - 1 %>] &= lboMask; + r[<%=n64-1%>] &= lboMask; - if (mpn_cmp(r, <%=name%>_rawq, <%=name%>_N64) >= 0) - { - mpn_sub_n(r, r, <%=name%>_rawq, <%=name%>_N64); - } + if (mp_cmp(r, <%=name%>_rawq) >= 0) + mp_sub(r, r, <%=name%>_rawq); } void <%=name%>_rawShr(<%=name%>RawElement r, <%=name%>RawElement a, uint64_t b) { - const uint64_t bit_shift = b % 64; - const uint64_t word_shift = b / 64; - const uint64_t word_count = <%=name%>_N64 - word_shift; - - mpn_copyi(r, a + word_shift, word_count); - std::memset(r + word_count, 0, word_shift * sizeof(uint64_t)); - - if (bit_shift) - { - mpn_rshift(r, r, <%=name%>_N64, bit_shift); - } + mp_shr(r, a, (uint32_t)b); } void <%=name%>_rawNot(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA) { - mpn_com(pRawResult, pRawA, <%=name%>_N64); + mp_not(pRawResult, pRawA); - pRawResult[<%= n64 - 1 %>] &= lboMask; + pRawResult[<%=n64-1%>] &= lboMask; - if (mpn_cmp(pRawResult, <%=name%>_rawq, <%=name%>_N64) >= 0) + if (mp_cmp(pRawResult, <%=name%>_rawq) >= 0) { - mpn_sub_n(pRawResult, pRawResult, <%=name%>_rawq, <%=name%>_N64); + mp_sub(pRawResult, pRawResult, <%=name%>_rawq); } } diff --git a/src/mp.cpp.ejs b/src/mp.cpp.ejs new file mode 100644 index 0000000..24b0a8c --- /dev/null +++ b/src/mp.cpp.ejs @@ -0,0 +1,1385 @@ +#include "mp.hpp" + +#include +#include +#include +#include +#include +#include +#include + +static inline uint64_t add_carry(uint64_t *out, uint64_t a, uint64_t b, uint64_t c) { +#if defined(__clang__) || defined(__GNUC__) + uint64_t t; + unsigned c1 = __builtin_add_overflow(a, b, &t); + unsigned c2 = __builtin_add_overflow(t, c, &t); + *out = t; + return (c1 | c2); +#else + uint64_t t0 = a + b; + uint64_t carry1 = (t0 < a); + uint64_t t1 = t0 + c; + uint64_t carry2 = (t1 < t0); + *out = t1; + return carry1 | carry2; +#endif +} + +static inline uint64_t sub_borrow(uint64_t *out, uint64_t a, uint64_t b, uint64_t c) { +#if defined(__clang__) || defined(__GNUC__) + uint64_t t; + unsigned b1 = __builtin_sub_overflow(a, b, &t); + unsigned b2 = __builtin_sub_overflow(t, c, &t); + *out = t; + return (b1 | b2); +#else + uint64_t t0 = a - b; + uint64_t borrow1 = (a < b); + uint64_t t1 = t0 - c; + uint64_t borrow2 = (t0 < c); + *out = t1; + return borrow1 | borrow2; +#endif +} + +void mp_zero(uint64_t *r) { + std::memset(r, 0, MP_N64 * sizeof(uint64_t)); +} + +void mp_set(uint64_t *r, uint64_t a) { + r[0] = a; + + for (int i = 1; i < MP_N64; i++) r[i] = 0; +} + +void mp_copy(uint64_t *r, const uint64_t *a) { + if (r == a) return; + + int i = 0, end = MP_N64, step = 1; + + if (r > a && r < a + MP_N64) { + i = MP_N64 - 1; + end = -1; + step = -1; + } + + for (; i != end; i += step) r[i] = a[i]; +} + +int mp_cmp(const uint64_t *a, const uint64_t *b) { + for (int i = MP_N64 - 1; i >= 0; i--) { + if (a[i] < b[i]) return -1; + if (a[i] > b[i]) return 1; + } + return 0; +} + +bool mp_is_zero(const uint64_t *a) { + uint64_t acc = 0; + for (int i = 0; i < MP_N64; ++i) + acc |= a[i]; + return acc == 0; +} + +void mp_shl(uint64_t *r, const uint64_t *a, uint64_t k) { + if (k >= (uint64_t)MP_N64 * 64u) { + mp_zero(r); + return; + } + if (k == 0) { + mp_copy(r, a); + return; + } + + const auto wordShift = (uint32_t)(k >> 6); + const auto bitShift = (uint32_t)(k & 63u); + + if (bitShift == 0) { + for (int i = MP_N64 - 1; i >= 0; --i) { + int si = i - (int)wordShift; + r[i] = si >= 0 ? a[si] : 0; + } + return; + } + + const uint32_t inv = 64u - bitShift; + for (int i = MP_N64 - 1; i >= 0; --i) { + int si0 = i - (int)wordShift; + int si1 = si0 - 1; + + uint64_t lo = (si0 >= 0) ? a[si0] : 0; + uint64_t hi = (si1 >= 0) ? a[si1] : 0; + + r[i] = lo << bitShift | hi >> inv; + } +} + +void mp_shr(uint64_t *r, const uint64_t *a, uint64_t k) { + if (k >= (uint64_t)MP_N64 * 64u) { + mp_zero(r); + return; + } + if (k == 0) { + mp_copy(r, a); + return; + } + + const auto wordShift = (uint32_t)(k >> 6); + const auto bitShift = (uint32_t)(k & 63u); + + if (bitShift == 0) { + for (uint32_t i = 0; i < MP_N64; ++i) { + uint32_t si = i + wordShift; + r[i] = (si < (uint32_t)MP_N64) ? a[si] : 0; + } + return; + } + + const uint32_t inv = 64u - bitShift; + for (uint32_t i = 0; i < MP_N64; ++i) { + uint32_t si0 = i + wordShift; + uint32_t si1 = si0 + 1; + + uint64_t lo = (si0 < (uint32_t)MP_N64) ? a[si0] : 0; + uint64_t hi = (si1 < (uint32_t)MP_N64) ? a[si1] : 0; + + r[i] = lo >> bitShift | hi << inv; + } +} + +uint64_t mp_add(uint64_t *r, const uint64_t *a, const uint64_t *b) { + uint64_t c = 0; + + for (int i = 0; i < MP_N64; ++i) { + c = add_carry(&r[i], a[i], b[i], c); + } + + return c; +} + +uint64_t mp_add(uint64_t *r, const uint64_t *a, uint64_t b) { + uint64_t c = add_carry(&r[0], a[0], b, 0); + + for (int i = 1; i < MP_N64; ++i) { + c = add_carry(&r[i], a[i], 0, c); + } + + return c; +} + +uint64_t mp_sub(uint64_t *r, const uint64_t *a, const uint64_t *b) { + uint64_t br = 0; + + for (int i = 0; i < MP_N64; ++i) { + br = sub_borrow(&r[i], a[i], b[i], br); + } + + return br; +} + +uint64_t mp_sub(uint64_t *r, const uint64_t *a, uint64_t b) { + uint64_t br = sub_borrow(&r[0], a[0], b, 0); + + for (int i = 1; i < MP_N64; ++i) { + br = sub_borrow(&r[i], a[i], 0, br); + } + + return br; +} + +bool mp_tstbit(const uint64_t *a, size_t bit) { + if (bit >= (size_t)MP_N64 * 64u) return false; + return a[bit >> 6] >> (bit & 63u) & 1ULL; +} + +void mp_export_be(uint8_t *r, const uint64_t *a) { + const auto *p = reinterpret_cast(a); + const size_t nbytes = MP_N64 * sizeof(uint64_t); + + for (size_t i = 0; i < nbytes; ++i) { + r[i] = p[nbytes - 1 - i]; + } +} + +void mp_import_be(uint64_t *r, const uint8_t *a) { + mp_zero(r); + auto *dst = reinterpret_cast(r); + const size_t nbytes = MP_N64 * sizeof(uint64_t); + + for (size_t i = 0; i < nbytes; ++i) { + dst[i] = a[nbytes - 1 - i]; + } +} + +bool mp_set(uint64_t *r, const char *str, uint32_t base) { + if (base == 0u) base = 10u; + if (base < 2u || base > 16u) return false; + + mp_zero(r); + + while (*str && std::isspace((unsigned char)*str)) str++; + if (*str == '+') str++; + if (*str == '-') return false; + + bool any = false; + + for (; *str; ++str) { + if (std::isspace((unsigned char)*str)) break; + + const unsigned char uc = (unsigned char)*str; + uint32_t digit; + if (uc >= (unsigned char)'0' && uc <= (unsigned char)'9') { + digit = (uint32_t)(uc - (unsigned char)'0'); + } else if (uc >= (unsigned char)'a' && uc <= (unsigned char)'f') { + digit = (uint32_t)(uc - (unsigned char)'a') + 10u; + } else if (uc >= (unsigned char)'A' && uc <= (unsigned char)'F') { + digit = (uint32_t)(uc - (unsigned char)'A') + 10u; + } else { + return false; + } + if (digit >= base) return false; + + if (mp_mul(r, r, base) != 0) return false; + if (mp_add(r, r, digit) != 0) return false; + any = true; + } + + if (!any) return false; + while (*str && std::isspace((unsigned char)*str)) str++; + return *str == '\0'; +} + +static inline uint32_t mp_div(uint64_t *q, const uint64_t *a, uint32_t base) { +#if defined(__SIZEOF_INT128__) + uint64_t rem = 0; + + for (int i = MP_N64 - 1; i >= 0; i--) { + __uint128_t cur = (((__uint128_t)rem) << 2*MP_N) | (__uint128_t)a[i]; + q[i] = (uint64_t)(cur / base); + rem = (uint64_t)(cur % base); + } + return (uint32_t)rem; +#else + // long division in base 2^32: numerator = rem*2^64 + a[i] + // rem < base <= 16 => safe in uint32_t + uint32_t rem = 0; + for (int i = MP_N64 - 1; i >= 0; --i) { + uint32_t hi = (uint32_t)(a[i] >> MP_N); + uint32_t lo = (uint32_t)(a[i] & 0xFFFFFFFFu); + + uint64_t cur = ((uint64_t)rem << MP_N) | (uint64_t)hi; + uint32_t qhi = (uint32_t)(cur / base); + rem = (uint32_t)(cur % base); + + cur = ((uint64_t)rem << MP_N) | (uint64_t)lo; + uint32_t qlo = (uint32_t)(cur / base); + rem = (uint32_t)(cur % base); + + q[i] = ((uint64_t)qhi << MP_N) | (uint64_t)qlo; + } + return rem; +#endif +} + +std::string mp_get_str(const uint64_t *a, uint32_t base) { + if (base == 0u) base = 10u; + if (base < 2u || base > 16u) return {}; + + if (mp_is_zero(a)) return {"0"}; + + if (base == 16u) { + static const char *hex = "0123456789abcdef"; + std::string out; + bool started = false; + + for (int i = MP_N64 - 1; i >= 0; --i) { + for (int shift = 60; shift >= 0; shift -= 4) { + uint32_t nib = (uint32_t)((a[i] >> shift) & 0xFu); + if (!started) { + if (nib == 0) continue; + started = true; + } + out.push_back(hex[nib]); + } + } + return out; + } + + uint64_t v[MP_N64]; + mp_copy(v, a); + + if (base == 10u) { + static constexpr uint32_t DEC_BASE = 1000000000u; + std::vector chunks; + + while (!mp_is_zero(v)) { + uint64_t q[MP_N64]; + uint32_t rem = mp_div(q, v, DEC_BASE); + chunks.push_back(rem); + mp_copy(v, q); + } + + std::string out = std::to_string(chunks.back()); + for (int i = (int)chunks.size() - 2; i >= 0; --i) { + std::string s = std::to_string(chunks[i]); + out.append(9 - s.size(), '0'); + out += s; + } + return out; + } + + std::string out; + while (!mp_is_zero(v)) { + uint64_t q[MP_N64]; + uint32_t rem = mp_div(q, v, base); + char digit = (rem < 10u) + ? (char)('0' + rem) + : (char)('a' + (rem - 10u)); + out.push_back(digit); + mp_copy(v, q); + } + + std::reverse(out.begin(), out.end()); + return out; +} + +bool mp_fits_int32(const uint64_t *a) { + for (int i = 1; i < MP_N64; ++i) { + if (a[i] != 0) return false; + } + return a[0] <= (uint64_t)INT32_MAX; +} + +int32_t mp_get_int32(const mp_uint_t a) { + return (int32_t)a[0]; +} + +static inline void mp_add_mod(uint64_t *r, const uint64_t *a, const uint64_t *b, const uint64_t *mod) { + uint64_t carry = mp_add(r, a, b); + + if (carry || mp_cmp(r, mod) >= 0) { + mp_sub(r, r, mod); + } +} + +static inline void mp_mul_mod(uint64_t *r, const uint64_t *a, uint32_t b, const uint64_t *mod) { + uint64_t res[MP_N64]; mp_zero(res); + + uint64_t cur[MP_N64]; + if (mp_cmp(a, mod) >= 0) { + mp_uint_t q, rem; + mp_div(q, rem, a, mod); + mp_copy(cur, rem); + } else { + mp_copy(cur, a); + } + + uint32_t k = b; + while (k) { + if (k & 1u) { + uint64_t tmp[MP_N64]; + mp_add_mod(tmp, res, cur, mod); + mp_copy(res, tmp); + } + k >>= 1u; + if (k) { + uint64_t tmp[MP_N64]; + mp_add_mod(tmp, cur, cur, mod); + mp_copy(cur, tmp); + } + } + mp_copy(r, res); +} + +bool mp_set_mod(uint64_t *r, const char *str, uint32_t base, const uint64_t *mod) { + mp_zero(r); + + if (base == 0u) base = 10u; + if (base < 2u || base > 16u) return false; + + while (*str && std::isspace((unsigned char)*str)) str++; + + bool neg = false; + if (*str == '+') str++; + else if (*str == '-') { neg = true; str++; } + + uint64_t acc[MP_N64]; + mp_zero(acc); + + bool any = false; + + auto reduce_small = [&](uint64_t *x) { + // x < mod*base + 15 => at most base subtractions (base <= 16) + while (mp_cmp(x, mod) >= 0) { + mp_sub(x, x, mod); + } + }; + + for (; *str; str++) { + if (std::isspace((unsigned char)*str)) break; + + const char uc = *str; + uint32_t d; + if (uc >= '0' && uc <= '9') { + d = uc - '0'; + } else if (uc >= 'a' && uc <= 'f') { + d = (uc - 'a') + 10u; + } else if (uc >= 'A' && uc <= 'F') { + d = (uc - 'A') + 10u; + } else { + return false; + } + if (d >= base) return false; + +#if !defined(__SIZEOF_INT128__) + { + uint64_t t1[MP_N64]; + mp_mul_mod(t1, acc, base, mod); + uint64_t dv[MP_N64]; + mp_set(dv, d); + uint64_t t2[MP_N64]; + mp_add_mod(t2, t1, dv, mod); + mp_copy(acc, t2); + any = true; + continue; + } +#else + + uint64_t tmp[MP_N64]; + { + __uint128_t carry = 0; + for (size_t i = 0; i < MP_N64; i++) { + __uint128_t prod = (__uint128_t)acc[i] * (__uint128_t)base + carry; + tmp[i] = (uint64_t)prod; + carry = prod >> 2*MP_N; + } + + if (carry) { + uint64_t t1[MP_N64]; + mp_mul_mod(t1, acc, base, mod); + uint64_t dv[MP_N64]; + mp_set(dv, d); + uint64_t t2[MP_N64]; + mp_add_mod(t2, t1, dv, mod); + mp_copy(acc, t2); + any = true; + continue; + } + } + + // tmp += d + { + uint64_t c = d; + + for (size_t i = 0; i < MP_N64 && c; i++) { + uint64_t before = tmp[i]; + tmp[i] += c; + c = tmp[i] < before ? 1u : 0u; + } + + if (c) { + uint64_t t1[MP_N64]; + mp_mul_mod(t1, acc, base, mod); + uint64_t dv[MP_N64]; + mp_set(dv, d); + uint64_t t2[MP_N64]; + mp_add_mod(t2, t1, dv, mod); + mp_copy(acc, t2); + any = true; + continue; + } + } + + mp_copy(acc, tmp); + reduce_small(acc); + any = true; +#endif + } + + if (!any) return false; + + while (*str && std::isspace((unsigned char)*str)) str++; + if (*str != '\0') return false; + + if (neg && !mp_is_zero(acc)) { + uint64_t tmp[MP_N64]; + mp_sub(tmp, mod, acc); + mp_copy(acc, tmp); + } + + mp_copy(r, acc); + return true; +} + +static inline unsigned mp_clz64(uint64_t x) { return x ? __builtin_clzll(x) : 64; } + +static inline void mp_mul64(uint64_t a, uint64_t b, uint64_t &lo, uint64_t &hi) { + const uint64_t a0 = (uint32_t)a; + const uint64_t a1 = a >> MP_N; + const uint64_t b0 = (uint32_t)b; + const uint64_t b1 = b >> MP_N; + + const uint64_t p00 = a0 * b0; + const uint64_t p01 = a0 * b1; + const uint64_t p10 = a1 * b0; + const uint64_t p11 = a1 * b1; + + const uint64_t mid = (p00 >> MP_N) + (uint32_t)p01 + (uint32_t)p10; + lo = (p00 & 0xFFFFFFFFULL) | (mid << MP_N); + hi = p11 + (p01 >> MP_N) + (p10 >> MP_N) + (mid >> MP_N); +} + +static inline int mp_num_limbs(const uint64_t *a) { + for (int i = MP_N64 - 1; i >= 0; --i) { + if (a[i] != 0) return i + 1; + } + + return 0; +} + +static inline uint64_t div_2by1(uint64_t *q, uint64_t hi, uint64_t lo, uint64_t v) { + uint64_t qq = 0; + uint64_t rem = hi; + + for (int bit = (2 * MP_N) - 1; bit >= 0; --bit) { + // Shift remainder left by 1 and bring next bit from lo. + uint64_t overflow = rem >> ((2 * MP_N) - 1); + rem = (rem << 1) | ((lo >> bit) & 1ULL); + + // If previous top bit was 1, actual shifted remainder was >= 2^64. + // Since rem_before < v, shifted remainder < 2*v + 1, so one subtraction is enough. + if (overflow || rem >= v) { + rem -= v; + qq |= (1ULL << bit); + } + } + + *q = qq; + return rem; +} + +static inline uint64_t div_1word(uint64_t *q, const uint64_t *u, uint64_t v) +{ +#if defined(__SIZEOF_INT128__) + __uint128_t rem = 0; + + for (int i = MP_N64 - 1; i >= 0; i--) { + __uint128_t cur = rem << 2*MP_N | (__uint128_t)u[i]; + q[i] = (uint64_t)(cur / v); + rem = (uint64_t)(cur % v); + } + + return (uint64_t)rem; +#else +uint64_t rem = 0; + +for (int i = MP_N64 - 1; i >= 0; --i) { + uint64_t qi; + rem = div_2by1(&qi, rem, u[i], v); + q[i] = qi; +} + +return rem; +#endif +} + +static inline uint64_t mul_sub_knuth(uint64_t *u, const uint64_t *v, int n, uint64_t qhat) +{ +#if defined(__SIZEOF_INT128__) + __uint128_t carry = 0; + uint64_t borrow = 0; + + for (int i = 0; i < n; i++) { + __uint128_t prod = (__uint128_t)qhat * (__uint128_t)v[i] + carry; + uint64_t pl = (uint64_t)prod; + carry = prod >> 2*MP_N; + __int128_t t = (__int128_t)u[i] - (__int128_t)pl - (__int128_t)borrow; + u[i] = (uint64_t)t; + borrow = t < 0 ? 1u : 0u; + } + + __int128_t ttop = (__int128_t)u[n] - (__int128_t)carry - (__int128_t)borrow; + u[n] = (uint64_t)ttop; + + return ttop < 0 ? 1u : 0u; +#else + uint64_t carry = 0; + uint64_t borrow = 0; + + for (int i = 0; i < n; ++i) { + uint64_t hi, lo; + mp_mul64(qhat, v[i], lo, hi); + + // Add previous carry into low part of the product. + uint64_t c = add_carry(&lo, lo, carry, 0); + hi += c; + + // Subtract low limb and incoming borrow from u[i]. + borrow = sub_borrow(&u[i], u[i], lo, borrow); + + // High limb becomes carry for next step. + carry = hi; + } + + // Subtract final carry and borrow from top limb u[n]. + return sub_borrow(&u[n], u[n], carry, borrow); +#endif +} + +static inline uint64_t add_back_knuth(uint64_t *u, const uint64_t *v, int n) +{ +#if defined(__SIZEOF_INT128__) + __uint128_t carry = 0; + + for (int i = 0; i < n; i++) { + __uint128_t t = (__uint128_t)u[i] + (__uint128_t)v[i] + carry; + u[i] = (uint64_t)t; + carry = t >> 2*MP_N; + } + + __uint128_t ttop = (__uint128_t)u[n] + carry; + u[n] = (uint64_t)ttop; + + return (uint64_t)(ttop >> 2*MP_N); +#else + uint64_t carry = 0; + + for (int i = 0; i < n; ++i) { + carry = add_carry(&u[i], u[i], v[i], carry); + } + + return add_carry(&u[n], u[n], 0, carry); +#endif +} + +void mp_div(uint64_t *q, uint64_t *r, const uint64_t *num, const uint64_t *den) { + if (mp_is_zero(den)) { + mp_zero(q); + mp_zero(r); + return; + } + + if (mp_cmp(num, den) < 0) { + mp_zero(q); + mp_copy(r, num); + return; + } + +#if !defined(__SIZEOF_INT128__) + int m = mp_num_limbs(num); + int n = mp_num_limbs(den); + + if (n == 1) { + uint64_t v = den[0]; + mp_uint_t u; + mp_uint_t qq = {0}; + + mp_copy(u, num); + + uint64_t rem = div_1word(qq, u, v); + + mp_copy(q, qq); + mp_set(r, rem); + return; + } + + mp_uint_t vnorm = {0}; + uint64_t unorm[MP_N64 + 1] = {0}; + mp_uint_t vraw; + uint64_t uraw[MP_N64 + 1] = {0}; + + mp_copy(vraw, den); + for (int i = 0; i < MP_N64; ++i) { + uraw[i] = num[i]; + } + uraw[MP_N64] = 0; + + unsigned s = mp_clz64(vraw[n - 1]); + + if (s == 0) { + for (int i = 0; i < n; ++i) vnorm[i] = vraw[i]; + for (int i = n; i < MP_N64; ++i) vnorm[i] = 0; + for (int i = 0; i < MP_N64 + 1; ++i) unorm[i] = uraw[i]; + } else { + uint64_t carry = 0; + for (int i = 0; i < n; ++i) { + uint64_t x = vraw[i]; + vnorm[i] = (x << s) | carry; + carry = x >> ((2 * MP_N) - s); + } + + for (int i = n; i < MP_N64; ++i) vnorm[i] = 0; + + carry = 0; + for (int i = 0; i < MP_N64 + 1; ++i) { + uint64_t x = uraw[i]; + unorm[i] = (x << s) | carry; + carry = x >> ((2 * MP_N) - s); + } + } + + int qn = m - n + 1; + mp_uint_t qlimb = {0}; + + const uint64_t v1 = vnorm[n - 1]; + const uint64_t v2 = vnorm[n - 2]; + + for (int j = qn - 1; j >= 0; --j) { + uint64_t qhat, rhat; + rhat = div_2by1(&qhat, unorm[j + n], unorm[j + n - 1], v1); + + for (;;) { + uint64_t lo, hi; + mp_mul64(qhat, v2, lo, hi); + + bool too_big = false; + if (hi > rhat) { + too_big = true; + } else if (hi == rhat && lo > unorm[j + n - 2]) { + too_big = true; + } + + if (!too_big) break; + + --qhat; + uint64_t old_rhat = rhat; + rhat += v1; + if (rhat < old_rhat) break; + } + + uint64_t *u_seg = &unorm[j]; + uint64_t borrow_out = mul_sub_knuth(u_seg, vnorm, n, qhat); + + if (borrow_out) { + add_back_knuth(u_seg, vnorm, n); + --qhat; + } + + qlimb[j] = qhat; + } + + mp_uint_t rlimb = {0}; + + if (s == 0) { + for (int i = 0; i < n; ++i) rlimb[i] = unorm[i]; + } else { + uint64_t carry = 0; + for (int i = n - 1; i >= 0; --i) { + uint64_t x = unorm[i]; + rlimb[i] = (x >> s) | carry; + carry = x << ((2 * MP_N) - s); + } + } + + mp_copy(q, qlimb); + mp_copy(r, rlimb); + return; +#else + int m = mp_num_limbs(num); + int n = mp_num_limbs(den); + + if (n == 1) { + uint64_t v = den[0]; + mp_uint_t u; + mp_uint_t qq = {}; + + mp_copy(u, num); + + uint64_t rem = div_1word(qq, u, v); + + mp_copy(q, qq); + mp_set(r, rem); + return; + } + + mp_uint_t vnorm = {}; + uint64_t unorm[MP_N64 + 1] = {}; + mp_uint_t vraw; + uint64_t uraw[MP_N64 + 1] = {}; + + mp_copy(vraw, den); + for (int i = 0; i < MP_N64; ++i) { + uraw[i] = num[i]; + } + uraw[MP_N64] = 0; + + unsigned s = mp_clz64(vraw[n - 1]); + + if (s == 0) { + for (int i = 0; i < n; i++) vnorm[i] = vraw[i]; + for (int i = n; i < MP_N64; i++) vnorm[i] = 0; + for (int i = 0; i < MP_N64 + 1; i++) unorm[i] = uraw[i]; + } else { + uint64_t carry = 0; + for (int i = 0; i < n; i++) { + uint64_t x = vraw[i]; + vnorm[i] = x << s | carry; + carry = x >> (2*MP_N - s); + } + + for (int i = n; i < MP_N64; i++) vnorm[i] = 0; + + carry = 0; + + for (int i = 0; i < MP_N64 + 1; i++) { + uint64_t x = uraw[i]; + unorm[i] = x << s | carry; + carry = x >> (2*MP_N - s); + } + } + + int qn = m - n + 1; + mp_uint_t qlimb = {}; + + const uint64_t v1 = vnorm[n - 1]; + const uint64_t v2 = vnorm[n - 2]; + + for (int j = qn - 1; j >= 0; j--) { + uint64_t qhat; + __uint128_t rhat; + + if (unorm[j + n] == v1) { + qhat = UINT64_MAX; + rhat = (__uint128_t)unorm[j + n - 1] + (__uint128_t)v1; + } else { + __uint128_t uj2 = + (__uint128_t)unorm[j + n] << (2*MP_N) | + (__uint128_t)unorm[j + n - 1]; + + qhat = (uint64_t)(uj2 / v1); + rhat = uj2 % v1; + } + + for (;;) { + __uint128_t left = (__uint128_t)qhat * (__uint128_t)v2; + __uint128_t right = (rhat << 2*MP_N) | (__uint128_t)unorm[j + n - 2]; + if (left <= right) break; + + --qhat; + rhat += (__uint128_t)v1; + if (rhat >= (((__uint128_t)1) << 2*MP_N)) break; + } + + uint64_t *u_seg = &unorm[j]; + uint64_t borrow_out = mul_sub_knuth(u_seg, vnorm, n, qhat); + + if (borrow_out) { + add_back_knuth(u_seg, vnorm, n); + qhat--; + } + + qlimb[j] = qhat; + } + + mp_uint_t rlimb = {}; + + if (s == 0) { + for (int i = 0; i < n; i++) rlimb[i] = unorm[i]; + } else { + uint64_t carry = 0; + + for (int i = n - 1; i >= 0; i--) { + uint64_t x = unorm[i]; + rlimb[i] = x >> s | carry; + carry = x << (2*MP_N - s); + } + } + + mp_copy(q, qlimb); + mp_copy(r, rlimb); +#endif +} + +static inline void mp_mul_full(uint64_t *out2n, const uint64_t *a, const uint64_t *b) +{ +#if defined(__SIZEOF_INT128__) + for (int i = 0; i < 2 * MP_N64; ++i) { + out2n[i] = 0; + } + + for (int i = 0; i < MP_N64; ++i) { + __uint128_t carry = 0; + for (int j = 0; j < MP_N64; ++j) { + __uint128_t cur = (__uint128_t)a[i] * (__uint128_t)b[j] + + (__uint128_t)out2n[i + j] + + carry; + out2n[i + j] = (uint64_t)cur; + carry = cur >> (2 * MP_N); + } + + int k = i + MP_N64; + while (carry) { + __uint128_t cur = (__uint128_t)out2n[k] + carry; + out2n[k] = (uint64_t)cur; + carry = cur >> (2 * MP_N); + ++k; + } + } +#else + for (int i = 0; i < 2 * MP_N64; ++i) out2n[i] = 0; + + for (int i = 0; i < MP_N64; ++i) { + uint64_t carry = 0; + + for (int j = 0; j < MP_N64; ++j) { + uint64_t lo, hi; + mp_mul64(a[i], b[j], lo, hi); + + uint64_t x; + uint64_t c0 = add_carry(&x, out2n[i + j], lo, 0); + uint64_t c1 = add_carry(&x, x, carry, 0); + out2n[i + j] = x; + + carry = hi + c0 + c1; + } + + int k = i + MP_N64; + while (carry && k < 2 * MP_N64) { + uint64_t x; + uint64_t c = add_carry(&x, out2n[k], carry, 0); + out2n[k] = x; + carry = c; + ++k; + } + } +#endif +} + +static inline void mp_mod_2n_n(uint64_t *r, const uint64_t *num2n, const uint64_t *den) +{ + const int n = mp_num_limbs(den); + + if (n == 0) { + mp_zero(r); + return; + } + + if (n == 1) { + uint64_t rem = 0; +#if defined(__SIZEOF_INT128__) + for (int i = 2 * MP_N64 - 1; i >= 0; --i) { + __uint128_t cur = ((__uint128_t)rem << (2 * MP_N)) | (__uint128_t)num2n[i]; + rem = (uint64_t)(cur % den[0]); + } +#else + for (int i = 2 * MP_N64 - 1; i >= 0; --i) { + uint64_t qdummy; + rem = div_2by1(&qdummy, rem, num2n[i], den[0]); + } +#endif + mp_set(r, rem); + return; + } + + uint64_t vnorm[MP_N64] = {0}; + uint64_t unorm[2 * MP_N64 + 1] = {0}; + + const unsigned s = mp_clz64(den[n - 1]); + + if (s == 0) { + for (int i = 0; i < n; ++i) { + vnorm[i] = den[i]; + } + for (int i = 0; i < 2 * MP_N64; ++i) { + unorm[i] = num2n[i]; + } + unorm[2 * MP_N64] = 0; + } else { + uint64_t carry = 0; + for (int i = 0; i < n; ++i) { + uint64_t x = den[i]; + vnorm[i] = (x << s) | carry; + carry = x >> ((2 * MP_N) - s); + } + + carry = 0; + for (int i = 0; i < 2 * MP_N64; ++i) { + uint64_t x = num2n[i]; + unorm[i] = (x << s) | carry; + carry = x >> ((2 * MP_N) - s); + } + unorm[2 * MP_N64] = carry; + } + + const uint64_t v1 = vnorm[n - 1]; + const uint64_t v2 = vnorm[n - 2]; + + const int qn = 2 * MP_N64 - n + 1; + + for (int j = qn - 1; j >= 0; --j) { + uint64_t qhat, rhat; + +#if defined(__SIZEOF_INT128__) + __uint128_t uj2 = + ((__uint128_t)unorm[j + n] << (2 * MP_N)) | + (__uint128_t)unorm[j + n - 1]; + + qhat = (uint64_t)(uj2 / v1); + rhat = (uint64_t)(uj2 % v1); + + for (;;) { + __uint128_t left = + (__uint128_t)qhat * (__uint128_t)v2; + __uint128_t right = + ((__uint128_t)rhat << (2 * MP_N)) | + (__uint128_t)unorm[j + n - 2]; + + if (left <= right) break; + + --qhat; + uint64_t old_rhat = rhat; + rhat += v1; + if (rhat < old_rhat) break; + } +#else + rhat = div_2by1(&qhat, unorm[j + n], unorm[j + n - 1], v1); + + for (;;) { + uint64_t lo, hi; + mp_mul64(qhat, v2, lo, hi); + + bool too_big = false; + if (hi > rhat) { + too_big = true; + } else if (hi == rhat && lo > unorm[j + n - 2]) { + too_big = true; + } + + if (!too_big) break; + + --qhat; + uint64_t old_rhat = rhat; + rhat += v1; + if (rhat < old_rhat) break; + } +#endif + + uint64_t borrow_out = mul_sub_knuth(&unorm[j], vnorm, n, qhat); + if (borrow_out) { + add_back_knuth(&unorm[j], vnorm, n); + } + } + + mp_zero(r); + + if (s == 0) { + for (int i = 0; i < n; ++i) { + r[i] = unorm[i]; + } + } else { + uint64_t carry = 0; + for (int i = n - 1; i >= 0; --i) { + uint64_t x = unorm[i]; + r[i] = (x >> s) | carry; + carry = x << ((2 * MP_N) - s); + } + } + + while (mp_cmp(r, den) >= 0) { + mp_sub(r, r, den); + } +} + +static inline void mp_mulmod(uint64_t *r, const uint64_t *a, const uint64_t *b, const uint64_t *mod) +{ + if (mp_is_zero(mod)) { + mp_zero(r); + return; + } + + uint64_t prod[2 * MP_N64]; + mp_mul_full(prod, a, b); + mp_mod_2n_n(r, prod, mod); +} + +void mp_pow_mod(uint64_t *r, const uint64_t *base, const uint64_t *exp, const uint64_t *mod) +{ + mp_uint_t one; + mp_set(one, 1u); + + if (mp_cmp(mod, one) == 0) { + mp_zero(r); + return; + } + + mp_uint_t bcur; + + if (mp_cmp(base, mod) >= 0) { + mp_uint_t q, rem; + mp_div(q, rem, base, mod); + mp_copy(bcur, rem); + } else { + mp_copy(bcur, base); + } + + int topBit = -1; + for (int limb = MP_N64 - 1; limb >= 0 && topBit < 0; --limb) { + uint64_t w = exp[limb]; + if (!w) continue; + + for (int bit = 2 * MP_N - 1; bit >= 0; --bit) { + if ((w >> bit) & 1u) { + topBit = limb * 2 * MP_N + bit; + break; + } + } + } + + if (topBit < 0) { + mp_copy(r, one); + return; + } + + mp_uint_t acc; + mp_copy(acc, bcur); + + for (int i = topBit - 1; i >= 0; --i) { + mp_uint_t sq; + mp_mulmod(sq, acc, acc, mod); + mp_copy(acc, sq); + + const int limb = i / (2 * MP_N); + const int bit = i % (2 * MP_N); + + if ((exp[limb] >> bit) & 1u) { + mp_uint_t tmp; + mp_mulmod(tmp, acc, bcur, mod); + mp_copy(acc, tmp); + } + } + + mp_copy(r, acc); +} + +void mp_set_mod(uint64_t *r, int64_t a, const uint64_t *mod) { + if (a >= 0) { + mp_set(r, (uint64_t)a); + + if (mp_cmp(r, mod) >= 0) { + uint64_t q[MP_N64], rem[MP_N64]; + mp_div(q, rem, r, mod); + mp_copy(r, rem); + } + + return; + } + +#if defined(__SIZEOF_INT128__) + uint64_t absv = (uint64_t)(-((__int128_t)a)); +#else + uint64_t absv = (uint64_t)(-(uint64_t)a); +#endif + + uint64_t av[MP_N64]; + mp_set(av, absv); + + if (mp_is_zero(av)) { + mp_zero(r); + return; + } + + if (mp_cmp(av, mod) >= 0) { + uint64_t q[MP_N64], rem[MP_N64]; + mp_div(q, rem, av, mod); + mp_copy(av, rem); + } + + if (mp_is_zero(av)) { + mp_zero(r); + return; + } + + mp_sub(r, mod, av); +} + +uint64_t mp_mul(uint64_t *r, const uint64_t *a, uint64_t b) { +#if defined(__SIZEOF_INT128__) + __uint128_t carry = 0; + for (int i = 0; i < MP_N64; i++) { + __uint128_t t = (__uint128_t)a[i] * (__uint128_t)b + carry; + r[i] = (uint64_t)t; + carry = t >> 2*MP_N; + } + return (uint64_t)carry; +#else + uint64_t carry = 0; + for (int i = 0; i < MP_N64; i++) { + uint64_t lo, hi; + mp_mul64(a[i], b, lo, hi); + + uint64_t out = lo + carry; + hi += (out < lo); + + r[i] = out; + carry = hi; + } + return carry; +#endif +} + +uint64_t mp_addmul(uint64_t *r, const uint64_t *a, uint64_t b) { +#if defined(__SIZEOF_INT128__) + __uint128_t carry = 0; + for (int i = 0; i < MP_N64; i++) { + __uint128_t t = (__uint128_t)r[i] + (__uint128_t)a[i] * (__uint128_t)b + carry; + r[i] = (uint64_t)t; + carry = t >> 2*MP_N; + } + return (uint64_t)carry; +#else + uint64_t carry = 0; + for (int i = 0; i < MP_N64; i++) { + uint64_t lo, hi; + mp_mul64(a[i], b, lo, hi); + + // r[i] + lo + carry + uint64_t t = r[i] + lo; + uint64_t c1 = (t < r[i]); + uint64_t out = t + carry; + uint64_t c2 = (out < t); + + r[i] = out; + + // carry = hi + c1 + c2 + uint64_t c = hi; + c += c1; + c += c2; + carry = c; + } + return carry; +#endif +} + +void mp_and(uint64_t *r, const uint64_t *a, const uint64_t *b) { + for (int i = 0; i < MP_N64; i++) r[i] = a[i] & b[i]; +} + +void mp_or(uint64_t *r, const uint64_t *a, const uint64_t *b) { + for (int i = 0; i < MP_N64; i++) r[i] = a[i] | b[i]; +} + +void mp_xor(uint64_t *r, const uint64_t *a, const uint64_t *b) { + for (int i = 0; i < MP_N64; i++) r[i] = a[i] ^ b[i]; +} + +void mp_not(uint64_t *r, const uint64_t *a) { + for (int i = 0; i < MP_N64; i++) r[i] = ~a[i]; +} + +uint64_t mp_add(uint64_t *r, const uint64_t *a, size_t an, const uint64_t *b, size_t bn) { + size_t n = (an > bn) ? an : bn; + uint64_t carry = 0; + + for (size_t i = 0; i < n; i++) { + uint64_t ai = (i < an) ? a[i] : 0; + uint64_t bi = (i < bn) ? b[i] : 0; + carry = add_carry(&r[i], ai, bi, carry); + } + + return carry; +} + +static inline int mp_is_even(const uint64_t *a) { + return ((a[0] & 1ULL) == 0ULL); +} + +static inline bool mp_is_one(const uint64_t *a) { + if (a[0] != 1ULL) return false; + for (int i = 1; i < MP_N64; i++) { + if (a[i] != 0ULL) return false; + } + return true; +} + +static inline void mp_sub_mod(uint64_t *x, const uint64_t *y, const uint64_t *mod) { +#if MP_N64 == 4 //fast path + uint64_t t0, t1, t2, t3; + uint64_t br = 0; + + br = sub_borrow(&t0, x[0], y[0], br); + br = sub_borrow(&t1, x[1], y[1], br); + br = sub_borrow(&t2, x[2], y[2], br); + br = sub_borrow(&t3, x[3], y[3], br); + + if (br) { + uint64_t c = 0; + c = add_carry(&t0, t0, mod[0], c); + c = add_carry(&t1, t1, mod[1], c); + c = add_carry(&t2, t2, mod[2], c); + c = add_carry(&t3, t3, mod[3], c); + } + + x[0] = t0; x[1] = t1; x[2] = t2; x[3] = t3; +#else + uint64_t t[MP_N64]; + uint64_t br = 0; + + for (int i = 0; i < MP_N64; i++) { + br = sub_borrow(&t[i], x[i], y[i], br); + } + + if (br) { + uint64_t c = 0; + for (int i = 0; i < MP_N64; i++) { + c = add_carry(&t[i], t[i], mod[i], c); + } + } + + mp_copy(x, t); +#endif +} + +static inline void mp_div2_mod(uint64_t *x, const uint64_t *mod) { + uint64_t hi = 0; + + if (x[0] & 1ULL) { + uint64_t c = 0; + for (int i = 0; i < MP_N64; ++i) { + c = add_carry(&x[i], x[i], mod[i], c); + } + hi = c; + } + + for (int i = MP_N64 - 1; i >= 0; --i) { + uint64_t new_hi = x[i] & 1ULL; + x[i] = (x[i] >> 1) | (hi << (2*MP_N - 1)); + hi = new_hi; + } +} + +bool mp_inv_mod(uint64_t *r, const uint64_t *a, const uint64_t *mod) { + uint64_t u[MP_N64], v[MP_N64]; + mp_copy(u, a); + mp_copy(v, mod); + + if (mp_is_zero(u)) { mp_zero(r); return false; } + + uint64_t x1[MP_N64], x2[MP_N64]; + mp_set(x1, 1u); + mp_zero(x2); + + while (!mp_is_one(u) && !mp_is_one(v)) { + + while (mp_is_even(u)) { + mp_shr(u, u, 1u); + mp_div2_mod(x1, mod); + } + + while (mp_is_even(v)) { + mp_shr(v, v, 1u); + mp_div2_mod(x2, mod); + } + + if (mp_cmp(u, v) >= 0) { + mp_sub(u, u, v); + mp_sub_mod(x1, x2, mod); // x1 = x1 - x2 (mod) + } else { + mp_sub(v, v, u); + mp_sub_mod(x2, x1, mod); // x2 = x2 - x1 (mod) + } + } + + if (mp_is_one(u)) mp_copy(r, x1); + else mp_copy(r, x2); + return true; +} \ No newline at end of file diff --git a/src/mp.hpp.ejs b/src/mp.hpp.ejs new file mode 100644 index 0000000..74ed1bb --- /dev/null +++ b/src/mp.hpp.ejs @@ -0,0 +1,54 @@ +#ifndef MP_HPP +#define MP_HPP + +#include +#include +#include + +#define MP_N64 <%= n64 %> +#define MP_N 32 + +typedef uint64_t mp_uint_t[MP_N64]; + +void mp_set(uint64_t *r, uint64_t a); +void mp_copy(uint64_t *r, const uint64_t *a); + +int mp_cmp(const uint64_t *a, const uint64_t *b); +void mp_zero(uint64_t *r); +bool mp_is_zero(const uint64_t *a); + +uint64_t mp_add(uint64_t *r, const uint64_t *a, const uint64_t *b); +uint64_t mp_add(uint64_t *r, const uint64_t *a, size_t an, const uint64_t *b, size_t bn); +uint64_t mp_sub(uint64_t *r, const uint64_t *a, const uint64_t *b); + +uint64_t mp_add(uint64_t *r, const uint64_t *a, uint64_t b); +uint64_t mp_sub(uint64_t *r, const uint64_t *a, uint64_t b); + +uint64_t mp_mul(uint64_t *r, const uint64_t *a, uint64_t b); +uint64_t mp_addmul(uint64_t *r, const uint64_t *a, uint64_t b); + +void mp_and(uint64_t *r, const uint64_t *a, const uint64_t *b); +void mp_or(uint64_t *r, const uint64_t *a, const uint64_t *b); +void mp_xor(uint64_t *r, const uint64_t *a, const uint64_t *b); +void mp_not(uint64_t *r, const uint64_t *a); + +bool mp_tstbit(const uint64_t *a, size_t bit); +void mp_shl(uint64_t *r, const uint64_t *a, uint64_t k); +void mp_shr(uint64_t *r, const uint64_t *a, uint64_t k); + +int32_t mp_get_int32(const mp_uint_t a); +bool mp_fits_int32(const uint64_t *a); +bool mp_set(uint64_t *r, const char *str, uint32_t base); +std::string mp_get_str(const uint64_t *a, uint32_t base); + +void mp_set_mod(uint64_t *r, int64_t a, const uint64_t *mod); +bool mp_set_mod(uint64_t *r, const char *str, uint32_t base, const uint64_t *mod); + +void mp_export_be(uint8_t *r, const uint64_t *a); +void mp_import_be(uint64_t *r, const uint8_t *a); + +void mp_div(uint64_t *q, uint64_t *r, const uint64_t *num, const uint64_t *den); +void mp_pow_mod(uint64_t *r, const uint64_t *base, const uint64_t *exp, const uint64_t *mod); +bool mp_inv_mod(uint64_t *r, const uint64_t *a, const uint64_t *mod); + +#endif diff --git a/tasksfile.js b/tasksfile.js index af91d4f..f5e5a46 100644 --- a/tasksfile.js +++ b/tasksfile.js @@ -6,114 +6,248 @@ function cleanAll() { function downloadGoogleTest() { sh("mkdir -p build"); - sh("wget https://github.com/google/googletest/archive/release-1.10.0.tar.gz", {cwd: "build"}); - sh("tar xzf release-1.10.0.tar.gz", {cwd: "build"}); - sh("rm release-1.10.0.tar.gz", {cwd: "build"}); + sh("wget https://github.com/google/googletest/archive/release-1.10.0.tar.gz", { cwd: "build" }); + sh("tar xzf release-1.10.0.tar.gz", { cwd: "build" }); + sh("rm release-1.10.0.tar.gz", { cwd: "build" }); } function compileGoogleTest() { - sh("g++ -Igoogletest -Igoogletest/include -c googletest/src/gtest-all.cc", {cwd: "build/googletest-release-1.10.0"}); - sh("ar -rv libgtest.a gtest-all.o",{cwd: "build/googletest-release-1.10.0"}); + sh("g++ -Igoogletest -Igoogletest/include -c googletest/src/gtest-all.cc", { + cwd: "build/googletest-release-1.10.0", + nopipe: true + }); + sh("ar -rv libgtest.a gtest-all.o", { + cwd: "build/googletest-release-1.10.0", + nopipe: true + }); +} + +function createMpSources() { + sh("mkdir -p build"); + + const renderMpHpp = + String.raw`node -e "const fs=require('fs'); const ejs=require('ejs'); const path=require('path'); const data={ n64: 4 }; const tpl=fs.readFileSync(path.join('..','src','mp.hpp.ejs'),'utf8'); const out=ejs.render(tpl, data); fs.writeFileSync('mp.hpp', out);"`; + + const renderMpCpp = + String.raw`node -e "const fs=require('fs'); const ejs=require('ejs'); const path=require('path'); const data={ n64: 4 }; const tpl=fs.readFileSync(path.join('..','src','mp.cpp.ejs'),'utf8'); const out=ejs.render(tpl, data); fs.writeFileSync('mp.cpp', out);"`; + + sh(renderMpHpp, { cwd: "build", nopipe: true }); + sh(renderMpCpp, { cwd: "build", nopipe: true }); } function createFieldSources() { - sh("node ../src/buildzqfield.js -q 21888242871839275222246405745257275088696311157297823662689037894645226208583 -n Fq", {cwd: "build"}); - sh("node ../src/buildzqfield.js -q 21888242871839275222246405745257275088548364400416034343698204186575808495617 -n Fr", {cwd: "build"}); - - if (process.platform === "darwin") { - sh("nasm -fmacho64 --prefix _ fq.asm", {cwd: "build"}); - } else if (process.platform === "linux") { - sh("nasm -felf64 fq.asm", {cwd: "build"}); - } else throw("Unsupported platform"); - - if (process.platform === "darwin") { - sh("nasm -fmacho64 --prefix _ fr.asm", {cwd: "build"}); - } else if (process.platform === "linux") { - sh("nasm -felf64 fr.asm", {cwd: "build"}); - } else throw("Unsupported platform"); + sh("mkdir -p build"); + + sh( + "node ../src/buildzqfield.js -q 21888242871839275222246405745257275088696311157297823662689037894645226208583 -n Fq", + { cwd: "build", nopipe: true } + ); + sh( + "node ../src/buildzqfield.js -q 21888242871839275222246405745257275088548364400416034343698204186575808495617 -n Fr", + { cwd: "build", nopipe: true } + ); + + if (process.platform === "linux" && process.arch === "x64") { + sh("nasm -felf64 fq.asm -o fq_asm.o", { cwd: "build", nopipe: true }); + sh("nasm -felf64 fr.asm -o fr_asm.o", { cwd: "build", nopipe: true }); + } else if (process.platform === "darwin" && process.arch === "x64") { + sh("nasm -fmacho64 --prefix _ fq.asm -o fq_asm.o", { cwd: "build", nopipe: true }); + sh("nasm -fmacho64 --prefix _ fr.asm -o fr_asm.o", { cwd: "build", nopipe: true }); + } +} + +function getIncludeFlags(extra = []) { + const flags = [ + "-Igoogletest-release-1.10.0/googletest/include", + "-I.", + ...extra + ]; + return flags.join(" "); +} + +function getFieldSourcesForTest() { + if (process.platform === "darwin" && process.arch === "arm64") { + return [ + "fq.cpp", + "fq_generic.cpp", + "fq_raw_arm64.s", + //"fq_raw_generic.cpp", + "fr.cpp", + "fr_generic.cpp", + "fr_raw_arm64.s" + //"fr_raw_generic.cpp" + ]; + } + + if (process.arch === "x64") { + const asmExists = process.platform === "linux" || process.platform === "darwin"; + if (asmExists) { + return [ + "fq.cpp", + "fq_asm.o", + "fr.cpp", + "fr_asm.o" + ]; + } + } + + return [ + "fq.cpp", + "fq_generic.cpp", + "fq_raw_generic.cpp", + "fr.cpp", + "fr_generic.cpp", + "fr_raw_generic.cpp" + ]; +} + +function getFieldSourcesForBench() { + if (process.platform === "darwin" && process.arch === "arm64") { + return [ + "fq.cpp", + "fq_raw_arm64.s", + //"fq_raw_generic.cpp", + //"fq_generic.cpp", + "fr.cpp", + //"fr_generic.cpp", + "fr_raw_arm64.s" + //"fr_raw_generic.cpp" + + ]; + } + + if (process.arch === "x64") { + const asmExists = process.platform === "linux" || process.platform === "darwin"; + if (asmExists) { + return [ + "fq.cpp", + "fq_asm.o", + "fr.cpp", + "fr_asm.o" + ]; + } + } + + return [ + "fq.cpp", + "fq_generic.cpp", + "fq_raw_generic.cpp", + "fr.cpp", + "fr_generic.cpp", + "fr_raw_generic.cpp" + ]; +} + +function getOpenMpFlags() { + if (process.platform === "linux") { + return "-fopenmp"; + } + return ""; +} + +function getAsmDefines() { + if (process.platform === "darwin" && process.arch === "arm64") { + return "-DUSE_ASM -DARCH_ARM64"; + } + if (process.arch === "x64" && (process.platform === "darwin" || process.platform === "linux")) { + return "-DUSE_ASM -DARCH_X86_64"; + } + return ""; } function testSplitParStr() { - sh("g++" + - " -Igoogletest-release-1.10.0/googletest/include"+ - " -I."+ - " -I../src"+ - " ../c/splitparstr.cpp"+ - " ../c/splitparstr_test.cpp"+ - " googletest-release-1.10.0/libgtest.a"+ - " -pthread -std=c++17 -o splitparsestr_test", {cwd: "build", nopipe: true} - ); - sh("./splitparsestr_test", {cwd: "build", nopipe: true}); + const asmDefines = getAsmDefines(); + + const cmd = + "g++ " + + (asmDefines ? asmDefines + " " : "") + + getIncludeFlags(["-I../src"]) + + " ../c/splitparstr.cpp" + + " ../c/splitparstr_test.cpp" + + " googletest-release-1.10.0/libgtest.a" + + " -pthread -std=c++11 -o splitparsestr_test"; + + sh(cmd, { cwd: "build", nopipe: true }); + sh("./splitparsestr_test", { cwd: "build", nopipe: true }); } function testAltBn128() { - sh("g++" + - " -Igoogletest-release-1.10.0/googletest/include"+ - " -I."+ - " -I../c"+ - " ../c/naf.cpp"+ - " ../c/splitparstr.cpp"+ - " ../c/alt_bn128.cpp"+ - " ../c/alt_bn128_test.cpp"+ - " ../c/misc.cpp"+ - " fq.cpp"+ - " fq.o"+ - " fr.cpp"+ - " fr.o"+ - " googletest-release-1.10.0/libgtest.a"+ + const fieldSources = getFieldSourcesForTest().join(" "); + const asmDefines = getAsmDefines(); + + const cmd = + "g++ " + + //"-DCOUNT_OPS " + + (asmDefines ? asmDefines + " " : "") + + getIncludeFlags(["-I../c"]) + + " ../c/naf.cpp" + + " ../c/splitparstr.cpp" + + " ../c/alt_bn128.cpp" + + " ../c/alt_bn128_test.cpp" + + " ../c/misc.cpp" + + " mp.cpp" + + " " + fieldSources + + " googletest-release-1.10.0/libgtest.a" + " -o altbn128_test" + - " -fmax-errors=5 -pthread -std=c++17 -fopenmp -lgmp -g", {cwd: "build", nopipe: true} - ); - sh("./altbn128_test", {cwd: "build", nopipe: true}); -} + " -pthread -std=c++17 -g"; + sh(cmd, { cwd: "build", nopipe: true }); + sh("./altbn128_test", { cwd: "build", nopipe: true }); +} function benchMultiExpG1() { - sh("g++ -O3 -g" + - " -Igoogletest-release-1.10.0/googletest/include"+ - " -I."+ - " -I../c"+ - " ../c/naf.cpp"+ - " ../c/splitparstr.cpp"+ - " ../c/alt_bn128.cpp"+ - " ../c/misc.cpp"+ - " ../benchmark/multiexp_g1.cpp"+ - " fq.cpp"+ - " fq.o"+ - " fr.cpp"+ - " fr.o"+ - // " googletest-release-1.10.0/libgtest.a"+ - " -o multiexp_g1_benchmark" + - " -lgmp -pthread -std=c++17 -fopenmp" , {cwd: "build", nopipe: true} - ); - sh("./multiexp_g1_benchmark 16777216", {cwd: "build", nopipe: true}); + const fieldSources = getFieldSourcesForBench().join(" "); + const omp = getOpenMpFlags(); + const asmDefines = getAsmDefines(); + + const cmd = + "g++ -O3 -g " + + (asmDefines ? asmDefines + " " : "") + + getIncludeFlags(["-I../c"]) + + " ../c/naf.cpp" + + " ../c/splitparstr.cpp" + + " ../c/alt_bn128.cpp" + + " ../c/misc.cpp" + + " ../benchmark/multiexp_g1.cpp" + + " mp.cpp" + + " " + fieldSources + + " -o multiexp_g1_benchmark " + + " -pthread -std=c++11" + + (omp ? " " + omp : ""); + + sh(cmd, { cwd: "build", nopipe: true }); + sh("./multiexp_g1_benchmark 16777216", { cwd: "build", nopipe: true }); } function benchMultiExpG2() { - sh("g++" + - " -Igoogletest-release-1.10.0/googletest/include"+ - " -I."+ - " -I../c"+ - " ../c/naf.cpp"+ - " ../c/splitparstr.cpp"+ - " ../c/alt_bn128.cpp"+ - " ../c/misc.cpp"+ - " ../benchmark/multiexp_g2.cpp"+ - " fq.cpp"+ - " fq.o"+ - " fr.cpp"+ - " fr.o"+ - // " googletest-release-1.10.0/libgtest.a"+ - " -o multiexp_g2_benchmark" + - " -lgmp -pthread -std=c++17 -fopenmp" , {cwd: "build", nopipe: true} - ); - sh("./multiexp_g2_benchmark 16777216", {cwd: "build", nopipe: true}); + const fieldSources = getFieldSourcesForBench().join(" "); + const omp = getOpenMpFlags(); + const asmDefines = getAsmDefines(); + + const cmd = + "g++ -O3 -g " + + (asmDefines ? asmDefines + " " : "") + + getIncludeFlags(["-I../c"]) + + " ../c/naf.cpp" + + " ../c/splitparstr.cpp" + + " ../c/alt_bn128.cpp" + + " ../c/misc.cpp" + + " ../benchmark/multiexp_g2.cpp" + + " mp.cpp" + + " " + fieldSources + + " -o multiexp_g2_benchmark " + + " -pthread -std=c++11" + + (omp ? " " + omp : ""); + + sh(cmd, { cwd: "build", nopipe: true }); + sh("./multiexp_g2_benchmark 16777216", { cwd: "build", nopipe: true }); } cli({ cleanAll, downloadGoogleTest, compileGoogleTest, + createMpSources, createFieldSources, testSplitParStr, testAltBn128, diff --git a/test/fieldasm.js b/test/fieldasm.js index 27f2798..44be7cb 100644 --- a/test/fieldasm.js +++ b/test/fieldasm.js @@ -111,8 +111,14 @@ describe("field asm test", function () { } - generateTest(bn128r, "gl"); - generateTest(bn128r, "bn128"); + generateTest(gl, "gl"); + generateTest(bn128q, "bn128q"); + generateTest(bn128r, "bn128r"); + generateTest(bls12_381q, "bls12_381q"); + generateTest(secp256k1q, "secp256k1q"); + generateTest(secp256k1r, "secp256k1r"); + generateTest(mnt6753q, "mnt6753q"); + generateTest(mnt6753r, "mnt6753r"); }); function buildTestVector2(p, op) { diff --git a/test/tester/buildzqfieldtester.js b/test/tester/buildzqfieldtester.js index d576fbf..ce11821 100644 --- a/test/tester/buildzqfieldtester.js +++ b/test/tester/buildzqfieldtester.js @@ -2,51 +2,85 @@ const chai = require("chai"); const assert = chai.assert; const fs = require("fs"); -var tmp = require("tmp-promise"); +const tmp = require("tmp-promise"); const path = require("path"); const util = require("util"); +const ejs = require("ejs"); const exec = util.promisify(require("child_process").exec); const buildZqField = require("../../index.js").buildZqField; module.exports = testField; -async function testField(prime, test) { +async function renderTemplate(srcPath, data) { + const tpl = await fs.promises.readFile(srcPath, "utf8"); + return ejs.render(tpl, data); +} + +async function testField(prime, test, options = {}) { tmp.setGracefulCleanup(); - const dir = await tmp.dir({prefix: "ffiasm_", unsafeCleanup: true }); + const dir = await tmp.dir({ prefix: "ffiasm_", unsafeCleanup: true }); + const useAsm = options.useAsm !== false; const source = await buildZqField(prime, "Fr"); - // console.log(dir.path); - await fs.promises.writeFile(path.join(dir.path, "fr.asm"), source.asm, "utf8"); await fs.promises.writeFile(path.join(dir.path, "fr.hpp"), source.hpp, "utf8"); await fs.promises.writeFile(path.join(dir.path, "fr.cpp"), source.cpp, "utf8"); - - await exec(`cp ${path.join(__dirname, "tester.cpp")} ${dir.path}`); - - if (process.platform === "darwin") { - await exec("nasm -fmacho64 --prefix _ " + - ` ${path.join(dir.path, "fr.asm")}` + await fs.promises.writeFile(path.join(dir.path, "fr_element.hpp"), source.element_hpp, "utf8"); + await fs.promises.writeFile(path.join(dir.path, "fr_generic.cpp"), source.generic_cpp, "utf8"); + await fs.promises.writeFile(path.join(dir.path, "fr_raw_generic.cpp"), source.raw_generic_cpp, "utf8"); + await fs.promises.writeFile(path.join(dir.path, "fr_raw_arm64.s"), source.raw_arm64_s, "utf8"); + + const mpData = { n64: source.n64 }; + //console.log("prime bits =", prime.bitLength().toString()); + //console.log("source.n64 =", source.n64); + //console.log("mpData =", mpData); + const mpHpp = await renderTemplate(path.join(__dirname, "..", "..", "src", "mp.hpp.ejs"), mpData); + //console.log(mpHpp.match(/#define MP_N64 .*/)[0]); + const mpCpp = await renderTemplate(path.join(__dirname, "..", "..", "src", "mp.cpp.ejs"), mpData); + + await fs.promises.writeFile(path.join(dir.path, "mp.hpp"), mpHpp, "utf8"); + await fs.promises.writeFile(path.join(dir.path, "mp.cpp"), mpCpp, "utf8"); + + await exec(`cp ${path.join(__dirname, "tester.cpp")} ${dir.path}`); + + let defines = []; + let sources = [ + path.join(dir.path, "tester.cpp"), + path.join(dir.path, "mp.cpp"), + path.join(dir.path, "fr.cpp") + ]; + + if (useAsm && process.platform === "darwin" && process.arch === "arm64") { + defines.push("-DUSE_ASM", "-DARCH_ARM64"); + sources.push( + path.join(dir.path, "fr_generic.cpp"), + path.join(dir.path, "fr_raw_arm64.s") ); - } else if (process.platform === "linux") { - await exec("nasm -felf64 " + - ` ${path.join(dir.path, "fr.asm")}` + } else { + sources.push( + path.join(dir.path, "fr_generic.cpp"), + path.join(dir.path, "fr_raw_generic.cpp") ); - } else throw("Unsupported platform"); - - await exec("g++" + - ` ${path.join(dir.path, "tester.cpp")}` + - ` ${path.join(dir.path, "fr.o")}` + - ` ${path.join(dir.path, "fr.cpp")}` + - ` -o ${path.join(dir.path, "tester")}` + - " -lgmp -g" - ); + } + + const compileCmd = [ + "g++", + ...defines, + `-I${dir.path}`, + ...sources, + "-o", path.join(dir.path, "tester"), + "-std=c++17", + "-g" + ].join(" "); + + await exec(compileCmd); const inLines = []; - for (let i=0; i${path.join(dir.path, "out.tst")}`); + await exec( + `${path.join(dir.path, "tester")} <${path.join(dir.path, "in.tst")} >${path.join(dir.path, "out.tst")}` + ); const res = await fs.promises.readFile(path.join(dir.path, "out.tst"), "utf8"); const resLines = res.split("\n"); - for (let i=0; i &v) { u_int64_t a; if ((v.size()<1) || (v.size() > (Fr_N64+1))) { - printf("Invalid Size: %d - %d \n", v.size(), Fr_N64); + printf("Invalid Size: %zu - %d \n", v.size(), Fr_N64); throw std::runtime_error("Invalid number of parameters for number"); } FrElement e; @@ -155,10 +155,9 @@ int main(void) // Print the elements in the stack // for (int i=0; i Date: Mon, 16 Mar 2026 02:48:21 +0200 Subject: [PATCH 09/13] nogmp: fix addmul logic, update *_raw_generic w/ it --- src/fr_raw_generic.cpp.ejs | 34 +++++++++++++++++----------------- src/mp.cpp.ejs | 24 ++++++++++++++---------- src/mp.hpp.ejs | 2 +- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/fr_raw_generic.cpp.ejs b/src/fr_raw_generic.cpp.ejs index f14fb20..a6f8075 100644 --- a/src/fr_raw_generic.cpp.ejs +++ b/src/fr_raw_generic.cpp.ejs @@ -103,26 +103,26 @@ int <%=name%>_rawIsEq(const <%=name%>RawElement pRawA, const <%=name%>RawElement void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB) { constexpr size_t N = <%=name%>_N64 + 1; - const uint64_t *mq = <%=name%>_rawq; + const uint64_t *mq = <%=name%>_rawq; - uint64_t np0; + uint64_t np0; <% for (let i=0; i - uint64_t product<%=i%>[N] = {0}; + uint64_t product<%=i%>[N] = {0}; <% } %> product0[<%=n64%>] = mp_mul(product0, pRawB, pRawA[0]); <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; - product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, np0); + product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); - product<%=i+1%>[<%=n64%>] = mp_addmul(product<%=i+1%>, pRawB, pRawA[<%=i+1%>]); + product<%=i+1%>[<%=n64%>] = mp_addmul(product<%=i+1%>, pRawB, <%=name%>_N64, pRawA[<%=i+1%>]); mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; - mp_addmul(product<%=n64-1%>, mq, np0); + mp_addmul(product<%=n64-1%>, mq, N, np0); mp_copy(pRawResult, product<%=n64-1%>+1); @@ -135,24 +135,24 @@ void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, uint64_t pRawB) { constexpr size_t N = <%=name%>_N64 + 1; - const uint64_t *mq = <%=name%>_rawq; + const uint64_t *mq = <%=name%>_rawq; - uint64_t np0; + uint64_t np0; <% for (let i=0; i - uint64_t product<%=i%>[N] = {0}; + uint64_t product<%=i%>[N] = {0}; <% } %> product0[<%=n64%>] = mp_mul(product0, pRawA, pRawB); <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; - product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, np0); + product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; - mp_addmul(product<%=n64-1%>, mq, np0); + mp_addmul(product<%=n64-1%>, mq, N, np0); mp_copy(pRawResult, product<%=n64-1%>+1); @@ -165,25 +165,25 @@ void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElemen void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%>RawElement &pRawA) { constexpr size_t N = <%=name%>_N64 + 1; - const uint64_t *mq = <%=name%>_rawq; + const uint64_t *mq = <%=name%>_rawq; - uint64_t np0; + uint64_t np0; - uint64_t product0[N]; + uint64_t product0[N]; <% for (let i=1; i - uint64_t product<%=i%>[N] = {0}; + uint64_t product<%=i%>[N] = {0}; <% } %> mp_copy(product0, pRawA); product0[<%=n64%>] = 0; <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; - product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, np0); + product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; - mp_addmul(product<%=n64-1%>, mq, np0); + mp_addmul(product<%=n64-1%>, mq, N, np0); mp_copy(pRawResult, product<%=n64-1%>+1); diff --git a/src/mp.cpp.ejs b/src/mp.cpp.ejs index 24b0a8c..2f1c0e9 100644 --- a/src/mp.cpp.ejs +++ b/src/mp.cpp.ejs @@ -1217,36 +1217,40 @@ uint64_t mp_mul(uint64_t *r, const uint64_t *a, uint64_t b) { #endif } -uint64_t mp_addmul(uint64_t *r, const uint64_t *a, uint64_t b) { +uint64_t mp_addmul(uint64_t *r, const uint64_t *a, size_t n, uint64_t b) +{ #if defined(__SIZEOF_INT128__) __uint128_t carry = 0; - for (int i = 0; i < MP_N64; i++) { - __uint128_t t = (__uint128_t)r[i] + (__uint128_t)a[i] * (__uint128_t)b + carry; + + for (size_t i = 0; i < n; i++) { + __uint128_t t = (__uint128_t)r[i] + + (__uint128_t)a[i] * (__uint128_t)b + + carry; r[i] = (uint64_t)t; carry = t >> 2*MP_N; } + return (uint64_t)carry; #else uint64_t carry = 0; - for (int i = 0; i < MP_N64; i++) { + + for (size_t i = 0; i < n; i++) { uint64_t lo, hi; mp_mul64(a[i], b, lo, hi); // r[i] + lo + carry uint64_t t = r[i] + lo; uint64_t c1 = (t < r[i]); + uint64_t out = t + carry; uint64_t c2 = (out < t); r[i] = out; - - // carry = hi + c1 + c2 - uint64_t c = hi; - c += c1; - c += c2; - carry = c; + carry = hi + c1 + c2; } + return carry; +} #endif } diff --git a/src/mp.hpp.ejs b/src/mp.hpp.ejs index 74ed1bb..d9f4bd2 100644 --- a/src/mp.hpp.ejs +++ b/src/mp.hpp.ejs @@ -25,7 +25,7 @@ uint64_t mp_add(uint64_t *r, const uint64_t *a, uint64_t b); uint64_t mp_sub(uint64_t *r, const uint64_t *a, uint64_t b); uint64_t mp_mul(uint64_t *r, const uint64_t *a, uint64_t b); -uint64_t mp_addmul(uint64_t *r, const uint64_t *a, uint64_t b); +uint64_t mp_addmul(uint64_t *r, const uint64_t *a, size_t n, uint64_t b); void mp_and(uint64_t *r, const uint64_t *a, const uint64_t *b); void mp_or(uint64_t *r, const uint64_t *a, const uint64_t *b); From b22fe78787e3c71a5b7c8b015ca2a56b8c3b785a Mon Sep 17 00:00:00 2001 From: furze Date: Tue, 17 Mar 2026 21:35:14 +0200 Subject: [PATCH 10/13] nogmp: fix 1 limb fields gen, testgen; mp_div optimise --- c/alt_bn128_test.cpp | 16 +-- src/fr_generic.cpp.ejs | 39 ++++--- src/fr_raw_generic.cpp.ejs | 38 +++--- src/mp.cpp.ejs | 184 ++++++++++++++++++++++++------ tasksfile.js | 3 +- test/tester/buildzqfieldtester.js | 4 +- 6 files changed, 203 insertions(+), 81 deletions(-) diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp index 7018729..27a9f29 100644 --- a/c/alt_bn128_test.cpp +++ b/c/alt_bn128_test.cpp @@ -219,8 +219,8 @@ TEST(altBn128, g1_times_5) { ASSERT_TRUE(G2.isZero(p1)); } -/* -TEST(altBn128, multiExp) { + +TEST(altBn128, multiExp_old) { int NMExp = 40000; typedef mp_uint_t Scalar; @@ -259,7 +259,7 @@ TEST(altBn128, multiExp) { delete[] bases; delete[] scalars; } -*/ + TEST(altBn128, multiExp) { int NMExp = 40000; @@ -303,8 +303,8 @@ TEST(altBn128, multiExp) { delete[] bases; delete[] scalars; } -/* -TEST(altBn128, multiExpMSM) { + +TEST(altBn128, multiExpMSM_old) { int NMExp = 40000; typedef mp_uint_t Scalar; @@ -343,7 +343,7 @@ TEST(altBn128, multiExpMSM) { delete[] bases; delete[] scalars; } -*/ + TEST(altBn128, multiExpMSM) { int NMExp = 40000; @@ -529,7 +529,7 @@ TEST(altBn128, fft) { delete[] a; } -/* + using perf_clock = std::chrono::steady_clock; static inline double bench_ms(std::function fn) { @@ -744,7 +744,7 @@ TEST(altBn128Perf, prepare_breakdown_nogmp) { delete[] bases; delete[] scalars; } -*/ + } // namespace int main(int argc, char **argv) { diff --git a/src/fr_generic.cpp.ejs b/src/fr_generic.cpp.ejs index ccd5668..8d63429 100644 --- a/src/fr_generic.cpp.ejs +++ b/src/fr_generic.cpp.ejs @@ -1,12 +1,14 @@ #include "<%=name.toLowerCase()+".hpp"%>" #include #include +#include <%=name%>Element <%=name%>_q = {0, 0x80000000, {<%= constantElement(q) %>}}; <%=name%>Element <%=name%>_R2 = {0, 0x80000000, {<%= constantElement(bigInt.one.shiftLeft(n64*64*2).mod(q)) %>}}; <%=name%>Element <%=name%>_R3 = {0, 0x80000000, {<%= constantElement(bigInt.one.shiftLeft(n64*64*3).mod(q)) %>}}; static <%=name%>RawElement half = {<%= constantElement(q.shiftRight(1)) %>}; +static <%=name%>RawElement zero = {0}; static <%=name%>RawElement <%=name%>_rawR2 = {<%= constantElement(bigInt.one.shiftLeft(n64*64*2).mod(q)) %>}; void <%=name%>_rawMSquare(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA) @@ -318,7 +320,7 @@ void <%=name%>_toMontgomery(P<%=name%>Element r, P<%=name%>Element a) } else if (a->shortVal < 0) { - int64_t a_shortVal = a->shortVal; + int64_t a_shortVal = a->shortVal; <%=name%>_rawMMul1(r->longVal, <%=name%>_R2.longVal, -a_shortVal); <%=name%>_rawNeg(r->longVal, r->longVal); @@ -337,7 +339,6 @@ void <%=name%>_copyn(P<%=name%>Element r, P<%=name%>Element a, int n) std::memcpy(r, a, n * sizeof(<%=name%>Element)); } - static inline int has_add32_overflow(int64_t val) { int64_t signs = (val >> 31) & 0x3; @@ -779,7 +780,7 @@ int <%=name%>_longNormal(P<%=name%>Element pE) return <%=name%>_longNeg(pE); } - if (pE->longVal[1] || pE->longVal[2] || pE->longVal[3]) + if (memcmp(&pE->longVal[1], zero, (sizeof(pE->longVal) - sizeof(pE->longVal[0])))) { return <%=name%>_longNeg(pE); } @@ -1509,7 +1510,7 @@ static inline void and_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; + a_n = {0, 0, {(uint64_t)a->shortVal}}; } else { @@ -1530,7 +1531,7 @@ static inline void and_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; + b_n = {0, 0, {(uint64_t)b->shortVal}}; } else { @@ -1551,7 +1552,7 @@ static inline void and_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; + a_n = {0, 0, {(uint64_t)a->shortVal}}; } else { @@ -1569,7 +1570,7 @@ static inline void and_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; + b_n = {0, 0, {(uint64_t)b->shortVal}}; } else { @@ -1801,7 +1802,7 @@ static inline void do_shr(P<%=name%>Element r, P<%=name%>Element a, uint64_t b) static inline void <%=name%>_shr_big_shift(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - static <%=name%>RawElement max_shift = {<%= q.bitLength() %>, 0, 0, 0}; + static <%=name%>RawElement max_shift = {<%= q.bitLength() %>}; <%=name%>RawElement shift; @@ -1819,7 +1820,7 @@ static inline void <%=name%>_shr_big_shift(P<%=name%>Element r, P<%=name%>Elemen static inline void <%=name%>_shr_long(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - static <%=name%>RawElement max_shift = {<%= q.bitLength() %>, 0, 0, 0}; + static <%=name%>RawElement max_shift = {<%= q.bitLength() %>}; if (<%=name%>_rawCmp(b->longVal, max_shift) >= 0) { @@ -1877,7 +1878,7 @@ void <%=name%>_shr(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b static inline void <%=name%>_shl_big_shift(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - static <%=name%>RawElement max_shift = {<%= q.bitLength() %>, 0, 0, 0}; + static <%=name%>RawElement max_shift = {<%= q.bitLength() %>}; <%=name%>RawElement shift; @@ -1895,7 +1896,7 @@ static inline void <%=name%>_shl_big_shift(P<%=name%>Element r, P<%=name%>Elemen static inline void <%=name%>_shl_long(P<%=name%>Element r, P<%=name%>Element a, P<%=name%>Element b) { - static <%=name%>RawElement max_shift = {<%= q.bitLength() %>, 0, 0, 0}; + static <%=name%>RawElement max_shift = {<%= q.bitLength() %>}; if (<%=name%>_rawCmp(b->longVal, max_shift) >= 0) { @@ -2024,7 +2025,7 @@ static inline void or_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name%> if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; + a_n = {0, 0, {(uint64_t)a->shortVal}}; } else { @@ -2042,7 +2043,7 @@ static inline void or_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name%> if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; + a_n = {0, 0, {(uint64_t)a->shortVal}}; } else { @@ -2060,7 +2061,7 @@ static inline void or_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%> if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; + b_n = {0, 0, {(uint64_t)b->shortVal}}; } else { @@ -2081,7 +2082,7 @@ static inline void or_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name%> if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; + b_n = {0, 0, {(uint64_t)b->shortVal}}; } else { @@ -2211,7 +2212,7 @@ static inline void xor_s1l2n(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; + a_n = {0, 0, {(uint64_t)a->shortVal}}; } else { @@ -2232,7 +2233,7 @@ static inline void xor_s1l2m(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (a->shortVal >= 0) { - a_n = {0, 0, {(uint64_t)a->shortVal, 0, 0, 0}}; + a_n = {0, 0, {(uint64_t)a->shortVal}}; } else { @@ -2250,7 +2251,7 @@ static inline void xor_l1ns2(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; + b_n = {0, 0, {(uint64_t)b->shortVal}}; } else { @@ -2271,7 +2272,7 @@ static inline void xor_l1ms2(P<%=name%>Element r, P<%=name%>Element a, P<%=name% if (b->shortVal >= 0) { - b_n = {0, 0, {(uint64_t)b->shortVal, 0, 0, 0}}; + b_n = {0, 0, {(uint64_t)b->shortVal}}; } else { diff --git a/src/fr_raw_generic.cpp.ejs b/src/fr_raw_generic.cpp.ejs index a6f8075..d30b254 100644 --- a/src/fr_raw_generic.cpp.ejs +++ b/src/fr_raw_generic.cpp.ejs @@ -3,15 +3,15 @@ #include static uint64_t <%=name%>_rawq[] = {<%= constantElement(q) %>, 0}; -static const uint64_t <%=name%>_np = 0x<%= (bigInt.one.shiftLeft(64)).minus(q.modInv(bigInt.one.shiftLeft(64))).toString(16) %>ULL; -static uint64_t lboMask = 0x<%= bigInt("10000000000000000",16).shiftRight(n64*64 - q.bitLength()).minus(bigInt.one).toString(16) %>; +static const uint64_t <%=name%>_np = 0x<%= (bigInt.one.shiftLeft(64)).minus(q.modInv(bigInt.one.shiftLeft(64))).toString(16) %>ULL; +static uint64_t lboMask = 0x<%= bigInt("10000000000000000",16).shiftRight(n64*64 - q.bitLength()).minus(bigInt.one).toString(16) %>; void <%=name%>_rawAdd(<%=name%>RawElement pRawResult, const <%=name%>RawElement pRawA, const <%=name%>RawElement pRawB) { uint64_t carry = mp_add(pRawResult, pRawA, pRawB); - if(carry || mp_cmp(pRawResult, <%=name%>_rawq) >= 0) + if (carry || mp_cmp(pRawResult, <%=name%>_rawq) >= 0) { mp_sub(pRawResult, pRawResult, <%=name%>_rawq); } @@ -21,7 +21,7 @@ void <%=name%>_rawAddLS(<%=name%>RawElement pRawResult, <%=name%>RawElement pRaw { uint64_t carry = mp_add(pRawResult, pRawA, rawB); - if(carry || mp_cmp(pRawResult, <%=name%>_rawq) >= 0) + if (carry || mp_cmp(pRawResult, <%=name%>_rawq) >= 0) { mp_sub(pRawResult, pRawResult, <%=name%>_rawq); } @@ -31,7 +31,7 @@ void <%=name%>_rawSub(<%=name%>RawElement pRawResult, const <%=name%>RawElement { uint64_t carry = mp_sub(pRawResult, pRawA, pRawB); - if(carry) + if (carry) { mp_add(pRawResult, pRawResult, <%=name%>_rawq); } @@ -44,11 +44,12 @@ void <%=name%>_rawSubRegular(<%=name%>RawElement pRawResult, <%=name%>RawElement void <%=name%>_rawSubSL(<%=name%>RawElement pRawResult, uint64_t rawA, <%=name%>RawElement pRawB) { - <%=name%>RawElement pRawA = {rawA, 0, 0, 0}; + <%=name%>RawElement pRawA = {0}; + pRawA[0] = rawA; uint64_t carry = mp_sub(pRawResult, pRawA, pRawB); - if(carry) + if (carry) { mp_add(pRawResult, pRawResult, <%=name%>_rawq); } @@ -58,7 +59,7 @@ void <%=name%>_rawSubLS(<%=name%>RawElement pRawResult, <%=name%>RawElement pRaw { uint64_t carry = mp_sub(pRawResult, pRawA, rawB); - if(carry) + if (carry) { mp_add(pRawResult, pRawResult, <%=name%>_rawq); } @@ -76,7 +77,7 @@ void <%=name%>_rawNeg(<%=name%>RawElement pRawResult, const <%=name%>RawElement } } -// Substracts a long element and a short element form 0 +// Subtracts a long element and a short element from 0 void <%=name%>_rawNegLS(<%=name%>RawElement pRawResult, <%=name%>RawElement pRawA, uint64_t rawB) { uint64_t carry1 = mp_sub(pRawResult, <%=name%>_rawq, rawB); @@ -118,13 +119,13 @@ void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); product<%=i+1%>[<%=n64%>] = mp_addmul(product<%=i+1%>, pRawB, <%=name%>_N64, pRawA[<%=i+1%>]); - mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); + mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; mp_addmul(product<%=n64-1%>, mq, N, np0); - mp_copy(pRawResult, product<%=n64-1%>+1); + mp_copy(pRawResult, product<%=n64-1%> + 1); if (mp_cmp(pRawResult, mq) >= 0) { @@ -148,13 +149,13 @@ void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElemen <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); - mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); + mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; mp_addmul(product<%=n64-1%>, mq, N, np0); - mp_copy(pRawResult, product<%=n64-1%>+1); + mp_copy(pRawResult, product<%=n64-1%> + 1); if (mp_cmp(pRawResult, mq) >= 0) { @@ -174,18 +175,19 @@ void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%> uint64_t product<%=i%>[N] = {0}; <% } %> - mp_copy(product0, pRawA); product0[<%=n64%>] = 0; + mp_copy(product0, pRawA); + product0[<%=n64%>] = 0; <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); - mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%>+1, N-1); + mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; mp_addmul(product<%=n64-1%>, mq, N, np0); - mp_copy(pRawResult, product<%=n64-1%>+1); + mp_copy(pRawResult, product<%=n64-1%> + 1); if (mp_cmp(pRawResult, mq) >= 0) { @@ -230,7 +232,7 @@ void <%=name%>_rawCopyS2L(<%=name%>RawElement pRawResult, int64_t val) if (val < 0) { <% for (let i=1; i - pRawResult[<%=i%>] = -1; + pRawResult[<%=i%>] = (uint64_t)-1; <% } %> mp_add(pRawResult, pRawResult, <%=name%>_rawq); @@ -280,7 +282,9 @@ void <%=name%>_rawShl(<%=name%>RawElement r, <%=name%>RawElement a, uint64_t b) r[<%=n64-1%>] &= lboMask; if (mp_cmp(r, <%=name%>_rawq) >= 0) + { mp_sub(r, r, <%=name%>_rawq); + } } void <%=name%>_rawShr(<%=name%>RawElement r, <%=name%>RawElement a, uint64_t b) diff --git a/src/mp.cpp.ejs b/src/mp.cpp.ejs index 2f1c0e9..ecbe89e 100644 --- a/src/mp.cpp.ejs +++ b/src/mp.cpp.ejs @@ -541,18 +541,33 @@ static inline uint64_t div_2by1(uint64_t *q, uint64_t hi, uint64_t lo, uint64_t uint64_t qq = 0; uint64_t rem = hi; - for (int bit = (2 * MP_N) - 1; bit >= 0; --bit) { - // Shift remainder left by 1 and bring next bit from lo. - uint64_t overflow = rem >> ((2 * MP_N) - 1); - rem = (rem << 1) | ((lo >> bit) & 1ULL); - - // If previous top bit was 1, actual shifted remainder was >= 2^64. - // Since rem_before < v, shifted remainder < 2*v + 1, so one subtraction is enough. - if (overflow || rem >= v) { - rem -= v; - qq |= (1ULL << bit); - } - } +#define DIVSTEP(bit) \ +do { \ +const uint64_t overflow = rem >> 63; \ +const uint64_t rem2 = (rem << 1) | ((lo >> (bit)) & 1ULL); \ +const uint64_t ge = (uint64_t)(overflow | (uint64_t)(rem2 >= v)); \ +rem = rem2 - (v & (0ULL - ge)); \ +qq |= (ge << (bit)); \ +} while (0) + + DIVSTEP(63); DIVSTEP(62); DIVSTEP(61); DIVSTEP(60); + DIVSTEP(59); DIVSTEP(58); DIVSTEP(57); DIVSTEP(56); + DIVSTEP(55); DIVSTEP(54); DIVSTEP(53); DIVSTEP(52); + DIVSTEP(51); DIVSTEP(50); DIVSTEP(49); DIVSTEP(48); + DIVSTEP(47); DIVSTEP(46); DIVSTEP(45); DIVSTEP(44); + DIVSTEP(43); DIVSTEP(42); DIVSTEP(41); DIVSTEP(40); + DIVSTEP(39); DIVSTEP(38); DIVSTEP(37); DIVSTEP(36); + DIVSTEP(35); DIVSTEP(34); DIVSTEP(33); DIVSTEP(32); + DIVSTEP(31); DIVSTEP(30); DIVSTEP(29); DIVSTEP(28); + DIVSTEP(27); DIVSTEP(26); DIVSTEP(25); DIVSTEP(24); + DIVSTEP(23); DIVSTEP(22); DIVSTEP(21); DIVSTEP(20); + DIVSTEP(19); DIVSTEP(18); DIVSTEP(17); DIVSTEP(16); + DIVSTEP(15); DIVSTEP(14); DIVSTEP(13); DIVSTEP(12); + DIVSTEP(11); DIVSTEP(10); DIVSTEP(9); DIVSTEP(8); + DIVSTEP(7); DIVSTEP(6); DIVSTEP(5); DIVSTEP(4); + DIVSTEP(3); DIVSTEP(2); DIVSTEP(1); DIVSTEP(0); + +#undef DIVSTEP *q = qq; return rem; @@ -652,6 +667,64 @@ static inline uint64_t add_back_knuth(uint64_t *u, const uint64_t *v, int n) #endif } +static inline uint64_t mp_invert_limb_norm(uint64_t d) +{ +#if defined(__SIZEOF_INT128__) + // d is normalized (top bit set). Use a GMP-style 64-bit preinverse: + // floor((B^2 - 1)/d) - B, B = 2^64. + const __uint128_t B = ((__uint128_t)1) << 64; + return (uint64_t)((((B * B) - 1) / d) - B); +#else + return 0; // fallback path does not use preinverse +#endif +} + +static inline uint64_t mp_udiv_qrnnd_preinv( + uint64_t *r, + uint64_t nh, uint64_t nl, + uint64_t d, + uint64_t dinv) +{ +#if defined(__SIZEOF_INT128__) + const __uint128_t n = ((__uint128_t)nh << 64) | (__uint128_t)nl; + + // q ~= high(nh * dinv + nl) + nh + __uint128_t t = (__uint128_t)nh * (__uint128_t)dinv + (__uint128_t)nl; + uint64_t q = (uint64_t)(t >> 64) + nh; + + if (q == 0 || nh >= d) q = UINT64_MAX; + + __uint128_t prod = (__uint128_t)q * (__uint128_t)d; + + if (prod > n) { + --q; + prod -= d; + if (prod > n) { + --q; + prod -= d; + } + } else { + __uint128_t prod2 = prod + d; + if (prod2 <= n) { + ++q; + prod = prod2; + prod2 += d; + if (prod2 <= n) { + ++q; + prod = prod2; + } + } + } + + *r = (uint64_t)(n - prod); + return q; +#else + uint64_t q; + *r = div_2by1(&q, nh, nl, d); + return q; +#endif +} + void mp_div(uint64_t *q, uint64_t *r, const uint64_t *num, const uint64_t *den) { if (mp_is_zero(den)) { mp_zero(q); @@ -726,7 +799,13 @@ void mp_div(uint64_t *q, uint64_t *r, const uint64_t *num, const uint64_t *den) for (int j = qn - 1; j >= 0; --j) { uint64_t qhat, rhat; - rhat = div_2by1(&qhat, unorm[j + n], unorm[j + n - 1], v1); + + if (unorm[j + n] == v1) { + qhat = UINT64_MAX; + rhat = unorm[j + n - 1] + v1; + } else { + rhat = div_2by1(&qhat, unorm[j + n], unorm[j + n - 1], v1); + } for (;;) { uint64_t lo, hi; @@ -833,42 +912,82 @@ void mp_div(uint64_t *q, uint64_t *r, const uint64_t *num, const uint64_t *den) const uint64_t v1 = vnorm[n - 1]; const uint64_t v2 = vnorm[n - 2]; - - for (int j = qn - 1; j >= 0; j--) { + // Preinverse only pays off reliably for genuinely multi-limb quotient paths + // and larger normalized divisors. For very small n (especially n == 2), the + // exact 128/64 estimate is both safer and often faster on our fixed-size use + // cases. + const bool use_preinv = false; + const uint64_t v1_inv = 0; + + if (qn == 1) { uint64_t qhat; __uint128_t rhat; - if (unorm[j + n] == v1) { + if (unorm[n] == v1) { qhat = UINT64_MAX; - rhat = (__uint128_t)unorm[j + n - 1] + (__uint128_t)v1; + rhat = (__uint128_t)unorm[n - 1] + (__uint128_t)v1; } else { - __uint128_t uj2 = - (__uint128_t)unorm[j + n] << (2*MP_N) | - (__uint128_t)unorm[j + n - 1]; - - qhat = (uint64_t)(uj2 / v1); - rhat = uj2 % v1; + __uint128_t num2 = ((__uint128_t)unorm[n] << 64) | (__uint128_t)unorm[n - 1]; + qhat = (uint64_t)(num2 / v1); + rhat = (__uint128_t)(uint64_t)(num2 % v1); } for (;;) { __uint128_t left = (__uint128_t)qhat * (__uint128_t)v2; - __uint128_t right = (rhat << 2*MP_N) | (__uint128_t)unorm[j + n - 2]; + __uint128_t right = (rhat << 64) | (__uint128_t)unorm[n - 2]; if (left <= right) break; - --qhat; rhat += (__uint128_t)v1; - if (rhat >= (((__uint128_t)1) << 2*MP_N)) break; + if (rhat >= (((__uint128_t)1) << 64)) break; } - uint64_t *u_seg = &unorm[j]; - uint64_t borrow_out = mul_sub_knuth(u_seg, vnorm, n, qhat); - + uint64_t borrow_out = mul_sub_knuth(unorm, vnorm, n, qhat); if (borrow_out) { - add_back_knuth(u_seg, vnorm, n); - qhat--; + add_back_knuth(unorm, vnorm, n); + --qhat; } + qlimb[0] = qhat; + } else { + for (int j = qn - 1; j >= 0; --j) { + uint64_t qhat; + __uint128_t rhat; + + if (unorm[j + n] == v1) { + qhat = UINT64_MAX; + rhat = (__uint128_t)unorm[j + n - 1] + (__uint128_t)v1; + } else if (use_preinv) { + uint64_t rr; + qhat = mp_udiv_qrnnd_preinv( + &rr, + unorm[j + n], + unorm[j + n - 1], + v1, + v1_inv + ); + rhat = (__uint128_t)rr; + } else { + __uint128_t num2 = ((__uint128_t)unorm[j + n] << 64) | (__uint128_t)unorm[j + n - 1]; + qhat = (uint64_t)(num2 / v1); + rhat = (__uint128_t)(uint64_t)(num2 % v1); + } - qlimb[j] = qhat; + for (;;) { + __uint128_t left = (__uint128_t)qhat * (__uint128_t)v2; + __uint128_t right = (rhat << 64) | (__uint128_t)unorm[j + n - 2]; + if (left <= right) break; + --qhat; + rhat += (__uint128_t)v1; + if (rhat >= (((__uint128_t)1) << 64)) break; + } + + uint64_t *u_seg = &unorm[j]; + uint64_t borrow_out = mul_sub_knuth(u_seg, vnorm, n, qhat); + if (borrow_out) { + add_back_knuth(u_seg, vnorm, n); + --qhat; + } + qlimb[j] = qhat; + } } mp_uint_t rlimb = {}; @@ -1238,7 +1357,6 @@ uint64_t mp_addmul(uint64_t *r, const uint64_t *a, size_t n, uint64_t b) uint64_t lo, hi; mp_mul64(a[i], b, lo, hi); - // r[i] + lo + carry uint64_t t = r[i] + lo; uint64_t c1 = (t < r[i]); diff --git a/tasksfile.js b/tasksfile.js index f5e5a46..ca9724c 100644 --- a/tasksfile.js +++ b/tasksfile.js @@ -176,8 +176,7 @@ function testAltBn128() { const asmDefines = getAsmDefines(); const cmd = - "g++ " + - //"-DCOUNT_OPS " + + "g++ -O3 " + (asmDefines ? asmDefines + " " : "") + getIncludeFlags(["-I../c"]) + " ../c/naf.cpp" + diff --git a/test/tester/buildzqfieldtester.js b/test/tester/buildzqfieldtester.js index ce11821..7a030ef 100644 --- a/test/tester/buildzqfieldtester.js +++ b/test/tester/buildzqfieldtester.js @@ -53,13 +53,13 @@ async function testField(prime, test, options = {}) { path.join(dir.path, "fr.cpp") ]; - if (useAsm && process.platform === "darwin" && process.arch === "arm64") { + /*if (useAsm && process.platform === "darwin" && process.arch === "arm64") { defines.push("-DUSE_ASM", "-DARCH_ARM64"); sources.push( path.join(dir.path, "fr_generic.cpp"), path.join(dir.path, "fr_raw_arm64.s") ); - } else { + } else */{ sources.push( path.join(dir.path, "fr_generic.cpp"), path.join(dir.path, "fr_raw_generic.cpp") From 487f994010ca6dd2c95073bc67ccce6a08f71867 Mon Sep 17 00:00:00 2001 From: Oleg Lomaka Date: Wed, 15 Apr 2026 04:38:28 -0400 Subject: [PATCH 11/13] nogmp: restore Montgomery carry handling for large-top-limb primes The GMP-to-custom-mp migration dropped the canOptimizeConsensys branching from three Montgomery multiplication functions in fr_raw_generic.cpp.ejs: rawMMul, rawMMul1, and rawFromMontgomery. canOptimizeConsensys is true when the top limb of the prime q is below 2^63, meaning intermediate Montgomery reduction products cannot overflow their allocated limb space. When false (top bit of the top limb is set), mp_addmul can produce a non-zero carry that must be tracked and checked during the final reduction step. The new code always followed the optimized (no-carry) path, which produced wrong results for any prime with a large top limb. The Goldilocks field (q = 0xFFFFFFFF00000001, n64=1) was the first to fail: add, sub, mul, square, eq, neq, and div all returned incorrect values. Larger primes like bn128 were unaffected because their top limbs have bit 63 clear. The fix re-adds canOptimizeConsensys branching in all three functions. When the flag is false: - Intermediate loop: carry from mp_addmul accumulates into product[i+1][n64] (not the fixed index [1] used in the optimized path), and carry from mp_add is captured. - Final step: carry from the last mp_addmul is accumulated. - Final reduction: checks both carry and mp_cmp, not just mp_cmp alone. Additional fixes discovered during review: - mp.cpp.ejs: added Knuth Algorithm D saturation guard in the __SIZEOF_INT128__ branch of mp_mod_2n_n. When unorm[j+n] >= v1 the 128-bit division can produce a result >= 2^64, and the uint64_t cast truncates it. The existing mp_div already had this guard; mp_mod_2n_n was missing it. - mp.cpp.ejs: removed stray closing brace in the #else branch of mp_addmul that would cause compilation errors on platforms without __SIZEOF_INT128__. - buildzqfield.js: the CLI now renders and writes mp.hpp/mp.cpp alongside the other generated files. Without this, users running the CLI tool got a broken build. - tasksfile.js: restored fr_generic.cpp and fq_generic.cpp in the ARM64 benchmark source list. These files define _rawMSquare and _rawToMontgomery which are not provided by the ARM64 assembly templates. - tester.cpp: changed throw new std::runtime_error(...) to throw std::runtime_error(...). The former throws a pointer, which is uncatchable by the standard catch-by-reference pattern. - alt_bn128_test.cpp: fixed TEST macro indentation, removed duplicate perf utility functions (perf_clock2, bench_ms2, ns_per_op2), uncommented includes needed by perf tests. - curve.cpp: removed two blocks of commented-out dead code (old copy/neg implementations superseded by batch versions). - README.md: removed GMP references, updated generated file list to include mp.cpp/mp.hpp, fixed compile command. - package.json: added mocha to devDependencies (was referenced in the test script but never declared). All 184 field tests pass across gl, bn128q, bn128r, bls12_381q, secp256k1q, secp256k1r, mnt6753q, and mnt6753r. --- README.md | 6 +- c/alt_bn128_test.cpp | 39 +- c/curve.cpp | 32 +- package-lock.json | 1803 +++++++++++++++++++++++++++++++++++- package.json | 1 + src/buildzqfield.js | 6 +- src/fr_raw_generic.cpp.ejs | 62 ++ src/mp.cpp.ejs | 16 +- tasksfile.js | 7 +- test/tester/tester.cpp | 2 +- 10 files changed, 1851 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index 3a567b3..91b83da 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This package is a script that generates a Finite field Library in Intel64 / ARM6 ## Usage -install g++ nasm ang gmp library if you don't have it. +install g++ and nasm if you don't have them. ``` npm install -g ffiasm @@ -17,7 +17,7 @@ cd myProject buildzqfield -q 21888242871839275222246405745257275088548364400416034343698204186575808495617 -n Fr ``` -You now will have two files fr.cpp, fr.hpp and fr.asm +You now will have fr.cpp, fr.hpp, fr.asm, mp.cpp, and mp.hpp ``` ls @@ -66,7 +66,7 @@ int main() { Compile it ``` -g++ main.cpp fr.o fr.cpp -o example -lgmp +g++ main.cpp fr.o fr.cpp mp.cpp -o example ``` Run it diff --git a/c/alt_bn128_test.cpp b/c/alt_bn128_test.cpp index 27a9f29..925408d 100644 --- a/c/alt_bn128_test.cpp +++ b/c/alt_bn128_test.cpp @@ -5,12 +5,9 @@ #include "gtest/gtest.h" #include "alt_bn128.hpp" #include "fft.hpp" -/* -//perf #include #include #include -*/ using namespace AltBn128; namespace { @@ -133,7 +130,7 @@ TEST(altBn128, g1_times_3) { ASSERT_TRUE(G1.eq(p1,p2)); } - TEST(altBn128, g1_times_3_exp) { +TEST(altBn128, g1_times_3_exp) { G1Point p1; G1.add(p1, G1.one(), G1.one()); G1.add(p1, p1, G1.one()); @@ -169,7 +166,7 @@ TEST(altBn128, g1_times_5) { ASSERT_TRUE(G1.eq(p1,p6)); } - TEST(altBn128, g1_times_65_exp) { +TEST(altBn128, g1_times_65_exp) { G1Point p1; G1.dbl(p1, G1.one()); G1.dbl(p1, p1); @@ -190,7 +187,7 @@ TEST(altBn128, g1_times_5) { ASSERT_TRUE(G1.eq(p1,p2)); } - TEST(altBn128, g1_expToOrder) { +TEST(altBn128, g1_expToOrder) { mp_uint_t scalar; mp_uint_t x; ASSERT_TRUE(mp_set(x, @@ -205,7 +202,7 @@ TEST(altBn128, g1_times_5) { ASSERT_TRUE(G1.isZero(p1)); } - TEST(altBn128, g2_expToOrder) { +TEST(altBn128, g2_expToOrder) { mp_uint_t scalar; mp_uint_t x; ASSERT_TRUE(mp_set(x, @@ -640,18 +637,6 @@ TEST(altBn128Perf, multiexp_phases_timing_nogmp) { } - using perf_clock2 = std::chrono::steady_clock; - -static inline double bench_ms2(std::function fn) { - auto t0 = perf_clock2::now(); - fn(); - auto t1 = perf_clock2::now(); - return std::chrono::duration(t1 - t0).count(); -} - -static inline double ns_per_op2(double ms, size_t n) { - return (ms * 1e6) / (double)n; -} TEST(altBn128Perf, affine_build_path_timing_nogmp) { G1PointAffine p1_aff, p2_aff, out_aff; @@ -666,7 +651,7 @@ TEST(altBn128Perf, affine_build_path_timing_nogmp) { const size_t N_BUILD_STEP = 100000; // 1) pure affine+affine -> Jacobian - double ms_add_affine = bench_ms2([&]() { + double ms_add_affine = bench_ms([&]() { for (size_t i = 0; i < N_ADD_AFFINE; i++) { G1.add(out_jac, p1_aff, p2_aff); } @@ -674,7 +659,7 @@ TEST(altBn128Perf, affine_build_path_timing_nogmp) { // 2) pure Jacobian -> affine G1.add(tmp_jac, p1_aff, p2_aff); - double ms_to_affine = bench_ms2([&]() { + double ms_to_affine = bench_ms([&]() { for (size_t i = 0; i < N_TO_AFFINE; i++) { G1.copy(out_aff, tmp_jac); } @@ -685,7 +670,7 @@ TEST(altBn128Perf, affine_build_path_timing_nogmp) { G1PointAffine cur_aff; G1.copy(cur_aff, p1_aff); - double ms_build_step = bench_ms2([&]() { + double ms_build_step = bench_ms([&]() { for (size_t i = 0; i < N_BUILD_STEP; i++) { G1.add(tmp_jac, cur_aff, p1_aff); G1.copy(cur_aff, tmp_jac); @@ -698,15 +683,15 @@ TEST(altBn128Perf, affine_build_path_timing_nogmp) { std::cout << std::fixed << std::setprecision(3); std::cout << "[perf][nogmp] G1.add affine+affine -> jac : " << ms_add_affine << " ms (N=" << N_ADD_AFFINE - << ") => " << ns_per_op2(ms_add_affine, N_ADD_AFFINE) << " ns/op\n"; + << ") => " << ns_per_op(ms_add_affine, N_ADD_AFFINE) << " ns/op\n"; std::cout << "[perf][nogmp] G1.copy jac -> affine : " << ms_to_affine << " ms (N=" << N_TO_AFFINE - << ") => " << ns_per_op2(ms_to_affine, N_TO_AFFINE) << " ns/op\n"; + << ") => " << ns_per_op(ms_to_affine, N_TO_AFFINE) << " ns/op\n"; std::cout << "[perf][nogmp] build step (add_aff + toAff): " << ms_build_step << " ms (N=" << N_BUILD_STEP - << ") => " << ns_per_op2(ms_build_step, N_BUILD_STEP) << " ns/op\n"; + << ") => " << ns_per_op(ms_build_step, N_BUILD_STEP) << " ns/op\n"; } TEST(altBn128Perf, prepare_breakdown_nogmp) { @@ -717,7 +702,7 @@ TEST(altBn128Perf, prepare_breakdown_nogmp) { Scalar *scalars = new Scalar[NMExp]; G1PointAffine *bases = new G1PointAffine[NMExp]; - double ms_scalars = bench_ms2([&]() { + double ms_scalars = bench_ms([&]() { for (int i = 0; i < NMExp; i++) { mp_uint_t x; mp_set(x, (uint64_t)(i + 1)); @@ -725,7 +710,7 @@ TEST(altBn128Perf, prepare_breakdown_nogmp) { } }); - double ms_bases = bench_ms2([&]() { + double ms_bases = bench_ms([&]() { for (int i = 0; i < NMExp; i++) { if (i == 0) { G1.copy(bases[0], G1.one()); diff --git a/c/curve.cpp b/c/curve.cpp index 81b9fa6..acf51eb 100644 --- a/c/curve.cpp +++ b/c/curve.cpp @@ -558,21 +558,6 @@ void Curve::copy(Point &r, PointAffine &a) { F.copy(r.zz, F.one()); F.copy(r.zzz, F.one()); } -/* -template -void Curve::copy(PointAffine &r, Point &a) { -#ifdef COUNT_OPS - cntToAffine++; -#endif // COUNT_OPS - if (isZero(a)) { - F.copy(r.x, F.zero()); - F.copy(r.y, F.zero()); - return; - } - F.div(r.x, a.x, a.zz); - F.div(r.y, a.y, a.zzz); -} -*/ template void Curve::batchToAffine(PointAffine *out, Point *in, size_t n) { @@ -691,22 +676,7 @@ void Curve::neg(Point &r, PointAffine &a) { F.copy(r.zz, F.one()); F.copy(r.zzz, F.one()); } -/* -template -void Curve::neg(PointAffine &r, Point &a) { -#ifdef COUNT_OPS - cntToAffine++; -#endif // COUNT_OPS - if (isZero(a)) { - F.copy(r.x, F.zero()); - F.copy(r.y, F.zero()); - return; - } - F.div(r.x, a.x, a.zz); - F.div(r.y, a.y, a.zzz); - F.neg(r.y, r.y); -} -*/ + template void Curve::batchNegToAffine(PointAffine *out, Point *in, size_t n) { if (n == 0) return; diff --git a/package-lock.json b/package-lock.json index 91cd98e..606d3d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "chai": "^4.2.0", "eslint": "^6.8.0", "ffjavascript": "0.0.3", + "mocha": "^11.7.5", "tasksfile": "^5.1.0", "tmp-promise": "^2.0.2" } @@ -50,6 +51,109 @@ "js-tokens": "^4.0.0" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@pawelgalazka/cli": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@pawelgalazka/cli/-/cli-2.0.2.tgz", @@ -99,6 +203,17 @@ "node": ">=8.6.0" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -165,9 +280,10 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -235,6 +351,13 @@ "concat-map": "0.0.1" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -298,6 +421,22 @@ "node": "*" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -384,13 +523,21 @@ } }, "node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decamelize": { @@ -419,6 +566,16 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -431,6 +588,13 @@ "node": ">=6.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ejs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz", @@ -445,6 +609,16 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -700,6 +874,16 @@ "node": ">=8" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -720,6 +904,100 @@ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/foreground-child/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -805,6 +1083,16 @@ "node": ">=4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -997,18 +1285,67 @@ "node": ">=0.10.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1070,6 +1407,106 @@ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -1097,6 +1534,16 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", @@ -1110,16 +1557,347 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, "node_modules/natural-compare": { @@ -1214,6 +1992,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1252,6 +2037,23 @@ "node": ">=4" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", @@ -1261,6 +2063,13 @@ "node": "*" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -1288,6 +2097,30 @@ "node": ">=6" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", @@ -1368,6 +2201,27 @@ "npm": ">=2.0.0" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -1383,6 +2237,16 @@ "semver": "bin/semver.js" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -1445,24 +2309,55 @@ "dev": true }, "node_modules/string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -1480,6 +2375,20 @@ "node": ">=6" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi/node_modules/ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -1490,12 +2399,16 @@ } }, "node_modules/strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { @@ -1729,6 +2642,13 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -1742,6 +2662,74 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -1839,6 +2827,61 @@ "engines": { "node": ">=6" } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { @@ -1868,6 +2911,71 @@ "js-tokens": "^4.0.0" } }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "requires": { + "ansi-regex": "^6.2.2" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@pawelgalazka/cli": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@pawelgalazka/cli/-/cli-2.0.2.tgz", @@ -1906,6 +3014,13 @@ "integrity": "sha512-MsNuS9M2vVbJNW3+YSiz9eF0/U2ZPlEEVC4ky8QJnxFlU4H7DZOH5A1hDQUfF5DV/pYwBD76dkF3+mrzZTJ3Tw==", "dev": true }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1954,9 +3069,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "3.2.1", @@ -2009,6 +3124,12 @@ "concat-map": "0.0.1" } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2057,6 +3178,15 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "requires": { + "readdirp": "^4.0.1" + } + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -2135,12 +3265,12 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "^2.1.3" } }, "decamelize": { @@ -2163,6 +3293,12 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2172,6 +3308,12 @@ "esutils": "^2.0.2" } }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "ejs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz", @@ -2182,6 +3324,12 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -2378,6 +3526,12 @@ "path-exists": "^4.0.0" } }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -2395,6 +3549,65 @@ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2456,6 +3669,12 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2605,18 +3824,46 @@ "is-extglob": "^2.1.1" } }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2669,6 +3916,73 @@ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -2690,6 +4004,12 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true + }, "mkdirp": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", @@ -2699,10 +4019,228 @@ "minimist": "^1.2.5" } }, + "mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, + "requires": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.2" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "mute-stream": { @@ -2782,6 +4320,12 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2808,12 +4352,28 @@ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -2832,6 +4392,21 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true + }, "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", @@ -2891,6 +4466,12 @@ "tslib": "^1.9.0" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -2903,6 +4484,15 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -2955,21 +4545,43 @@ "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "dependencies": { "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" + } + } + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" } } } @@ -2991,10 +4603,19 @@ } } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { @@ -3185,6 +4806,12 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -3227,6 +4854,52 @@ } } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3273,6 +4946,38 @@ "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index 76bbaf7..52ba831 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "chai": "^4.2.0", "eslint": "^6.8.0", "ffjavascript": "0.0.3", + "mocha": "^11.7.5", "tasksfile": "^5.1.0", "tmp-promise": "^2.0.2" }, diff --git a/src/buildzqfield.js b/src/buildzqfield.js index 3cb3b47..a44adeb 100755 --- a/src/buildzqfield.js +++ b/src/buildzqfield.js @@ -53,8 +53,10 @@ async function buildField(q, name) { const generic_cpp = await renderFile(path.join(__dirname, "fr_generic.cpp.ejs"), builder); const raw_generic_cpp = await renderFile(path.join(__dirname, "fr_raw_generic.cpp.ejs"), builder); const raw_arm64_s = await renderFile(path.join(__dirname, "fr_raw_arm64.s.ejs"), builder); + const mp_hpp = await renderFile(path.join(__dirname, "mp.hpp.ejs"), {n64: builder.n64}); + const mp_cpp = await renderFile(path.join(__dirname, "mp.cpp.ejs"), {n64: builder.n64}); - return {n64: builder.n64, asm: asm, hpp: hpp, cpp: cpp, element_hpp: element_hpp, generic_cpp: generic_cpp, raw_generic_cpp: raw_generic_cpp, raw_arm64_s: raw_arm64_s}; + return {n64: builder.n64, asm: asm, hpp: hpp, cpp: cpp, element_hpp: element_hpp, generic_cpp: generic_cpp, raw_generic_cpp: raw_generic_cpp, raw_arm64_s: raw_arm64_s, mp_hpp: mp_hpp, mp_cpp: mp_cpp}; } if (runningAsScript) { @@ -85,6 +87,8 @@ if (runningAsScript) { fs.writeFileSync(cGenericFileName, res.generic_cpp, "utf8"); fs.writeFileSync(cRawGenericFileName, res.raw_generic_cpp, "utf8"); fs.writeFileSync(sRawArm64FileName, res.raw_arm64_s, "utf8"); + fs.writeFileSync("mp.hpp", res.mp_hpp, "utf8"); + fs.writeFileSync("mp.cpp", res.mp_cpp, "utf8"); }); } else { diff --git a/src/fr_raw_generic.cpp.ejs b/src/fr_raw_generic.cpp.ejs index d30b254..6c51e01 100644 --- a/src/fr_raw_generic.cpp.ejs +++ b/src/fr_raw_generic.cpp.ejs @@ -107,6 +107,9 @@ void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement const uint64_t *mq = <%=name%>_rawq; uint64_t np0; +<% if (!canOptimizeConsensys) { %> + uint64_t c = 0; +<% } %> <% for (let i=0; i uint64_t product<%=i%>[N] = {0}; @@ -116,18 +119,35 @@ void <%=name%>_rawMMul(<%=name%>RawElement pRawResult, const <%=name%>RawElement <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; +<% if (!canOptimizeConsensys) { %> + c += mp_addmul(product<%=i%>, mq, N, np0); +<% } else { %> product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); +<% } %> product<%=i+1%>[<%=n64%>] = mp_addmul(product<%=i+1%>, pRawB, <%=name%>_N64, pRawA[<%=i+1%>]); +<% if (!canOptimizeConsensys) { %> + product<%=i+1%>[<%=n64%>] += c; + c = mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); +<% } else { %> mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); +<% } %> <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; +<% if (!canOptimizeConsensys) { %> + c += mp_addmul(product<%=n64-1%>, mq, N, np0); +<% } else { %> mp_addmul(product<%=n64-1%>, mq, N, np0); +<% } %> mp_copy(pRawResult, product<%=n64-1%> + 1); +<% if (!canOptimizeConsensys) { %> + if (c || mp_cmp(pRawResult, mq) >= 0) +<% } else { %> if (mp_cmp(pRawResult, mq) >= 0) +<% } %> { mp_sub(pRawResult, pRawResult, mq); } @@ -139,6 +159,9 @@ void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElemen const uint64_t *mq = <%=name%>_rawq; uint64_t np0; +<% if (!canOptimizeConsensys) { %> + uint64_t c = 0; +<% } %> <% for (let i=0; i uint64_t product<%=i%>[N] = {0}; @@ -148,16 +171,34 @@ void <%=name%>_rawMMul1(<%=name%>RawElement pRawResult, const <%=name%>RawElemen <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; +<% if (!canOptimizeConsensys) { %> + c += mp_addmul(product<%=i%>, mq, N, np0); +<% } else { %> product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); +<% } %> + +<% if (!canOptimizeConsensys) { %> + product<%=i+1%>[<%=n64%>] += c; + c = mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); +<% } else { %> mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); +<% } %> <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; +<% if (!canOptimizeConsensys) { %> + c += mp_addmul(product<%=n64-1%>, mq, N, np0); +<% } else { %> mp_addmul(product<%=n64-1%>, mq, N, np0); +<% } %> mp_copy(pRawResult, product<%=n64-1%> + 1); +<% if (!canOptimizeConsensys) { %> + if (c || mp_cmp(pRawResult, mq) >= 0) +<% } else { %> if (mp_cmp(pRawResult, mq) >= 0) +<% } %> { mp_sub(pRawResult, pRawResult, mq); } @@ -169,6 +210,9 @@ void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%> const uint64_t *mq = <%=name%>_rawq; uint64_t np0; +<% if (!canOptimizeConsensys) { %> + uint64_t c = 0; +<% } %> uint64_t product0[N]; <% for (let i=1; i @@ -180,16 +224,34 @@ void <%=name%>_rawFromMontgomery(<%=name%>RawElement pRawResult, const <%=name%> <% for (let i=0; i np0 = <%=name%>_np * product<%=i%>[0]; +<% if (!canOptimizeConsensys) { %> + c += mp_addmul(product<%=i%>, mq, N, np0); +<% } else { %> product<%=i+1%>[1] = mp_addmul(product<%=i%>, mq, N, np0); +<% } %> + +<% if (!canOptimizeConsensys) { %> + product<%=i+1%>[<%=n64%>] += c; + c = mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); +<% } else { %> mp_add(product<%=i+1%>, product<%=i+1%>, N, product<%=i%> + 1, N - 1); +<% } %> <% } %> np0 = <%=name%>_np * product<%=n64-1%>[0]; +<% if (!canOptimizeConsensys) { %> + c += mp_addmul(product<%=n64-1%>, mq, N, np0); +<% } else { %> mp_addmul(product<%=n64-1%>, mq, N, np0); +<% } %> mp_copy(pRawResult, product<%=n64-1%> + 1); +<% if (!canOptimizeConsensys) { %> + if (c || mp_cmp(pRawResult, mq) >= 0) +<% } else { %> if (mp_cmp(pRawResult, mq) >= 0) +<% } %> { mp_sub(pRawResult, pRawResult, mq); } diff --git a/src/mp.cpp.ejs b/src/mp.cpp.ejs index ecbe89e..168a010 100644 --- a/src/mp.cpp.ejs +++ b/src/mp.cpp.ejs @@ -1129,12 +1129,17 @@ static inline void mp_mod_2n_n(uint64_t *r, const uint64_t *num2n, const uint64_ uint64_t qhat, rhat; #if defined(__SIZEOF_INT128__) - __uint128_t uj2 = - ((__uint128_t)unorm[j + n] << (2 * MP_N)) | - (__uint128_t)unorm[j + n - 1]; + if (unorm[j + n] >= v1) { + qhat = UINT64_MAX; + rhat = unorm[j + n - 1] + v1; + } else { + __uint128_t uj2 = + ((__uint128_t)unorm[j + n] << (2 * MP_N)) | + (__uint128_t)unorm[j + n - 1]; - qhat = (uint64_t)(uj2 / v1); - rhat = (uint64_t)(uj2 % v1); + qhat = (uint64_t)(uj2 / v1); + rhat = (uint64_t)(uj2 % v1); + } for (;;) { __uint128_t left = @@ -1368,7 +1373,6 @@ uint64_t mp_addmul(uint64_t *r, const uint64_t *a, size_t n, uint64_t b) } return carry; -} #endif } diff --git a/tasksfile.js b/tasksfile.js index ca9724c..818889f 100644 --- a/tasksfile.js +++ b/tasksfile.js @@ -105,14 +105,11 @@ function getFieldSourcesForBench() { if (process.platform === "darwin" && process.arch === "arm64") { return [ "fq.cpp", + "fq_generic.cpp", "fq_raw_arm64.s", - //"fq_raw_generic.cpp", - //"fq_generic.cpp", "fr.cpp", - //"fr_generic.cpp", + "fr_generic.cpp", "fr_raw_arm64.s" - //"fr_raw_generic.cpp" - ]; } diff --git a/test/tester/tester.cpp b/test/tester/tester.cpp index f522656..87560b0 100644 --- a/test/tester/tester.cpp +++ b/test/tester/tester.cpp @@ -91,7 +91,7 @@ void pushNumber(std::vector &v) { void callFunction(FunctionSpec fs) { if (stack.size() < fs.nOps) { - throw new std::runtime_error("Not enough elements in stack"); + throw std::runtime_error("Not enough elements in stack"); } if (fs.nOps == 1) { FrElement a = stack.back(); From 70e9eb7b732eeeeba9069e732626a9e5a8056a86 Mon Sep 17 00:00:00 2001 From: furze Date: Tue, 12 May 2026 22:37:31 +0300 Subject: [PATCH 12/13] nogmp: add mp_copy/cmp 4-limb-fast-path --- src/mp.cpp.ejs | 44 +++++++++++++++++++++++++++++++++++++------- src/mp.hpp.ejs | 1 + 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/mp.cpp.ejs b/src/mp.cpp.ejs index 168a010..a8ea195 100644 --- a/src/mp.cpp.ejs +++ b/src/mp.cpp.ejs @@ -55,23 +55,53 @@ void mp_set(uint64_t *r, uint64_t a) { void mp_copy(uint64_t *r, const uint64_t *a) { if (r == a) return; - int i = 0, end = MP_N64, step = 1; - - if (r > a && r < a + MP_N64) { - i = MP_N64 - 1; - end = -1; - step = -1; +#if MP_N64 == 4 + const uintptr_t rp = reinterpret_cast(r); + const uintptr_t ap = reinterpret_cast(a); + static constexpr uintptr_t BYTES = MP_N64 * sizeof(uint64_t); + + if (rp > ap && rp < ap + BYTES) { + r[3] = a[3]; + r[2] = a[2]; + r[1] = a[1]; + r[0] = a[0]; + return; } - for (; i != end; i += step) r[i] = a[i]; + r[0] = a[0]; + r[1] = a[1]; + r[2] = a[2]; + r[3] = a[3]; +#else + std::memmove(r, a, MP_N64 * sizeof(uint64_t)); +#endif } int mp_cmp(const uint64_t *a, const uint64_t *b) { +#if MP_N64 == 4 + uint64_t ai, bi; + + ai = a[3]; bi = b[3]; + if (ai != bi) return (ai > bi) ? 1 : -1; + + ai = a[2]; bi = b[2]; + if (ai != bi) return (ai > bi) ? 1 : -1; + + ai = a[1]; bi = b[1]; + if (ai != bi) return (ai > bi) ? 1 : -1; + + ai = a[0]; bi = b[0]; + if (ai != bi) return (ai > bi) ? 1 : -1; + + return 0; + +#else for (int i = MP_N64 - 1; i >= 0; i--) { if (a[i] < b[i]) return -1; if (a[i] > b[i]) return 1; } return 0; +#endif } bool mp_is_zero(const uint64_t *a) { diff --git a/src/mp.hpp.ejs b/src/mp.hpp.ejs index d9f4bd2..0df988f 100644 --- a/src/mp.hpp.ejs +++ b/src/mp.hpp.ejs @@ -4,6 +4,7 @@ #include #include #include +#include #define MP_N64 <%= n64 %> #define MP_N 32 From e9f5da4f2e1ba99174dfa7af55e880131ca6b3fc Mon Sep 17 00:00:00 2001 From: furze Date: Tue, 16 Jun 2026 21:43:37 +0300 Subject: [PATCH 13/13] update mp api --- src/mp.cpp.ejs | 1921 +++++++++++++++++++++++++++++++++++++++--------- src/mp.hpp.ejs | 204 ++++- 2 files changed, 1780 insertions(+), 345 deletions(-) diff --git a/src/mp.cpp.ejs b/src/mp.cpp.ejs index a8ea195..8e8812e 100644 --- a/src/mp.cpp.ejs +++ b/src/mp.cpp.ejs @@ -42,142 +42,38 @@ static inline uint64_t sub_borrow(uint64_t *out, uint64_t a, uint64_t b, uint64_ #endif } -void mp_zero(uint64_t *r) { - std::memset(r, 0, MP_N64 * sizeof(uint64_t)); -} - void mp_set(uint64_t *r, uint64_t a) { - r[0] = a; - - for (int i = 1; i < MP_N64; i++) r[i] = 0; -} - -void mp_copy(uint64_t *r, const uint64_t *a) { - if (r == a) return; - #if MP_N64 == 4 - const uintptr_t rp = reinterpret_cast(r); - const uintptr_t ap = reinterpret_cast(a); - static constexpr uintptr_t BYTES = MP_N64 * sizeof(uint64_t); - - if (rp > ap && rp < ap + BYTES) { - r[3] = a[3]; - r[2] = a[2]; - r[1] = a[1]; - r[0] = a[0]; - return; - } - - r[0] = a[0]; - r[1] = a[1]; - r[2] = a[2]; - r[3] = a[3]; + r[0] = a; + r[1] = 0; + r[2] = 0; + r[3] = 0; #else - std::memmove(r, a, MP_N64 * sizeof(uint64_t)); + r[0] = a; + for (int i = 1; i < MP_N64; i++) r[i] = 0; #endif } -int mp_cmp(const uint64_t *a, const uint64_t *b) { +bool mp_is_zero(const uint64_t *a) { #if MP_N64 == 4 - uint64_t ai, bi; - - ai = a[3]; bi = b[3]; - if (ai != bi) return (ai > bi) ? 1 : -1; - - ai = a[2]; bi = b[2]; - if (ai != bi) return (ai > bi) ? 1 : -1; - - ai = a[1]; bi = b[1]; - if (ai != bi) return (ai > bi) ? 1 : -1; - - ai = a[0]; bi = b[0]; - if (ai != bi) return (ai > bi) ? 1 : -1; - - return 0; - + return (a[0] | a[1] | a[2] | a[3]) == 0; #else - for (int i = MP_N64 - 1; i >= 0; i--) { - if (a[i] < b[i]) return -1; - if (a[i] > b[i]) return 1; - } - return 0; -#endif -} - -bool mp_is_zero(const uint64_t *a) { uint64_t acc = 0; for (int i = 0; i < MP_N64; ++i) acc |= a[i]; return acc == 0; -} - -void mp_shl(uint64_t *r, const uint64_t *a, uint64_t k) { - if (k >= (uint64_t)MP_N64 * 64u) { - mp_zero(r); - return; - } - if (k == 0) { - mp_copy(r, a); - return; - } - - const auto wordShift = (uint32_t)(k >> 6); - const auto bitShift = (uint32_t)(k & 63u); - - if (bitShift == 0) { - for (int i = MP_N64 - 1; i >= 0; --i) { - int si = i - (int)wordShift; - r[i] = si >= 0 ? a[si] : 0; - } - return; - } - - const uint32_t inv = 64u - bitShift; - for (int i = MP_N64 - 1; i >= 0; --i) { - int si0 = i - (int)wordShift; - int si1 = si0 - 1; - - uint64_t lo = (si0 >= 0) ? a[si0] : 0; - uint64_t hi = (si1 >= 0) ? a[si1] : 0; - - r[i] = lo << bitShift | hi >> inv; - } -} - -void mp_shr(uint64_t *r, const uint64_t *a, uint64_t k) { - if (k >= (uint64_t)MP_N64 * 64u) { - mp_zero(r); - return; - } - if (k == 0) { - mp_copy(r, a); - return; - } - - const auto wordShift = (uint32_t)(k >> 6); - const auto bitShift = (uint32_t)(k & 63u); - - if (bitShift == 0) { - for (uint32_t i = 0; i < MP_N64; ++i) { - uint32_t si = i + wordShift; - r[i] = (si < (uint32_t)MP_N64) ? a[si] : 0; - } - return; - } - - const uint32_t inv = 64u - bitShift; - for (uint32_t i = 0; i < MP_N64; ++i) { - uint32_t si0 = i + wordShift; - uint32_t si1 = si0 + 1; - - uint64_t lo = (si0 < (uint32_t)MP_N64) ? a[si0] : 0; - uint64_t hi = (si1 < (uint32_t)MP_N64) ? a[si1] : 0; - - r[i] = lo >> bitShift | hi << inv; - } +#endif } uint64_t mp_add(uint64_t *r, const uint64_t *a, const uint64_t *b) { +#if MP_N64 == 4 + uint64_t c = 0; + c = add_carry(&r[0], a[0], b[0], c); + c = add_carry(&r[1], a[1], b[1], c); + c = add_carry(&r[2], a[2], b[2], c); + c = add_carry(&r[3], a[3], b[3], c); + return c; +#else uint64_t c = 0; for (int i = 0; i < MP_N64; ++i) { @@ -185,9 +81,18 @@ uint64_t mp_add(uint64_t *r, const uint64_t *a, const uint64_t *b) { } return c; +#endif } uint64_t mp_add(uint64_t *r, const uint64_t *a, uint64_t b) { +#if MP_N64 == 4 + uint64_t c = 0; + c = add_carry(&r[0], a[0], b, c); + c = add_carry(&r[1], a[1], 0, c); + c = add_carry(&r[2], a[2], 0, c); + c = add_carry(&r[3], a[3], 0, c); + return c; +#else uint64_t c = add_carry(&r[0], a[0], b, 0); for (int i = 1; i < MP_N64; ++i) { @@ -195,9 +100,18 @@ uint64_t mp_add(uint64_t *r, const uint64_t *a, uint64_t b) { } return c; +#endif } uint64_t mp_sub(uint64_t *r, const uint64_t *a, const uint64_t *b) { +#if MP_N64 == 4 + uint64_t br = 0; + br = sub_borrow(&r[0], a[0], b[0], br); + br = sub_borrow(&r[1], a[1], b[1], br); + br = sub_borrow(&r[2], a[2], b[2], br); + br = sub_borrow(&r[3], a[3], b[3], br); + return br; +#else uint64_t br = 0; for (int i = 0; i < MP_N64; ++i) { @@ -205,9 +119,18 @@ uint64_t mp_sub(uint64_t *r, const uint64_t *a, const uint64_t *b) { } return br; +#endif } uint64_t mp_sub(uint64_t *r, const uint64_t *a, uint64_t b) { +#if MP_N64 == 4 + uint64_t br = 0; + br = sub_borrow(&r[0], a[0], b, br); + br = sub_borrow(&r[1], a[1], 0, br); + br = sub_borrow(&r[2], a[2], 0, br); + br = sub_borrow(&r[3], a[3], 0, br); + return br; +#else uint64_t br = sub_borrow(&r[0], a[0], b, 0); for (int i = 1; i < MP_N64; ++i) { @@ -215,36 +138,274 @@ uint64_t mp_sub(uint64_t *r, const uint64_t *a, uint64_t b) { } return br; +#endif +} + +static inline uint64_t load_be64(const uint8_t *p) { + uint64_t x; + std::memcpy(&x, p, sizeof(x)); +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return __builtin_bswap64(x); +#else + return x; +#endif +} + +static inline void store_be64(uint8_t *p, uint64_t x) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + x = __builtin_bswap64(x); +#endif + std::memcpy(p, &x, sizeof(x)); } -bool mp_tstbit(const uint64_t *a, size_t bit) { - if (bit >= (size_t)MP_N64 * 64u) return false; - return a[bit >> 6] >> (bit & 63u) & 1ULL; +void mp_import_be(uint64_t *r, const uint8_t *a) { +#if MP_N64 == 4 + r[3] = load_be64(a + 0); + r[2] = load_be64(a + 8); + r[1] = load_be64(a + 16); + r[0] = load_be64(a + 24); +#else + auto *dst = reinterpret_cast(r); + const size_t nbytes = MP_N64 * sizeof(uint64_t); + + for (size_t i = 0; i < nbytes; ++i) { + dst[i] = a[nbytes - 1 - i]; + } +#endif } void mp_export_be(uint8_t *r, const uint64_t *a) { +#if MP_N64 == 4 + store_be64(r + 0, a[3]); + store_be64(r + 8, a[2]); + store_be64(r + 16, a[1]); + store_be64(r + 24, a[0]); +#else const auto *p = reinterpret_cast(a); const size_t nbytes = MP_N64 * sizeof(uint64_t); for (size_t i = 0; i < nbytes; ++i) { r[i] = p[nbytes - 1 - i]; } +#endif } -void mp_import_be(uint64_t *r, const uint8_t *a) { +enum class MpHexParseStatus { + Ok, + Invalid, + Overflow +}; + +alignas(64) static constexpr int8_t MP_HEX_DIGIT_TABLE[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static inline int mp_hex_digit(unsigned char c) { + return (int)MP_HEX_DIGIT_TABLE[c]; +} + +static inline bool mp_parse_hex64_exact_u256(uint64_t *r, const char *p) { + uint64_t r3 = 0; + uint64_t r2 = 0; + uint64_t r1 = 0; + uint64_t r0 = 0; + + for (int i = 0; i < 16; ++i) { + const int d = mp_hex_digit((unsigned char)p[i]); + if (d < 0) return false; + r3 = (r3 << 4) | (uint64_t)d; + } + + for (int i = 16; i < 32; ++i) { + const int d = mp_hex_digit((unsigned char)p[i]); + if (d < 0) return false; + r2 = (r2 << 4) | (uint64_t)d; + } + + for (int i = 32; i < 48; ++i) { + const int d = mp_hex_digit((unsigned char)p[i]); + if (d < 0) return false; + r1 = (r1 << 4) | (uint64_t)d; + } + + for (int i = 48; i < 64; ++i) { + const int d = mp_hex_digit((unsigned char)p[i]); + if (d < 0) return false; + r0 = (r0 << 4) | (uint64_t)d; + } + + r[3] = r3; + r[2] = r2; + r[1] = r1; + r[0] = r0; + return true; +} + +static inline bool mp_parse_hex_chunk_u64( + uint64_t *out, + const char *begin, + const char *end +) { + uint64_t x = 0; + + for (const char *p = begin; p != end; ++p) { + const int d = mp_hex_digit((unsigned char)*p); + if (d < 0) { + return false; + } + + x = (x << 4) | (uint64_t)d; + } + + *out = x; + return true; +} + +static MpHexParseStatus mp_parse_hex_u256( + uint64_t *r, + const char *str, + bool allow_neg, + bool *neg_out +) { mp_zero(r); - auto *dst = reinterpret_cast(r); - const size_t nbytes = MP_N64 * sizeof(uint64_t); - for (size_t i = 0; i < nbytes; ++i) { - dst[i] = a[nbytes - 1 - i]; + while (*str && std::isspace((unsigned char)*str)) { + ++str; } + + bool neg = false; + + if (*str == '+') { + ++str; + } else if (*str == '-') { + if (!allow_neg) { + return MpHexParseStatus::Invalid; + } + + neg = true; + ++str; + } + +#if MP_N64 == 4 + { + const char *p = str; + + size_t len = 0; + while (len <= 64u) { + const unsigned char c = (unsigned char)p[len]; + + if (c == '\0' || std::isspace(c)) { + break; + } + + ++len; + } + + if (len == 64u) { + const char *tail = p + 64; + + while (*tail && std::isspace((unsigned char)*tail)) { + ++tail; + } + + if (*tail == '\0') { + if (mp_parse_hex64_exact_u256(r, p)) { + if (neg_out) { + *neg_out = neg; + } + + return MpHexParseStatus::Ok; + } + } + } + } +#endif + + const char *digits = str; + const char *end = str; + + while (*end && !std::isspace((unsigned char)*end)) { + ++end; + } + + if (end == digits) { + return MpHexParseStatus::Invalid; + } + + const char *tail = end; + while (*tail && std::isspace((unsigned char)*tail)) { + ++tail; + } + + if (*tail != '\0') { + return MpHexParseStatus::Invalid; + } + + uint64_t tmp[MP_N64] = {}; + size_t pos = (size_t)(end - digits); + + for (int limb = 0; limb < MP_N64 && pos > 0; ++limb) { + const size_t chunk_len = pos > 16u ? 16u : pos; + + const char *chunk_begin = digits + pos - chunk_len; + const char *chunk_end = digits + pos; + + if (!mp_parse_hex_chunk_u64(&tmp[limb], chunk_begin, chunk_end)) { + return MpHexParseStatus::Invalid; + } + + pos -= chunk_len; + } + + bool overflow = false; + + while (pos > 0) { + const int d = mp_hex_digit((unsigned char)digits[--pos]); + + if (d < 0) { + return MpHexParseStatus::Invalid; + } + + if (d != 0) { + overflow = true; + } + } + + for (int i = 0; i < MP_N64; ++i) { + r[i] = tmp[i]; + } + + if (neg_out) { + *neg_out = neg; + } + + return overflow ? MpHexParseStatus::Overflow : MpHexParseStatus::Ok; } bool mp_set(uint64_t *r, const char *str, uint32_t base) { if (base == 0u) base = 10u; if (base < 2u || base > 16u) return false; + if (base == 16u) { + MpHexParseStatus st = mp_parse_hex_u256(r, str, false, nullptr); + return st == MpHexParseStatus::Ok; + } + mp_zero(r); while (*str && std::isspace((unsigned char)*str)) str++; @@ -311,6 +472,151 @@ static inline uint32_t mp_div(uint64_t *q, const uint64_t *a, uint32_t base) { #endif } +#if defined(__SIZEOF_INT128__) +static inline uint64_t mp_div_u64_base(uint64_t *q, const uint64_t *a, uint64_t base) { + __uint128_t rem = 0; + + for (int i = MP_N64 - 1; i >= 0; --i) { + __uint128_t cur = (rem << 64) | (__uint128_t)a[i]; + q[i] = (uint64_t)(cur / base); + rem = cur % base; + } + + return (uint64_t)rem; +} +#endif + +static inline void mp_append_dec_u64(std::string &out, uint64_t v) { + char buf[20]; + int pos = 20; + + do { + const uint64_t q = v / 10u; + buf[--pos] = (char)('0' + (v - q * 10u)); + v = q; + } while (v != 0); + + out.append(buf + pos, 20 - pos); +} + +static inline void mp_append_dec_padded_9(std::string &out, uint32_t v) { + char buf[9]; + + for (int i = 8; i >= 0; --i) { + const uint32_t q = v / 10u; + buf[i] = (char)('0' + (v - q * 10u)); + v = q; + } + + out.append(buf, 9); +} + +#if defined(__SIZEOF_INT128__) +static inline void mp_append_dec_padded_19(std::string &out, uint64_t v) { + char buf[19]; + + for (int i = 18; i >= 0; --i) { + const uint64_t q = v / 10u; + buf[i] = (char)('0' + (v - q * 10u)); + v = q; + } + + out.append(buf, 19); +} +#endif + +static constexpr char MP_DEC_PAIR_TABLE[] = + "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899"; + +static inline char *mp_write_dec_u64_back(char *p, uint64_t v) { + while (v >= 100u) { + const uint64_t q = v / 100u; + const unsigned r = (unsigned)(v - q * 100u); + + p -= 2; + p[0] = MP_DEC_PAIR_TABLE[2u * r + 0u]; + p[1] = MP_DEC_PAIR_TABLE[2u * r + 1u]; + + v = q; + } + + if (v >= 10u) { + const unsigned r = (unsigned)v; + + p -= 2; + p[0] = MP_DEC_PAIR_TABLE[2u * r + 0u]; + p[1] = MP_DEC_PAIR_TABLE[2u * r + 1u]; + } else { + *--p = (char)('0' + v); + } + + return p; +} + +static inline char *mp_write_dec_19_back(char *p, uint64_t v) { + for (int i = 0; i < 9; ++i) { + const uint64_t q = v / 100u; + const unsigned r = (unsigned)(v - q * 100u); + + p -= 2; + p[0] = MP_DEC_PAIR_TABLE[2u * r + 0u]; + p[1] = MP_DEC_PAIR_TABLE[2u * r + 1u]; + + v = q; + } + + *--p = (char)('0' + v); + + return p; +} + +static inline char *mp_write_dec_9_back(char *p, uint32_t v) { + for (int i = 0; i < 4; ++i) { + const uint32_t q = v / 100u; + const unsigned r = (unsigned)(v - q * 100u); + + p -= 2; + p[0] = MP_DEC_PAIR_TABLE[2u * r + 0u]; + p[1] = MP_DEC_PAIR_TABLE[2u * r + 1u]; + + v = q; + } + + *--p = (char)('0' + v); + + return p; +} + +static inline void mp_store_hex16(char *out, uint64_t x) { + static constexpr char hex[] = "0123456789abcdef"; + + out[0] = hex[(x >> 60) & 0xFu]; + out[1] = hex[(x >> 56) & 0xFu]; + out[2] = hex[(x >> 52) & 0xFu]; + out[3] = hex[(x >> 48) & 0xFu]; + out[4] = hex[(x >> 44) & 0xFu]; + out[5] = hex[(x >> 40) & 0xFu]; + out[6] = hex[(x >> 36) & 0xFu]; + out[7] = hex[(x >> 32) & 0xFu]; + out[8] = hex[(x >> 28) & 0xFu]; + out[9] = hex[(x >> 24) & 0xFu]; + out[10] = hex[(x >> 20) & 0xFu]; + out[11] = hex[(x >> 16) & 0xFu]; + out[12] = hex[(x >> 12) & 0xFu]; + out[13] = hex[(x >> 8) & 0xFu]; + out[14] = hex[(x >> 4) & 0xFu]; + out[15] = hex[x & 0xFu]; +} + std::string mp_get_str(const uint64_t *a, uint32_t base) { if (base == 0u) base = 10u; if (base < 2u || base > 16u) return {}; @@ -318,53 +624,174 @@ std::string mp_get_str(const uint64_t *a, uint32_t base) { if (mp_is_zero(a)) return {"0"}; if (base == 16u) { - static const char *hex = "0123456789abcdef"; +#if MP_N64 == 4 + char buf[64]; + + mp_store_hex16(buf + 0, a[3]); + mp_store_hex16(buf + 16, a[2]); + mp_store_hex16(buf + 32, a[1]); + mp_store_hex16(buf + 48, a[0]); + + size_t first = 0; + while (first < 63 && buf[first] == '0') { + ++first; + } + + return std::string(buf + first, 64u - first); + #else + static constexpr char hex[] = "0123456789abcdef"; + + int top = MP_N64 - 1; + while (top > 0 && a[top] == 0) { + --top; + } + + uint64_t x = a[top]; + + int shift = 60; + while (shift > 0 && ((x >> shift) & 0xFu) == 0) { + shift -= 4; + } + std::string out; - bool started = false; - - for (int i = MP_N64 - 1; i >= 0; --i) { - for (int shift = 60; shift >= 0; shift -= 4) { - uint32_t nib = (uint32_t)((a[i] >> shift) & 0xFu); - if (!started) { - if (nib == 0) continue; - started = true; - } - out.push_back(hex[nib]); - } + out.reserve((size_t)top * 16u + (size_t)(shift / 4 + 1)); + + for (; shift >= 0; shift -= 4) { + out.push_back(hex[(x >> shift) & 0xFu]); + } + + for (int i = top - 1; i >= 0; --i) { + x = a[i]; + + out.push_back(hex[(x >> 60) & 0xFu]); + out.push_back(hex[(x >> 56) & 0xFu]); + out.push_back(hex[(x >> 52) & 0xFu]); + out.push_back(hex[(x >> 48) & 0xFu]); + out.push_back(hex[(x >> 44) & 0xFu]); + out.push_back(hex[(x >> 40) & 0xFu]); + out.push_back(hex[(x >> 36) & 0xFu]); + out.push_back(hex[(x >> 32) & 0xFu]); + out.push_back(hex[(x >> 28) & 0xFu]); + out.push_back(hex[(x >> 24) & 0xFu]); + out.push_back(hex[(x >> 20) & 0xFu]); + out.push_back(hex[(x >> 16) & 0xFu]); + out.push_back(hex[(x >> 12) & 0xFu]); + out.push_back(hex[(x >> 8) & 0xFu]); + out.push_back(hex[(x >> 4) & 0xFu]); + out.push_back(hex[x & 0xFu]); } return out; +#endif } uint64_t v[MP_N64]; mp_copy(v, a); if (base == 10u) { - static constexpr uint32_t DEC_BASE = 1000000000u; - std::vector chunks; +#if defined(__SIZEOF_INT128__) && MP_N64 == 4 + static constexpr uint64_t DEC_BASE = 10000000000000000000ULL; // 1e19 + + uint64_t q0[MP_N64]; + uint64_t q1[MP_N64]; + uint64_t q2[MP_N64]; + uint64_t q3[MP_N64]; + uint64_t q4[MP_N64]; + + uint64_t chunks[5]; + size_t n_chunks = 0; + + chunks[n_chunks++] = mp_div_u64_base(q0, v, DEC_BASE); + + if (!mp_is_zero(q0)) { + chunks[n_chunks++] = mp_div_u64_base(q1, q0, DEC_BASE); + + if (!mp_is_zero(q1)) { + chunks[n_chunks++] = mp_div_u64_base(q2, q1, DEC_BASE); + + if (!mp_is_zero(q2)) { + chunks[n_chunks++] = mp_div_u64_base(q3, q2, DEC_BASE); + + if (!mp_is_zero(q3)) { + chunks[n_chunks++] = mp_div_u64_base(q4, q3, DEC_BASE); + } + } + } + } + + char buf[(size_t)MP_N64 * 20u + 1u]; + char *end = buf + sizeof(buf); + char *p = end; + + for (size_t i = 0; i + 1 < n_chunks; ++i) { + p = mp_write_dec_19_back(p, chunks[i]); + } + + p = mp_write_dec_u64_back(p, chunks[n_chunks - 1]); + + return std::string(p, (size_t)(end - p)); +#elif defined(__SIZEOF_INT128__) + static constexpr uint64_t DEC_BASE = 10000000000000000000ULL; // 1e19 + + uint64_t chunks[(size_t)MP_N64 * 4u]; + size_t n_chunks = 0; while (!mp_is_zero(v)) { uint64_t q[MP_N64]; - uint32_t rem = mp_div(q, v, DEC_BASE); - chunks.push_back(rem); + const uint64_t rem = mp_div_u64_base(q, v, DEC_BASE); + + chunks[n_chunks++] = rem; mp_copy(v, q); } - std::string out = std::to_string(chunks.back()); - for (int i = (int)chunks.size() - 2; i >= 0; --i) { - std::string s = std::to_string(chunks[i]); - out.append(9 - s.size(), '0'); - out += s; + char buf[(size_t)MP_N64 * 20u + 1u]; + char *end = buf + sizeof(buf); + char *p = end; + + for (size_t i = 0; i + 1 < n_chunks; ++i) { + p = mp_write_dec_19_back(p, chunks[i]); } - return out; + + p = mp_write_dec_u64_back(p, chunks[n_chunks - 1]); + + return std::string(p, (size_t)(end - p)); +#else + static constexpr uint32_t DEC_BASE = 1000000000u; // 1e9 + + uint32_t chunks[(size_t)MP_N64 * 8u]; + size_t n_chunks = 0; + + while (!mp_is_zero(v)) { + uint64_t q[MP_N64]; + const uint32_t rem = mp_div(q, v, DEC_BASE); + + chunks[n_chunks++] = rem; + mp_copy(v, q); + } + + char buf[(size_t)MP_N64 * 20u + 1u]; + char *end = buf + sizeof(buf); + char *p = end; + + for (size_t i = 0; i + 1 < n_chunks; ++i) { + p = mp_write_dec_9_back(p, chunks[i]); } + p = mp_write_dec_u64_back(p, chunks[n_chunks - 1]); + + return std::string(p, (size_t)(end - p)); +#endif +} + std::string out; + while (!mp_is_zero(v)) { uint64_t q[MP_N64]; uint32_t rem = mp_div(q, v, base); + char digit = (rem < 10u) ? (char)('0' + rem) : (char)('a' + (rem - 10u)); + out.push_back(digit); mp_copy(v, q); } @@ -427,6 +854,37 @@ bool mp_set_mod(uint64_t *r, const char *str, uint32_t base, const uint64_t *mod if (base == 0u) base = 10u; if (base < 2u || base > 16u) return false; + if (base == 16u) { + uint64_t tmp[MP_N64]; + bool neg = false; + + MpHexParseStatus st = mp_parse_hex_u256(tmp, str, true, &neg); + + if (st == MpHexParseStatus::Invalid) { + return false; + } + + if (st == MpHexParseStatus::Ok) { + if (mp_cmp(tmp, mod) >= 0) { + uint64_t q[MP_N64]; + uint64_t rem[MP_N64]; + + mp_div(q, rem, tmp, mod); + mp_copy(tmp, rem); + } + + if (neg && !mp_is_zero(tmp)) { + uint64_t reduced_neg[MP_N64]; + + mp_sub(reduced_neg, mod, tmp); + mp_copy(tmp, reduced_neg); + } + + mp_copy(r, tmp); + return true; + } + } + while (*str && std::isspace((unsigned char)*str)) str++; bool neg = false; @@ -571,33 +1029,18 @@ static inline uint64_t div_2by1(uint64_t *q, uint64_t hi, uint64_t lo, uint64_t uint64_t qq = 0; uint64_t rem = hi; -#define DIVSTEP(bit) \ -do { \ -const uint64_t overflow = rem >> 63; \ -const uint64_t rem2 = (rem << 1) | ((lo >> (bit)) & 1ULL); \ -const uint64_t ge = (uint64_t)(overflow | (uint64_t)(rem2 >= v)); \ -rem = rem2 - (v & (0ULL - ge)); \ -qq |= (ge << (bit)); \ -} while (0) - - DIVSTEP(63); DIVSTEP(62); DIVSTEP(61); DIVSTEP(60); - DIVSTEP(59); DIVSTEP(58); DIVSTEP(57); DIVSTEP(56); - DIVSTEP(55); DIVSTEP(54); DIVSTEP(53); DIVSTEP(52); - DIVSTEP(51); DIVSTEP(50); DIVSTEP(49); DIVSTEP(48); - DIVSTEP(47); DIVSTEP(46); DIVSTEP(45); DIVSTEP(44); - DIVSTEP(43); DIVSTEP(42); DIVSTEP(41); DIVSTEP(40); - DIVSTEP(39); DIVSTEP(38); DIVSTEP(37); DIVSTEP(36); - DIVSTEP(35); DIVSTEP(34); DIVSTEP(33); DIVSTEP(32); - DIVSTEP(31); DIVSTEP(30); DIVSTEP(29); DIVSTEP(28); - DIVSTEP(27); DIVSTEP(26); DIVSTEP(25); DIVSTEP(24); - DIVSTEP(23); DIVSTEP(22); DIVSTEP(21); DIVSTEP(20); - DIVSTEP(19); DIVSTEP(18); DIVSTEP(17); DIVSTEP(16); - DIVSTEP(15); DIVSTEP(14); DIVSTEP(13); DIVSTEP(12); - DIVSTEP(11); DIVSTEP(10); DIVSTEP(9); DIVSTEP(8); - DIVSTEP(7); DIVSTEP(6); DIVSTEP(5); DIVSTEP(4); - DIVSTEP(3); DIVSTEP(2); DIVSTEP(1); DIVSTEP(0); - -#undef DIVSTEP + for (int bit = (2 * MP_N) - 1; bit >= 0; --bit) { + // Shift remainder left by 1 and bring next bit from lo. + uint64_t overflow = rem >> ((2 * MP_N) - 1); + rem = (rem << 1) | ((lo >> bit) & 1ULL); + + // If previous top bit was 1, actual shifted remainder was >= 2^64. + // Since rem_before < v, shifted remainder < 2*v + 1, so one subtraction is enough. + if (overflow || rem >= v) { + rem -= v; + qq |= (1ULL << bit); + } + } *q = qq; return rem; @@ -697,64 +1140,6 @@ static inline uint64_t add_back_knuth(uint64_t *u, const uint64_t *v, int n) #endif } -static inline uint64_t mp_invert_limb_norm(uint64_t d) -{ -#if defined(__SIZEOF_INT128__) - // d is normalized (top bit set). Use a GMP-style 64-bit preinverse: - // floor((B^2 - 1)/d) - B, B = 2^64. - const __uint128_t B = ((__uint128_t)1) << 64; - return (uint64_t)((((B * B) - 1) / d) - B); -#else - return 0; // fallback path does not use preinverse -#endif -} - -static inline uint64_t mp_udiv_qrnnd_preinv( - uint64_t *r, - uint64_t nh, uint64_t nl, - uint64_t d, - uint64_t dinv) -{ -#if defined(__SIZEOF_INT128__) - const __uint128_t n = ((__uint128_t)nh << 64) | (__uint128_t)nl; - - // q ~= high(nh * dinv + nl) + nh - __uint128_t t = (__uint128_t)nh * (__uint128_t)dinv + (__uint128_t)nl; - uint64_t q = (uint64_t)(t >> 64) + nh; - - if (q == 0 || nh >= d) q = UINT64_MAX; - - __uint128_t prod = (__uint128_t)q * (__uint128_t)d; - - if (prod > n) { - --q; - prod -= d; - if (prod > n) { - --q; - prod -= d; - } - } else { - __uint128_t prod2 = prod + d; - if (prod2 <= n) { - ++q; - prod = prod2; - prod2 += d; - if (prod2 <= n) { - ++q; - prod = prod2; - } - } - } - - *r = (uint64_t)(n - prod); - return q; -#else - uint64_t q; - *r = div_2by1(&q, nh, nl, d); - return q; -#endif -} - void mp_div(uint64_t *q, uint64_t *r, const uint64_t *num, const uint64_t *den) { if (mp_is_zero(den)) { mp_zero(q); @@ -829,13 +1214,7 @@ void mp_div(uint64_t *q, uint64_t *r, const uint64_t *num, const uint64_t *den) for (int j = qn - 1; j >= 0; --j) { uint64_t qhat, rhat; - - if (unorm[j + n] == v1) { - qhat = UINT64_MAX; - rhat = unorm[j + n - 1] + v1; - } else { - rhat = div_2by1(&qhat, unorm[j + n], unorm[j + n - 1], v1); - } + rhat = div_2by1(&qhat, unorm[j + n], unorm[j + n - 1], v1); for (;;) { uint64_t lo, hi; @@ -942,82 +1321,42 @@ void mp_div(uint64_t *q, uint64_t *r, const uint64_t *num, const uint64_t *den) const uint64_t v1 = vnorm[n - 1]; const uint64_t v2 = vnorm[n - 2]; - // Preinverse only pays off reliably for genuinely multi-limb quotient paths - // and larger normalized divisors. For very small n (especially n == 2), the - // exact 128/64 estimate is both safer and often faster on our fixed-size use - // cases. - const bool use_preinv = false; - const uint64_t v1_inv = 0; - - if (qn == 1) { + + for (int j = qn - 1; j >= 0; j--) { uint64_t qhat; __uint128_t rhat; - if (unorm[n] == v1) { + if (unorm[j + n] == v1) { qhat = UINT64_MAX; - rhat = (__uint128_t)unorm[n - 1] + (__uint128_t)v1; + rhat = (__uint128_t)unorm[j + n - 1] + (__uint128_t)v1; } else { - __uint128_t num2 = ((__uint128_t)unorm[n] << 64) | (__uint128_t)unorm[n - 1]; - qhat = (uint64_t)(num2 / v1); - rhat = (__uint128_t)(uint64_t)(num2 % v1); + __uint128_t uj2 = + (__uint128_t)unorm[j + n] << (2*MP_N) | + (__uint128_t)unorm[j + n - 1]; + + qhat = (uint64_t)(uj2 / v1); + rhat = uj2 % v1; } for (;;) { __uint128_t left = (__uint128_t)qhat * (__uint128_t)v2; - __uint128_t right = (rhat << 64) | (__uint128_t)unorm[n - 2]; + __uint128_t right = (rhat << 2*MP_N) | (__uint128_t)unorm[j + n - 2]; if (left <= right) break; + --qhat; rhat += (__uint128_t)v1; - if (rhat >= (((__uint128_t)1) << 64)) break; + if (rhat >= (((__uint128_t)1) << 2*MP_N)) break; } - uint64_t borrow_out = mul_sub_knuth(unorm, vnorm, n, qhat); + uint64_t *u_seg = &unorm[j]; + uint64_t borrow_out = mul_sub_knuth(u_seg, vnorm, n, qhat); + if (borrow_out) { - add_back_knuth(unorm, vnorm, n); - --qhat; + add_back_knuth(u_seg, vnorm, n); + qhat--; } - qlimb[0] = qhat; - } else { - for (int j = qn - 1; j >= 0; --j) { - uint64_t qhat; - __uint128_t rhat; - - if (unorm[j + n] == v1) { - qhat = UINT64_MAX; - rhat = (__uint128_t)unorm[j + n - 1] + (__uint128_t)v1; - } else if (use_preinv) { - uint64_t rr; - qhat = mp_udiv_qrnnd_preinv( - &rr, - unorm[j + n], - unorm[j + n - 1], - v1, - v1_inv - ); - rhat = (__uint128_t)rr; - } else { - __uint128_t num2 = ((__uint128_t)unorm[j + n] << 64) | (__uint128_t)unorm[j + n - 1]; - qhat = (uint64_t)(num2 / v1); - rhat = (__uint128_t)(uint64_t)(num2 % v1); - } - for (;;) { - __uint128_t left = (__uint128_t)qhat * (__uint128_t)v2; - __uint128_t right = (rhat << 64) | (__uint128_t)unorm[j + n - 2]; - if (left <= right) break; - --qhat; - rhat += (__uint128_t)v1; - if (rhat >= (((__uint128_t)1) << 64)) break; - } - - uint64_t *u_seg = &unorm[j]; - uint64_t borrow_out = mul_sub_knuth(u_seg, vnorm, n, qhat); - if (borrow_out) { - add_back_knuth(u_seg, vnorm, n); - --qhat; - } - qlimb[j] = qhat; - } + qlimb[j] = qhat; } mp_uint_t rlimb = {}; @@ -1159,17 +1498,12 @@ static inline void mp_mod_2n_n(uint64_t *r, const uint64_t *num2n, const uint64_ uint64_t qhat, rhat; #if defined(__SIZEOF_INT128__) - if (unorm[j + n] >= v1) { - qhat = UINT64_MAX; - rhat = unorm[j + n - 1] + v1; - } else { - __uint128_t uj2 = - ((__uint128_t)unorm[j + n] << (2 * MP_N)) | - (__uint128_t)unorm[j + n - 1]; + __uint128_t uj2 = + ((__uint128_t)unorm[j + n] << (2 * MP_N)) | + (__uint128_t)unorm[j + n - 1]; - qhat = (uint64_t)(uj2 / v1); - rhat = (uint64_t)(uj2 % v1); - } + qhat = (uint64_t)(uj2 / v1); + rhat = (uint64_t)(uj2 % v1); for (;;) { __uint128_t left = @@ -1234,6 +1568,7 @@ static inline void mp_mod_2n_n(uint64_t *r, const uint64_t *num2n, const uint64_ } } + static inline void mp_mulmod(uint64_t *r, const uint64_t *a, const uint64_t *b, const uint64_t *mod) { if (mp_is_zero(mod)) { @@ -1246,6 +1581,298 @@ static inline void mp_mulmod(uint64_t *r, const uint64_t *a, const uint64_t *b, mp_mod_2n_n(r, prod, mod); } +#if MP_N64 == 4 && defined(__SIZEOF_INT128__) + +static inline uint64_t mp_mont_n0inv_4(uint64_t n0) { + // Computes -n0^{-1} mod 2^64. + // Valid only when n0 is odd. + uint64_t x = 1; + + x *= 2ULL - n0 * x; + x *= 2ULL - n0 * x; + x *= 2ULL - n0 * x; + x *= 2ULL - n0 * x; + x *= 2ULL - n0 * x; + x *= 2ULL - n0 * x; + + return (uint64_t)0 - x; +} + +static inline void mp_mont_reduce_4( + uint64_t *r, + const uint64_t *in, + const uint64_t *mod, + uint64_t n0inv +) { + uint64_t t[9] = { + in[0], in[1], in[2], in[3], + in[4], in[5], in[6], in[7], + 0 + }; + + for (int i = 0; i < 4; ++i) { + const uint64_t m = t[i] * n0inv; + + __uint128_t z; + __uint128_t carry = 0; + + z = (__uint128_t)m * mod[0] + t[i + 0] + carry; + t[i + 0] = (uint64_t)z; + carry = z >> 64; + + z = (__uint128_t)m * mod[1] + t[i + 1] + carry; + t[i + 1] = (uint64_t)z; + carry = z >> 64; + + z = (__uint128_t)m * mod[2] + t[i + 2] + carry; + t[i + 2] = (uint64_t)z; + carry = z >> 64; + + z = (__uint128_t)m * mod[3] + t[i + 3] + carry; + t[i + 3] = (uint64_t)z; + carry = z >> 64; + + z = (__uint128_t)t[i + 4] + carry; + t[i + 4] = (uint64_t)z; + + uint64_t c = (uint64_t)(z >> 64); + + for (int k = i + 5; c && k < 9; ++k) { + z = (__uint128_t)t[k] + c; + t[k] = (uint64_t)z; + c = (uint64_t)(z >> 64); + } + } + + r[0] = t[4]; + r[1] = t[5]; + r[2] = t[6]; + r[3] = t[7]; + + if (t[8] != 0 || mp_cmp(r, mod) >= 0) { + mp_sub(r, r, mod); + } +} + +static inline void mp_mont_mul_4( + uint64_t *r, + const uint64_t *a, + const uint64_t *b, + const uint64_t *mod, + uint64_t n0inv +) { + uint64_t prod[8]; + + mp_mul_full(prod, a, b); + mp_mont_reduce_4(r, prod, mod, n0inv); +} + +static inline void mp_mont_compute_r_r2_4( + uint64_t *r_mod, + uint64_t *r2_mod, + const uint64_t *mod +) { + // R = 2^256. + // r_mod = R mod mod. + uint64_t r_full[8] = { + 0, 0, 0, 0, + 1, 0, 0, 0 + }; + + mp_mod_2n_n(r_mod, r_full, mod); + + // r2_mod = R^2 mod mod = (R mod mod)^2 mod mod. + uint64_t prod[8]; + mp_mul_full(prod, r_mod, r_mod); + mp_mod_2n_n(r2_mod, prod, mod); +} + +static inline void mp_to_mont_4( + uint64_t *r, + const uint64_t *a, + const uint64_t *r2_mod, + const uint64_t *mod, + uint64_t n0inv +) { + // aM = a * R mod mod = REDC(a * R^2) + mp_mont_mul_4(r, a, r2_mod, mod, n0inv); +} + +static inline void mp_from_mont_4( + uint64_t *r, + const uint64_t *a_mont, + const uint64_t *mod, + uint64_t n0inv +) { + uint64_t one[4] = {1, 0, 0, 0}; + + // a = REDC(aM * 1) + mp_mont_mul_4(r, a_mont, one, mod, n0inv); +} + +struct MpMontCtx4 { + uint64_t mod[4]; + uint64_t r_mod[4]; + uint64_t r2_mod[4]; + uint64_t n0inv; +}; + +static constexpr MpMontCtx4 MP_MONT_BN254_FQ = { + { + 0x3c208c16d87cfd47ULL, + 0x97816a916871ca8dULL, + 0xb85045b68181585dULL, + 0x30644e72e131a029ULL + }, + { + 0xd35d438dc58f0d9dULL, + 0x0a78eb28f5c70b3dULL, + 0x666ea36f7879462cULL, + 0x0e0a77c19a07df2fULL + }, + { + 0xf32cfc5b538afa89ULL, + 0xb5e71911d44501fbULL, + 0x47ab1eff0a417ff6ULL, + 0x06d89f71cab8351fULL + }, + 0x87d20782e4866389ULL +}; + +static constexpr MpMontCtx4 MP_MONT_BN254_FR = { + { + 0x43e1f593f0000001ULL, + 0x2833e84879b97091ULL, + 0xb85045b68181585dULL, + 0x30644e72e131a029ULL + }, + { + 0xac96341c4ffffffbULL, + 0x36fc76959f60cd29ULL, + 0x666ea36f7879462eULL, + 0x0e0a77c19a07df2fULL + }, + { + 0x1bb8e645ae216da7ULL, + 0x53fe3ab1e35c59e3ULL, + 0x8c49833d53bb8085ULL, + 0x0216d0b17f4e44a5ULL + }, + 0xc2e1f593efffffffULL +}; + +static inline bool mp_mont_mod_eq_4(const uint64_t *a, const uint64_t *b) { + return ((a[0] ^ b[0]) | + (a[1] ^ b[1]) | + (a[2] ^ b[2]) | + (a[3] ^ b[3])) == 0; +} + +static inline const MpMontCtx4 *mp_mont_get_known_ctx_4(const uint64_t *mod) { + if (mp_mont_mod_eq_4(mod, MP_MONT_BN254_FQ.mod)) { + return &MP_MONT_BN254_FQ; + } + + if (mp_mont_mod_eq_4(mod, MP_MONT_BN254_FR.mod)) { + return &MP_MONT_BN254_FR; + } + + return nullptr; +} + + +static inline int mp_exp_popcount_4(const uint64_t *exp) { + return __builtin_popcountll(exp[0]) + + __builtin_popcountll(exp[1]) + + __builtin_popcountll(exp[2]) + + __builtin_popcountll(exp[3]); +} + +static inline void mp_pow_mod_mont_windowed_4( + uint64_t *r, + const uint64_t *base, + const uint64_t *exp, + const uint64_t *mod, + uint64_t n0inv, + const uint64_t *r2_mod, + int topBit, + int windowBits +) { + // Supports windowBits 4 or 5. Table stores odd powers: 1,3,...,(2^w - 1). + uint64_t table[16][4]; + const int tableSize = 1 << (windowBits - 1); + + // table[0] = base^1 in Montgomery form. + mp_to_mont_4(table[0], base, r2_mod, mod, n0inv); + + // b2 = base^2 in Montgomery form. + uint64_t b2[4]; + mp_mont_mul_4(b2, table[0], table[0], mod, n0inv); + + // table[i] = base^(2*i + 1) in Montgomery form. + for (int i = 1; i < tableSize; ++i) { + mp_mont_mul_4(table[i], table[i - 1], b2, mod, n0inv); + } + + uint64_t one[4]; + mp_set(one, 1u); + + uint64_t acc[4]; + mp_to_mont_4(acc, one, r2_mod, mod, n0inv); + + for (int i = topBit; i >= 0;) { + const int limb = i / (2 * MP_N); + const int bit = i % (2 * MP_N); + + if (((exp[limb] >> bit) & 1ULL) == 0) { + uint64_t sq[4]; + mp_mont_mul_4(sq, acc, acc, mod, n0inv); + mp_copy(acc, sq); + --i; + continue; + } + + int width = std::min(windowBits, i + 1); + int low = i - width + 1; + + // Keep the selected window odd by moving its low end to a set bit. + while (low < i) { + const int low_limb = low / (2 * MP_N); + const int low_bit = low % (2 * MP_N); + + if ((exp[low_limb] >> low_bit) & 1ULL) { + break; + } + + ++low; + --width; + } + + unsigned window = 0; + for (int j = i; j >= low; --j) { + const int j_limb = j / (2 * MP_N); + const int j_bit = j % (2 * MP_N); + window = (window << 1) | (unsigned)((exp[j_limb] >> j_bit) & 1ULL); + } + + for (int j = 0; j < width; ++j) { + uint64_t sq[4]; + mp_mont_mul_4(sq, acc, acc, mod, n0inv); + mp_copy(acc, sq); + } + + uint64_t tmp[4]; + mp_mont_mul_4(tmp, acc, table[window >> 1], mod, n0inv); + mp_copy(acc, tmp); + + i = low - 1; + } + + mp_from_mont_4(r, acc, mod, n0inv); +} + +#endif + void mp_pow_mod(uint64_t *r, const uint64_t *base, const uint64_t *exp, const uint64_t *mod) { mp_uint_t one; @@ -1267,9 +1894,13 @@ void mp_pow_mod(uint64_t *r, const uint64_t *base, const uint64_t *exp, const ui } int topBit = -1; + for (int limb = MP_N64 - 1; limb >= 0 && topBit < 0; --limb) { uint64_t w = exp[limb]; - if (!w) continue; + + if (!w) { + continue; + } for (int bit = 2 * MP_N - 1; bit >= 0; --bit) { if ((w >> bit) & 1u) { @@ -1284,11 +1915,72 @@ void mp_pow_mod(uint64_t *r, const uint64_t *base, const uint64_t *exp, const ui return; } +#if MP_N64 == 4 && defined(__SIZEOF_INT128__) + if ((mod[0] & 1ULL) != 0) { + uint64_t n0inv; + uint64_t r2_mod_local[4]; + const uint64_t *r2_mod_ptr = nullptr; + + const MpMontCtx4 *ctx = mp_mont_get_known_ctx_4(mod); + + if (ctx) { + n0inv = ctx->n0inv; + r2_mod_ptr = ctx->r2_mod; + } else { + n0inv = mp_mont_n0inv_4(mod[0]); + + uint64_t r_mod_local[4]; + mp_mont_compute_r_r2_4(r_mod_local, r2_mod_local, mod); + + r2_mod_ptr = r2_mod_local; + } + + const int popcnt = mp_exp_popcount_4(exp); + + if (topBit >= 191 && popcnt >= 64) { + mp_pow_mod_mont_windowed_4(r, bcur, exp, mod, n0inv, r2_mod_ptr, topBit, 5); + return; + } + + if (topBit >= 63 && popcnt >= 16) { + mp_pow_mod_mont_windowed_4(r, bcur, exp, mod, n0inv, r2_mod_ptr, topBit, 4); + return; + } + + uint64_t b_mont[4]; + uint64_t acc_mont[4]; + + mp_to_mont_4(b_mont, bcur, r2_mod_ptr, mod, n0inv); + mp_copy(acc_mont, b_mont); + + for (int i = topBit - 1; i >= 0; --i) { + uint64_t sq[4]; + + mp_mont_mul_4(sq, acc_mont, acc_mont, mod, n0inv); + mp_copy(acc_mont, sq); + + const int limb = i / (2 * MP_N); + const int bit = i % (2 * MP_N); + + if ((exp[limb] >> bit) & 1u) { + uint64_t tmp[4]; + + mp_mont_mul_4(tmp, acc_mont, b_mont, mod, n0inv); + mp_copy(acc_mont, tmp); + } + } + + mp_from_mont_4(r, acc_mont, mod, n0inv); + return; + } +#endif + mp_uint_t acc; mp_copy(acc, bcur); for (int i = topBit - 1; i >= 0; --i) { mp_uint_t sq; + mp_mulmod(sq, acc, acc, mod); mp_copy(acc, sq); @@ -1297,6 +1989,7 @@ void mp_pow_mod(uint64_t *r, const uint64_t *base, const uint64_t *exp, const ui if ((exp[limb] >> bit) & 1u) { mp_uint_t tmp; + mp_mulmod(tmp, acc, bcur, mod); mp_copy(acc, tmp); } @@ -1347,12 +2040,32 @@ void mp_set_mod(uint64_t *r, int64_t a, const uint64_t *mod) { } uint64_t mp_mul(uint64_t *r, const uint64_t *a, uint64_t b) { -#if defined(__SIZEOF_INT128__) +#if defined(__SIZEOF_INT128__) && MP_N64 == 4 + __uint128_t t; + + t = (__uint128_t)a[0] * b; + r[0] = (uint64_t)t; + t >>= 64; + + t += (__uint128_t)a[1] * b; + r[1] = (uint64_t)t; + t >>= 64; + + t += (__uint128_t)a[2] * b; + r[2] = (uint64_t)t; + t >>= 64; + + t += (__uint128_t)a[3] * b; + r[3] = (uint64_t)t; + + return (uint64_t)(t >> 64); + +#elif defined(__SIZEOF_INT128__) __uint128_t carry = 0; for (int i = 0; i < MP_N64; i++) { __uint128_t t = (__uint128_t)a[i] * (__uint128_t)b + carry; r[i] = (uint64_t)t; - carry = t >> 2*MP_N; + carry = t >> 64; } return (uint64_t)carry; #else @@ -1374,6 +2087,141 @@ uint64_t mp_mul(uint64_t *r, const uint64_t *a, uint64_t b) { uint64_t mp_addmul(uint64_t *r, const uint64_t *a, size_t n, uint64_t b) { #if defined(__SIZEOF_INT128__) + +#if MP_N64 == 4 + // mp_addmul(productX, pRawB, Fq_N64/Fr_N64=4, pRawA[i]) + if (n == 4) { + const __uint128_t p0 = (__uint128_t)a[0] * b; + const __uint128_t p1 = (__uint128_t)a[1] * b; + const __uint128_t p2 = (__uint128_t)a[2] * b; + const __uint128_t p3 = (__uint128_t)a[3] * b; + + const uint64_t lo0 = (uint64_t)p0; + const uint64_t lo1 = (uint64_t)p1; + const uint64_t lo2 = (uint64_t)p2; + const uint64_t lo3 = (uint64_t)p3; + + uint64_t x0; + uint64_t x1; + uint64_t x2; + uint64_t x3; + + uint64_t c; + + /* + * First carry-chain: + * + * r0 + lo0 + * r1 + hi0 + * r2 + hi1 + * r3 + hi2 + * hi3 + */ + c = add_carry( + &x0, + r[0], + lo0, + 0 + ); + + c = add_carry( + &x1, + r[1], + (uint64_t)(p0 >> 64), + c + ); + + c = add_carry( + &x2, + r[2], + (uint64_t)(p1 >> 64), + c + ); + + c = add_carry( + &x3, + r[3], + (uint64_t)(p2 >> 64), + c + ); + + uint64_t top = (uint64_t)(p3 >> 64) + c; + + /* + * Second carry-chain: + * + * x1 + lo1 + * x2 + lo2 + * x3 + lo3 + */ + c = add_carry(&x1, x1, lo1, 0); + c = add_carry(&x2, x2, lo2, c); + c = add_carry(&x3, x3, lo3, c); + + top += c; + + r[0] = x0; + r[1] = x1; + r[2] = x2; + r[3] = x3; + + return top; + } + + if (n == 5 && a[4] == 0) { + __uint128_t t; + + t = (__uint128_t)r[0] + (__uint128_t)a[0] * (__uint128_t)b; + r[0] = (uint64_t)t; + t >>= 2*MP_N; + + t += (__uint128_t)r[1] + (__uint128_t)a[1] * (__uint128_t)b; + r[1] = (uint64_t)t; + t >>= 2*MP_N; + + t += (__uint128_t)r[2] + (__uint128_t)a[2] * (__uint128_t)b; + r[2] = (uint64_t)t; + t >>= 2*MP_N; + + t += (__uint128_t)r[3] + (__uint128_t)a[3] * (__uint128_t)b; + r[3] = (uint64_t)t; + t >>= 2*MP_N; + + // a[4] == 0, so only propagate carry into r[4]. + t += (__uint128_t)r[4]; + r[4] = (uint64_t)t; + + return (uint64_t)(t >> 2*MP_N); + } + + // mp_addmul(productX, mq, N=5, np0) + if (n == 5) { + __uint128_t t; + + t = (__uint128_t)r[0] + (__uint128_t)a[0] * (__uint128_t)b; + r[0] = (uint64_t)t; + t >>= 2*MP_N; + + t += (__uint128_t)r[1] + (__uint128_t)a[1] * (__uint128_t)b; + r[1] = (uint64_t)t; + t >>= 2*MP_N; + + t += (__uint128_t)r[2] + (__uint128_t)a[2] * (__uint128_t)b; + r[2] = (uint64_t)t; + t >>= 2*MP_N; + + t += (__uint128_t)r[3] + (__uint128_t)a[3] * (__uint128_t)b; + r[3] = (uint64_t)t; + t >>= 2*MP_N; + + t += (__uint128_t)r[4] + (__uint128_t)a[4] * (__uint128_t)b; + r[4] = (uint64_t)t; + + return (uint64_t)(t >> 2*MP_N); + } + +#endif + __uint128_t carry = 0; for (size_t i = 0; i < n; i++) { @@ -1385,6 +2233,7 @@ uint64_t mp_addmul(uint64_t *r, const uint64_t *a, size_t n, uint64_t b) } return (uint64_t)carry; + #else uint64_t carry = 0; @@ -1406,23 +2255,34 @@ uint64_t mp_addmul(uint64_t *r, const uint64_t *a, size_t n, uint64_t b) #endif } -void mp_and(uint64_t *r, const uint64_t *a, const uint64_t *b) { - for (int i = 0; i < MP_N64; i++) r[i] = a[i] & b[i]; -} +uint64_t mp_add(uint64_t *r, const uint64_t *a, size_t an, const uint64_t *b, size_t bn) { +#if defined(__SIZEOF_INT128__) + if (an == 5 && bn == 4) { + __uint128_t t; -void mp_or(uint64_t *r, const uint64_t *a, const uint64_t *b) { - for (int i = 0; i < MP_N64; i++) r[i] = a[i] | b[i]; -} + t = (__uint128_t)a[0] + b[0]; + r[0] = (uint64_t)t; + t >>= 64; -void mp_xor(uint64_t *r, const uint64_t *a, const uint64_t *b) { - for (int i = 0; i < MP_N64; i++) r[i] = a[i] ^ b[i]; -} + t += (__uint128_t)a[1] + b[1]; + r[1] = (uint64_t)t; + t >>= 64; -void mp_not(uint64_t *r, const uint64_t *a) { - for (int i = 0; i < MP_N64; i++) r[i] = ~a[i]; -} + t += (__uint128_t)a[2] + b[2]; + r[2] = (uint64_t)t; + t >>= 64; + + t += (__uint128_t)a[3] + b[3]; + r[3] = (uint64_t)t; + t >>= 64; + + t += (__uint128_t)a[4]; + r[4] = (uint64_t)t; + + return (uint64_t)(t >> 64); + } +#endif -uint64_t mp_add(uint64_t *r, const uint64_t *a, size_t an, const uint64_t *b, size_t bn) { size_t n = (an > bn) ? an : bn; uint64_t carry = 0; @@ -1485,7 +2345,53 @@ static inline void mp_sub_mod(uint64_t *x, const uint64_t *y, const uint64_t *mo #endif } +static inline void mp_shr1(uint64_t *x) { +#if MP_N64 == 4 + const uint64_t x0 = x[0]; + const uint64_t x1 = x[1]; + const uint64_t x2 = x[2]; + const uint64_t x3 = x[3]; + + x[0] = (x0 >> 1) | (x1 << 63); + x[1] = (x1 >> 1) | (x2 << 63); + x[2] = (x2 >> 1) | (x3 << 63); + x[3] = (x3 >> 1); +#else + uint64_t hi = 0; + + for (int i = MP_N64 - 1; i >= 0; --i) { + uint64_t new_hi = x[i] & 1ULL; + x[i] = (x[i] >> 1) | (hi << 63); + hi = new_hi; + } +#endif +} + static inline void mp_div2_mod(uint64_t *x, const uint64_t *mod) { +#if MP_N64 == 4 + uint64_t hi = 0; + + if (x[0] & 1ULL) { + uint64_t c = 0; + + c = add_carry(&x[0], x[0], mod[0], c); + c = add_carry(&x[1], x[1], mod[1], c); + c = add_carry(&x[2], x[2], mod[2], c); + c = add_carry(&x[3], x[3], mod[3], c); + + hi = c; + } + + const uint64_t x0 = x[0]; + const uint64_t x1 = x[1]; + const uint64_t x2 = x[2]; + const uint64_t x3 = x[3]; + + x[0] = (x0 >> 1) | (x1 << 63); + x[1] = (x1 >> 1) | (x2 << 63); + x[2] = (x2 >> 1) | (x3 << 63); + x[3] = (x3 >> 1) | (hi << 63); +#else uint64_t hi = 0; if (x[0] & 1ULL) { @@ -1498,12 +2404,13 @@ static inline void mp_div2_mod(uint64_t *x, const uint64_t *mod) { for (int i = MP_N64 - 1; i >= 0; --i) { uint64_t new_hi = x[i] & 1ULL; - x[i] = (x[i] >> 1) | (hi << (2*MP_N - 1)); + x[i] = (x[i] >> 1) | (hi << 63); hi = new_hi; } +#endif } -bool mp_inv_mod(uint64_t *r, const uint64_t *a, const uint64_t *mod) { +static inline bool mp_inv_mod_bin(uint64_t *r, const uint64_t *a, const uint64_t *mod) { uint64_t u[MP_N64], v[MP_N64]; mp_copy(u, a); mp_copy(v, mod); @@ -1517,12 +2424,12 @@ bool mp_inv_mod(uint64_t *r, const uint64_t *a, const uint64_t *mod) { while (!mp_is_one(u) && !mp_is_one(v)) { while (mp_is_even(u)) { - mp_shr(u, u, 1u); + mp_shr1(u); mp_div2_mod(x1, mod); } while (mp_is_even(v)) { - mp_shr(v, v, 1u); + mp_shr1(v); mp_div2_mod(x2, mod); } @@ -1538,4 +2445,354 @@ bool mp_inv_mod(uint64_t *r, const uint64_t *a, const uint64_t *mod) { if (mp_is_one(u)) mp_copy(r, x1); else mp_copy(r, x2); return true; +} + +#if MP_N64 == 4 && defined(__SIZEOF_INT128__) + +struct MpInvSigned62 { + int64_t v[5]; +}; + +struct MpInvTrans2x2 { + int64_t u; + int64_t v; + int64_t q; + int64_t r; +}; + +static constexpr uint64_t MP_INV_M62_U = UINT64_MAX >> 2; +static constexpr int64_t MP_INV_M62_S = (int64_t)(UINT64_MAX >> 2); + +static inline bool mp_inv_is_zero4(const uint64_t *x) { + return (x[0] | x[1] | x[2] | x[3]) == 0; +} + +static inline uint64_t mp_inv_mod2_62(uint64_t a) { + uint64_t x = 1; + + x *= 2ULL - a * x; + x *= 2ULL - a * x; + x *= 2ULL - a * x; + x *= 2ULL - a * x; + x *= 2ULL - a * x; + x *= 2ULL - a * x; + + return x & MP_INV_M62_U; +} + +static inline void mp_inv_to_signed62(MpInvSigned62 *r, const uint64_t *x) { + r->v[0] = (int64_t)(x[0] & MP_INV_M62_U); + r->v[1] = (int64_t)((x[0] >> 62) | ((x[1] & ((1ULL << 60) - 1ULL)) << 2)); + r->v[2] = (int64_t)((x[1] >> 60) | ((x[2] & ((1ULL << 58) - 1ULL)) << 4)); + r->v[3] = (int64_t)((x[2] >> 58) | ((x[3] & ((1ULL << 56) - 1ULL)) << 6)); + r->v[4] = (int64_t)(x[3] >> 56); +} + +static inline void mp_inv_from_signed62(uint64_t *r, const MpInvSigned62 *x) { + const uint64_t x0 = (uint64_t)x->v[0]; + const uint64_t x1 = (uint64_t)x->v[1]; + const uint64_t x2 = (uint64_t)x->v[2]; + const uint64_t x3 = (uint64_t)x->v[3]; + const uint64_t x4 = (uint64_t)x->v[4]; + + r[0] = x0 | (x1 << 62); + r[1] = (x1 >> 2) | (x2 << 60); + r[2] = (x2 >> 4) | (x3 << 58); + r[3] = (x3 >> 6) | (x4 << 56); +} + +static inline int mp_inv_ctz64_var(uint64_t x) { + return __builtin_ctzll(x); +} + +static inline int64_t mp_inv_divsteps_62_var( + int64_t eta, + uint64_t f0, + uint64_t g0, + MpInvTrans2x2 *t +) { + uint64_t u = 1; + uint64_t v = 0; + uint64_t q = 0; + uint64_t r = 1; + uint64_t f = f0; + uint64_t g = g0; + + int i = 62; + + for (;;) { + const int zeros = mp_inv_ctz64_var(g | (UINT64_MAX << i)); + + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + + if (i == 0) { + break; + } + + int limit; + uint64_t mask; + uint32_t w; + + if (eta < 0) { + uint64_t tmp; + + eta = -eta; + + tmp = f; f = g; g = -tmp; + tmp = u; u = q; q = -tmp; + tmp = v; v = r; r = -tmp; + + limit = ((int)eta + 1 > i) ? i : ((int)eta + 1); + mask = (UINT64_MAX >> (64 - limit)) & 63U; + w = (uint32_t)((f * g * (f * f - 2U)) & mask); + } else { + limit = ((int)eta + 1 > i) ? i : ((int)eta + 1); + mask = (UINT64_MAX >> (64 - limit)) & 15U; + w = (uint32_t)(f + (((f + 1U) & 4U) << 1)); + w = (uint32_t)((-(uint64_t)w * g) & mask); + } + + g += f * w; + q += u * w; + r += v * w; + } + + t->u = (int64_t)u; + t->v = (int64_t)v; + t->q = (int64_t)q; + t->r = (int64_t)r; + + return eta; +} + +static inline void mp_inv_update_fg_62_var( + int len, + MpInvSigned62 *f, + MpInvSigned62 *g, + const MpInvTrans2x2 *t +) { + const int64_t u = t->u; + const int64_t v = t->v; + const int64_t q = t->q; + const int64_t r = t->r; + + __int128 cf = (__int128)u * f->v[0] + (__int128)v * g->v[0]; + __int128 cg = (__int128)q * f->v[0] + (__int128)r * g->v[0]; + + cf >>= 62; + cg >>= 62; + + for (int i = 1; i < len; ++i) { + cf += (__int128)u * f->v[i] + (__int128)v * g->v[i]; + cg += (__int128)q * f->v[i] + (__int128)r * g->v[i]; + + f->v[i - 1] = (int64_t)((uint64_t)cf & MP_INV_M62_U); + cf >>= 62; + + g->v[i - 1] = (int64_t)((uint64_t)cg & MP_INV_M62_U); + cg >>= 62; + } + + f->v[len - 1] = (int64_t)cf; + g->v[len - 1] = (int64_t)cg; +} + +static inline void mp_inv_update_de_62( + MpInvSigned62 *d, + MpInvSigned62 *e, + const MpInvTrans2x2 *t, + const MpInvSigned62 *mod, + uint64_t mod_inv62 +) { + const int64_t d0 = d->v[0]; + const int64_t d1 = d->v[1]; + const int64_t d2 = d->v[2]; + const int64_t d3 = d->v[3]; + const int64_t d4 = d->v[4]; + + const int64_t e0 = e->v[0]; + const int64_t e1 = e->v[1]; + const int64_t e2 = e->v[2]; + const int64_t e3 = e->v[3]; + const int64_t e4 = e->v[4]; + + const int64_t u = t->u; + const int64_t v = t->v; + const int64_t q = t->q; + const int64_t r = t->r; + + const int64_t sd = d4 >> 63; + const int64_t se = e4 >> 63; + + int64_t md = (u & sd) + (v & se); + int64_t me = (q & sd) + (r & se); + + __int128 cd = (__int128)u * d0 + (__int128)v * e0; + __int128 ce = (__int128)q * d0 + (__int128)r * e0; + + md -= (int64_t)((mod_inv62 * (uint64_t)cd + (uint64_t)md) & MP_INV_M62_U); + me -= (int64_t)((mod_inv62 * (uint64_t)ce + (uint64_t)me) & MP_INV_M62_U); + + cd += (__int128)mod->v[0] * md; + ce += (__int128)mod->v[0] * me; + cd >>= 62; + ce >>= 62; + + cd += (__int128)u * d1 + (__int128)v * e1 + (__int128)mod->v[1] * md; + ce += (__int128)q * d1 + (__int128)r * e1 + (__int128)mod->v[1] * me; + d->v[0] = (int64_t)((uint64_t)cd & MP_INV_M62_U); + cd >>= 62; + e->v[0] = (int64_t)((uint64_t)ce & MP_INV_M62_U); + ce >>= 62; + + cd += (__int128)u * d2 + (__int128)v * e2 + (__int128)mod->v[2] * md; + ce += (__int128)q * d2 + (__int128)r * e2 + (__int128)mod->v[2] * me; + d->v[1] = (int64_t)((uint64_t)cd & MP_INV_M62_U); + cd >>= 62; + e->v[1] = (int64_t)((uint64_t)ce & MP_INV_M62_U); + ce >>= 62; + + cd += (__int128)u * d3 + (__int128)v * e3 + (__int128)mod->v[3] * md; + ce += (__int128)q * d3 + (__int128)r * e3 + (__int128)mod->v[3] * me; + d->v[2] = (int64_t)((uint64_t)cd & MP_INV_M62_U); + cd >>= 62; + e->v[2] = (int64_t)((uint64_t)ce & MP_INV_M62_U); + ce >>= 62; + + cd += (__int128)u * d4 + (__int128)v * e4 + (__int128)mod->v[4] * md; + ce += (__int128)q * d4 + (__int128)r * e4 + (__int128)mod->v[4] * me; + d->v[3] = (int64_t)((uint64_t)cd & MP_INV_M62_U); + cd >>= 62; + e->v[3] = (int64_t)((uint64_t)ce & MP_INV_M62_U); + ce >>= 62; + + d->v[4] = (int64_t)cd; + e->v[4] = (int64_t)ce; +} + +static inline void mp_inv_normalize_62( + MpInvSigned62 *r, + int64_t sign, + const MpInvSigned62 *mod +) { + int64_t r0 = r->v[0]; + int64_t r1 = r->v[1]; + int64_t r2 = r->v[2]; + int64_t r3 = r->v[3]; + int64_t r4 = r->v[4]; + + int64_t cond_add = r4 >> 63; + + r0 += mod->v[0] & cond_add; + r1 += mod->v[1] & cond_add; + r2 += mod->v[2] & cond_add; + r3 += mod->v[3] & cond_add; + r4 += mod->v[4] & cond_add; + + const int64_t cond_negate = sign >> 63; + + r0 = (r0 ^ cond_negate) - cond_negate; + r1 = (r1 ^ cond_negate) - cond_negate; + r2 = (r2 ^ cond_negate) - cond_negate; + r3 = (r3 ^ cond_negate) - cond_negate; + r4 = (r4 ^ cond_negate) - cond_negate; + + r1 += r0 >> 62; r0 &= MP_INV_M62_S; + r2 += r1 >> 62; r1 &= MP_INV_M62_S; + r3 += r2 >> 62; r2 &= MP_INV_M62_S; + r4 += r3 >> 62; r3 &= MP_INV_M62_S; + + cond_add = r4 >> 63; + + r0 += mod->v[0] & cond_add; + r1 += mod->v[1] & cond_add; + r2 += mod->v[2] & cond_add; + r3 += mod->v[3] & cond_add; + r4 += mod->v[4] & cond_add; + + r1 += r0 >> 62; r0 &= MP_INV_M62_S; + r2 += r1 >> 62; r1 &= MP_INV_M62_S; + r3 += r2 >> 62; r2 &= MP_INV_M62_S; + r4 += r3 >> 62; r3 &= MP_INV_M62_S; + + r->v[0] = r0; + r->v[1] = r1; + r->v[2] = r2; + r->v[3] = r3; + r->v[4] = r4; +} + +static bool mp_inv_mod_safegcd_4(uint64_t *r, const uint64_t *a, const uint64_t *mod) { + if (mp_inv_is_zero4(a)) { + mp_zero(r); + return false; + } + + MpInvSigned62 mod62; + MpInvSigned62 x; + + mp_inv_to_signed62(&mod62, mod); + mp_inv_to_signed62(&x, a); + + const uint64_t mod_inv62 = mp_inv_mod2_62((uint64_t)mod62.v[0]); + + MpInvSigned62 d = {{0, 0, 0, 0, 0}}; + MpInvSigned62 e = {{1, 0, 0, 0, 0}}; + MpInvSigned62 f = mod62; + MpInvSigned62 g = x; + + int len = 5; + int64_t eta = -1; + + for (int guard = 0; guard < 20; ++guard) { + MpInvTrans2x2 t; + + eta = mp_inv_divsteps_62_var(eta, (uint64_t)f.v[0], (uint64_t)g.v[0], &t); + mp_inv_update_de_62(&d, &e, &t, &mod62, mod_inv62); + mp_inv_update_fg_62_var(len, &f, &g, &t); + + if (g.v[0] == 0) { + int64_t cond = 0; + for (int j = 1; j < len; ++j) { + cond |= g.v[j]; + } + if (cond == 0) { + mp_inv_normalize_62(&d, f.v[len - 1], &mod62); + mp_inv_from_signed62(r, &d); + return true; + } + } + + const int64_t fn = f.v[len - 1]; + const int64_t gn = g.v[len - 1]; + int64_t cond = ((int64_t)len - 2) >> 63; + + cond |= fn ^ (fn >> 63); + cond |= gn ^ (gn >> 63); + + if (cond == 0) { + f.v[len - 2] |= (uint64_t)fn << 62; + g.v[len - 2] |= (uint64_t)gn << 62; + --len; + } + } + + mp_zero(r); + return false; +} + +#endif + +bool mp_inv_mod(uint64_t *r, const uint64_t *a, const uint64_t *mod) { +#if MP_N64 == 4 && defined(__SIZEOF_INT128__) + if ((mod[0] & 1ULL) != 0) { + return mp_inv_mod_safegcd_4(r, a, mod); + } +#endif + + return mp_inv_mod_bin(r, a, mod); } \ No newline at end of file diff --git a/src/mp.hpp.ejs b/src/mp.hpp.ejs index 0df988f..4f42df9 100644 --- a/src/mp.hpp.ejs +++ b/src/mp.hpp.ejs @@ -11,11 +11,198 @@ typedef uint64_t mp_uint_t[MP_N64]; -void mp_set(uint64_t *r, uint64_t a); -void mp_copy(uint64_t *r, const uint64_t *a); +static inline void mp_zero(uint64_t *r) { +#if MP_N64 == 4 + r[0] = 0; + r[1] = 0; + r[2] = 0; + r[3] = 0; +#else + std::memset(r, 0, MP_N64 * sizeof(uint64_t)); +#endif +} + +static inline void mp_copy(uint64_t *r, const uint64_t *a) { + if (r == a) return; + +#if MP_N64 == 4 + const uintptr_t rp = reinterpret_cast(r); + const uintptr_t ap = reinterpret_cast(a); + static constexpr uintptr_t BYTES = MP_N64 * sizeof(uint64_t); + + if (rp > ap && rp < ap + BYTES) { + r[3] = a[3]; + r[2] = a[2]; + r[1] = a[1]; + r[0] = a[0]; + return; + } + + r[0] = a[0]; + r[1] = a[1]; + r[2] = a[2]; + r[3] = a[3]; +#else + std::memmove(r, a, MP_N64 * sizeof(uint64_t)); +#endif +} + +static inline int mp_cmp(const uint64_t *a, const uint64_t *b) { +#if MP_N64 == 4 + uint64_t ai; + uint64_t bi; + + ai = a[3]; + bi = b[3]; + if (__builtin_expect(ai != bi, 1)) { + return (ai > bi) ? 1 : -1; + } + + ai = a[2]; + bi = b[2]; + if (__builtin_expect(ai != bi, 0)) { + return (ai > bi) ? 1 : -1; + } + + ai = a[1]; + bi = b[1]; + if (__builtin_expect(ai != bi, 0)) { + return (ai > bi) ? 1 : -1; + } + + ai = a[0]; + bi = b[0]; + if (__builtin_expect(ai != bi, 0)) { + return (ai > bi) ? 1 : -1; + } + + return 0; +#else + for (int i = MP_N64 - 1; i >= 0; --i) { + if (a[i] < b[i]) return -1; + if (a[i] > b[i]) return 1; + } + + return 0; +#endif +} + +static inline void mp_shl(uint64_t *r, const uint64_t *a, uint64_t k) { + if (k >= (uint64_t)MP_N64 * 64u) { + mp_zero(r); + return; + } + if (k == 0) { + mp_copy(r, a); + return; + } + + const auto wordShift = (uint32_t)(k >> 6); + const auto bitShift = (uint32_t)(k & 63u); + + if (bitShift == 0) { + for (int i = MP_N64 - 1; i >= 0; --i) { + int si = i - (int)wordShift; + r[i] = si >= 0 ? a[si] : 0; + } + return; + } + + const uint32_t inv = 64u - bitShift; + for (int i = MP_N64 - 1; i >= 0; --i) { + int si0 = i - (int)wordShift; + int si1 = si0 - 1; + + uint64_t lo = (si0 >= 0) ? a[si0] : 0; + uint64_t hi = (si1 >= 0) ? a[si1] : 0; -int mp_cmp(const uint64_t *a, const uint64_t *b); -void mp_zero(uint64_t *r); + r[i] = lo << bitShift | hi >> inv; + } +} + +static inline void mp_shr(uint64_t *r, const uint64_t *a, uint64_t k) { + if (k >= (uint64_t)MP_N64 * 64u) { + mp_zero(r); + return; + } + if (k == 0) { + mp_copy(r, a); + return; + } + + const auto wordShift = (uint32_t)(k >> 6); + const auto bitShift = (uint32_t)(k & 63u); + + if (bitShift == 0) { + for (uint32_t i = 0; i < MP_N64; ++i) { + uint32_t si = i + wordShift; + r[i] = (si < (uint32_t)MP_N64) ? a[si] : 0; + } + return; + } + + const uint32_t inv = 64u - bitShift; + for (uint32_t i = 0; i < MP_N64; ++i) { + uint32_t si0 = i + wordShift; + uint32_t si1 = si0 + 1; + + uint64_t lo = (si0 < (uint32_t)MP_N64) ? a[si0] : 0; + uint64_t hi = (si1 < (uint32_t)MP_N64) ? a[si1] : 0; + + r[i] = lo >> bitShift | hi << inv; + } +} + +static inline void mp_and(uint64_t *r, const uint64_t *a, const uint64_t *b) { +#if MP_N64 == 4 + r[0] = a[0] & b[0]; + r[1] = a[1] & b[1]; + r[2] = a[2] & b[2]; + r[3] = a[3] & b[3]; +#else + for (int i = 0; i < MP_N64; i++) r[i] = a[i] & b[i]; +#endif +} + +static inline void mp_or(uint64_t *r, const uint64_t *a, const uint64_t *b) { +#if MP_N64 == 4 + r[0] = a[0] | b[0]; + r[1] = a[1] | b[1]; + r[2] = a[2] | b[2]; + r[3] = a[3] | b[3]; +#else + for (int i = 0; i < MP_N64; i++) r[i] = a[i] | b[i]; +#endif +} + +static inline void mp_xor(uint64_t *r, const uint64_t *a, const uint64_t *b) { +#if MP_N64 == 4 + r[0] = a[0] ^ b[0]; + r[1] = a[1] ^ b[1]; + r[2] = a[2] ^ b[2]; + r[3] = a[3] ^ b[3]; +#else + for (int i = 0; i < MP_N64; i++) r[i] = a[i] ^ b[i]; +#endif +} + +static inline void mp_not(uint64_t *r, const uint64_t *a) { +#if MP_N64 == 4 + r[0] = ~a[0]; + r[1] = ~a[1]; + r[2] = ~a[2]; + r[3] = ~a[3]; +#else + for (int i = 0; i < MP_N64; i++) r[i] = ~a[i]; +#endif +} + +static inline bool mp_tstbit(const uint64_t *a, size_t bit) { + if (bit >= (size_t)MP_N64 * 64u) return false; + return a[bit >> 6] >> (bit & 63u) & 1ULL; +} + +void mp_set(uint64_t *r, uint64_t a); bool mp_is_zero(const uint64_t *a); uint64_t mp_add(uint64_t *r, const uint64_t *a, const uint64_t *b); @@ -28,15 +215,6 @@ uint64_t mp_sub(uint64_t *r, const uint64_t *a, uint64_t b); uint64_t mp_mul(uint64_t *r, const uint64_t *a, uint64_t b); uint64_t mp_addmul(uint64_t *r, const uint64_t *a, size_t n, uint64_t b); -void mp_and(uint64_t *r, const uint64_t *a, const uint64_t *b); -void mp_or(uint64_t *r, const uint64_t *a, const uint64_t *b); -void mp_xor(uint64_t *r, const uint64_t *a, const uint64_t *b); -void mp_not(uint64_t *r, const uint64_t *a); - -bool mp_tstbit(const uint64_t *a, size_t bit); -void mp_shl(uint64_t *r, const uint64_t *a, uint64_t k); -void mp_shr(uint64_t *r, const uint64_t *a, uint64_t k); - int32_t mp_get_int32(const mp_uint_t a); bool mp_fits_int32(const uint64_t *a); bool mp_set(uint64_t *r, const char *str, uint32_t base);