Compare commits
9 Commits
main
...
opt-div-sp
Author | SHA1 | Date | |
---|---|---|---|
a329c455eb | |||
f2ae81fb54 | |||
db53bfdcff | |||
4fbd4a579a | |||
3b578c7f60 | |||
bcec853fe6 | |||
fd6e5e208e | |||
ddf108aa3e | |||
f72f369b2a |
@ -15,7 +15,7 @@ class int2048 {
|
|||||||
* num_length is the length of the integer, (num_length+kNum-1)/kNum is the
|
* num_length is the length of the integer, (num_length+kNum-1)/kNum is the
|
||||||
* length of val with data. Note that position in val without data is 0.
|
* length of val with data. Note that position in val without data is 0.
|
||||||
*/
|
*/
|
||||||
const static int kMod = 100000000, kNum = 8, kDefaultLength = 10;
|
const static int kStoreBase = 100000000, kNum = 8, kDefaultLength = 10;
|
||||||
const static int kMemAdditionScalar = 2, kMemDeleteScalar = 4;
|
const static int kMemAdditionScalar = 2, kMemDeleteScalar = 4;
|
||||||
/**
|
/**
|
||||||
* the follow data used by NTT is generated by this code:
|
* the follow data used by NTT is generated by this code:
|
||||||
@ -39,7 +39,7 @@ root= 6
|
|||||||
const static __int128_t kNTTMod = 180143985094819841ll;
|
const static __int128_t kNTTMod = 180143985094819841ll;
|
||||||
const static __int128_t kNTTRoot = 6;
|
const static __int128_t kNTTRoot = 6;
|
||||||
const static int kNTTBlockNum = 4;
|
const static int kNTTBlockNum = 4;
|
||||||
const static int kNTTBlcokBase = 10000;
|
const static int kNTTBlockBase = 10000;
|
||||||
|
|
||||||
size_t buf_length = 0;
|
size_t buf_length = 0;
|
||||||
int *val = nullptr;
|
int *val = nullptr;
|
||||||
@ -50,6 +50,8 @@ root= 6
|
|||||||
void NTTTransform(__int128_t *, int, bool);
|
void NTTTransform(__int128_t *, int, bool);
|
||||||
|
|
||||||
void RightMoveBy(int);
|
void RightMoveBy(int);
|
||||||
|
void ProcessHalfBlock();
|
||||||
|
void RestoreHalfBlock();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int2048();
|
int2048();
|
||||||
@ -65,8 +67,8 @@ root= 6
|
|||||||
void ClaimMem(size_t);
|
void ClaimMem(size_t);
|
||||||
|
|
||||||
inline friend int UnsignedCmp(const int2048 &, const int2048 &);
|
inline friend int UnsignedCmp(const int2048 &, const int2048 &);
|
||||||
inline friend void UnsignedAdd(int2048 &, const int2048 *);
|
inline friend void UnsignedAdd(int2048 &, const int2048 *, bool);
|
||||||
inline friend void UnsignedMinus(int2048 &, const int2048 *);
|
inline friend void UnsignedMinus(int2048 &, const int2048 *, bool);
|
||||||
int2048 &add(const int2048 &);
|
int2048 &add(const int2048 &);
|
||||||
friend int2048 add(int2048, const int2048 &);
|
friend int2048 add(int2048, const int2048 &);
|
||||||
|
|
||||||
@ -85,7 +87,7 @@ root= 6
|
|||||||
int2048 &operator-=(const int2048 &);
|
int2048 &operator-=(const int2048 &);
|
||||||
friend int2048 operator-(int2048, const int2048 &);
|
friend int2048 operator-(int2048, const int2048 &);
|
||||||
|
|
||||||
inline friend void UnsignedMultiply(int2048 &, const int2048 *);
|
inline friend void UnsignedMultiply(int2048 &, const int2048 *, bool, int);
|
||||||
int2048 &Multiply(const int2048 &);
|
int2048 &Multiply(const int2048 &);
|
||||||
friend int2048 Multiply(int2048, const int2048 &);
|
friend int2048 Multiply(int2048, const int2048 &);
|
||||||
|
|
||||||
|
438
src/int2048.cpp
438
src/int2048.cpp
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "int2048.h"
|
#include "int2048.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -148,6 +149,12 @@ void int2048::print() {
|
|||||||
delete[] buf;
|
delete[] buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Claim memory for the number.
|
||||||
|
*
|
||||||
|
* @details warning: ClaimMem doesn't change num_length, so you should change it
|
||||||
|
* manually.
|
||||||
|
*/
|
||||||
void int2048::ClaimMem(size_t number_length) {
|
void int2048::ClaimMem(size_t number_length) {
|
||||||
size_t new_number_blocks = (number_length + kNum - 1) / kNum;
|
size_t new_number_blocks = (number_length + kNum - 1) / kNum;
|
||||||
if (new_number_blocks > buf_length) {
|
if (new_number_blocks > buf_length) {
|
||||||
@ -172,9 +179,12 @@ inline int UnsignedCmp(const int2048 &A, const int2048 &B) {
|
|||||||
if (A.val[i] != B.val[i]) return A.val[i] < B.val[i] ? -1 : 1;
|
if (A.val[i] != B.val[i]) return A.val[i] < B.val[i] ? -1 : 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
inline void UnsignedMinus(int2048 &, const int2048 *, bool inverse = false);
|
||||||
|
|
||||||
inline void UnsignedAdd(int2048 &A, const int2048 *const pB) {
|
inline void UnsignedAdd(int2048 &A, const int2048 *const pB,
|
||||||
|
bool inverse = false) {
|
||||||
if (&A == pB) throw "UnsignedAdd: A and B are the same object";
|
if (&A == pB) throw "UnsignedAdd: A and B are the same object";
|
||||||
|
if (!inverse) {
|
||||||
A.ClaimMem(std::max(A.num_length, pB->num_length) + 2);
|
A.ClaimMem(std::max(A.num_length, pB->num_length) + 2);
|
||||||
for (int i = 0;
|
for (int i = 0;
|
||||||
i < (std::max(A.num_length, pB->num_length) + int2048::kNum - 1) /
|
i < (std::max(A.num_length, pB->num_length) + int2048::kNum - 1) /
|
||||||
@ -182,8 +192,8 @@ inline void UnsignedAdd(int2048 &A, const int2048 *const pB) {
|
|||||||
i++) {
|
i++) {
|
||||||
if (i < (pB->num_length + int2048::kNum - 1) / int2048::kNum)
|
if (i < (pB->num_length + int2048::kNum - 1) / int2048::kNum)
|
||||||
A.val[i] += pB->val[i];
|
A.val[i] += pB->val[i];
|
||||||
if (i + 1 < A.buf_length) A.val[i + 1] += A.val[i] / int2048::kMod;
|
if (i + 1 < A.buf_length) A.val[i + 1] += A.val[i] / int2048::kStoreBase;
|
||||||
A.val[i] %= int2048::kMod;
|
A.val[i] %= int2048::kStoreBase;
|
||||||
}
|
}
|
||||||
A.num_length = std::max(A.num_length, pB->num_length);
|
A.num_length = std::max(A.num_length, pB->num_length);
|
||||||
const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
||||||
@ -192,6 +202,24 @@ inline void UnsignedAdd(int2048 &A, const int2048 *const pB) {
|
|||||||
kPow10[A.num_length % int2048::kNum] >
|
kPow10[A.num_length % int2048::kNum] >
|
||||||
0)
|
0)
|
||||||
A.num_length++;
|
A.num_length++;
|
||||||
|
} else {
|
||||||
|
assert(("this code shouldn't be executed", 0));
|
||||||
|
assert(A.num_length % int2048::kNum == 0);
|
||||||
|
assert(pB->num_length % int2048::kNum == 0);
|
||||||
|
A.ClaimMem(std::max(A.num_length, pB->num_length));
|
||||||
|
A.num_length = std::max(A.num_length, pB->num_length);
|
||||||
|
for (int i = std::max(A.num_length, pB->num_length) / int2048::kNum - 1;
|
||||||
|
i >= 0; i--) {
|
||||||
|
if (i < pB->num_length / int2048::kNum) A.val[i] += pB->val[i];
|
||||||
|
if (A.val[i] >= int2048::kStoreBase && i - 1 >= 0) {
|
||||||
|
A.val[i - 1] += A.val[i] / int2048::kStoreBase;
|
||||||
|
A.val[i] %= int2048::kStoreBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (A.num_length > int2048::kNum &&
|
||||||
|
A.val[A.num_length / int2048::kNum - 1] == 0)
|
||||||
|
A.num_length -= int2048::kNum;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加上一个大整数
|
// 加上一个大整数
|
||||||
@ -237,13 +265,14 @@ int2048 add(int2048 A, const int2048 &B) {
|
|||||||
return std::move(A.add(B));
|
return std::move(A.add(B));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void UnsignedMinus(int2048 &A, const int2048 *const pB) {
|
inline void UnsignedMinus(int2048 &A, const int2048 *const pB, bool inverse) {
|
||||||
if (&A == pB) throw "UnsignedMinus: A and B are the same object";
|
if (&A == pB) throw "UnsignedMinus: A and B are the same object";
|
||||||
|
if (!inverse) {
|
||||||
for (int i = 0; i < (pB->num_length + int2048::kNum - 1) / int2048::kNum;
|
for (int i = 0; i < (pB->num_length + int2048::kNum - 1) / int2048::kNum;
|
||||||
i++) {
|
i++) {
|
||||||
A.val[i] -= pB->val[i];
|
A.val[i] -= pB->val[i];
|
||||||
if (A.val[i] < 0) {
|
if (A.val[i] < 0 && i + 1 < A.buf_length) {
|
||||||
A.val[i] += int2048::kMod;
|
A.val[i] += int2048::kStoreBase;
|
||||||
A.val[i + 1]--;
|
A.val[i + 1]--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,6 +285,29 @@ inline void UnsignedMinus(int2048 &A, const int2048 *const pB) {
|
|||||||
A.num_length = new_length;
|
A.num_length = new_length;
|
||||||
if (A.num_length == 0) A.num_length = 1;
|
if (A.num_length == 0) A.num_length = 1;
|
||||||
A.ClaimMem(A.num_length);
|
A.ClaimMem(A.num_length);
|
||||||
|
} else {
|
||||||
|
assert(A.num_length % int2048::kNum == 0);
|
||||||
|
assert(pB->num_length % int2048::kNum == 0);
|
||||||
|
int blocks_A = A.num_length / int2048::kNum;
|
||||||
|
int blocks_B = pB->num_length / int2048::kNum;
|
||||||
|
if (blocks_A < blocks_B) {
|
||||||
|
A.ClaimMem(blocks_B * int2048::kNum);
|
||||||
|
A.num_length = blocks_B * int2048::kNum;
|
||||||
|
blocks_A = blocks_B;
|
||||||
|
}
|
||||||
|
for (int i = (pB->num_length + int2048::kNum - 1) / int2048::kNum - 1;
|
||||||
|
i >= 0; i--) {
|
||||||
|
if (i < blocks_B && i < blocks_A) A.val[i] -= pB->val[i];
|
||||||
|
if (i < blocks_A && A.val[i] < 0 && i - 1 >= 0) {
|
||||||
|
A.val[i] += int2048::kStoreBase;
|
||||||
|
A.val[i - 1]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (A.num_length > int2048::kNum &&
|
||||||
|
A.val[A.num_length / int2048::kNum - 1] == 0)
|
||||||
|
A.num_length -= int2048::kNum;
|
||||||
|
A.ClaimMem(A.num_length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 减去一个大整数
|
// 减去一个大整数
|
||||||
@ -362,6 +414,63 @@ __int128_t int2048::QuickPow(__int128_t v, long long q) {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @brief Move the number to the left by L digits. That is, v'=v*(10^L)
|
||||||
|
// */
|
||||||
|
// void int2048::LeftMoveBy(int L) {
|
||||||
|
// const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
||||||
|
// 100000, 1000000, 10000000, 100000000};
|
||||||
|
// int big_move = L / int2048::kNum;
|
||||||
|
// int small_move = L % int2048::kNum;
|
||||||
|
// this->ClaimMem(this->num_length + L);
|
||||||
|
// for (int i = this->buf_length - 1; i >= big_move; i--) {
|
||||||
|
// this->val[i] = this->val[i - big_move];
|
||||||
|
// }
|
||||||
|
// for (int i = 0; i < big_move; i++) {
|
||||||
|
// this->val[i] = 0;
|
||||||
|
// }
|
||||||
|
// this->num_length += big_move * int2048::kNum;
|
||||||
|
// if (small_move == 0) return;
|
||||||
|
// for (int i = this->buf_length - 1; i >= 0; i--) {
|
||||||
|
// (this->val[i] *= kPow10[small_move]) %= int2048::kStoreBase;
|
||||||
|
// if (i - 1 >= 0) {
|
||||||
|
// this->val[i] += this->val[i - 1] / kPow10[int2048::kNum - small_move];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move the number to the right by L digits. That is, v'=v//(10^L)
|
||||||
|
*/
|
||||||
|
void int2048::RightMoveBy(int L) {
|
||||||
|
if (L >= this->num_length) {
|
||||||
|
this->num_length = 1;
|
||||||
|
this->val[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int big_move = L / int2048::kNum;
|
||||||
|
int small_move = L % int2048::kNum;
|
||||||
|
for (int i = 0; i < this->buf_length - big_move; i++) {
|
||||||
|
this->val[i] = this->val[i + big_move];
|
||||||
|
}
|
||||||
|
for (int i = this->buf_length - big_move; i < this->buf_length; i++) {
|
||||||
|
this->val[i] = 0;
|
||||||
|
}
|
||||||
|
this->num_length -= big_move * int2048::kNum;
|
||||||
|
if (small_move == 0) return;
|
||||||
|
const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
||||||
|
100000, 1000000, 10000000, 100000000};
|
||||||
|
for (int i = 0; i < this->buf_length; i++) {
|
||||||
|
this->val[i] /= kPow10[small_move];
|
||||||
|
if (i + 1 < this->buf_length) {
|
||||||
|
this->val[i] += this->val[i + 1] % kPow10[small_move] *
|
||||||
|
kPow10[int2048::kNum - small_move];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->num_length -= small_move;
|
||||||
|
}
|
||||||
|
|
||||||
void int2048::NTTTransform(__int128_t *a, int NTT_blocks,
|
void int2048::NTTTransform(__int128_t *a, int NTT_blocks,
|
||||||
bool inverse = false) {
|
bool inverse = false) {
|
||||||
for (int i = 1, j = 0; i < NTT_blocks; i++) {
|
for (int i = 1, j = 0; i < NTT_blocks; i++) {
|
||||||
@ -391,42 +500,82 @@ void int2048::NTTTransform(__int128_t *a, int NTT_blocks,
|
|||||||
for (int i = 0; i < NTT_blocks; i++) (a[i] *= inv) %= int2048::kNTTMod;
|
for (int i = 0; i < NTT_blocks; i++) (a[i] *= inv) %= int2048::kNTTMod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline void UnsignedMultiply(int2048 &A, const int2048 *pB) {
|
inline void UnsignedMultiply(int2048 &A, const int2048 *pB,
|
||||||
|
bool inverse = false, int lenght_limit = 0) {
|
||||||
if (&A == pB) throw "UnsignedMultiply: A and B are the same object";
|
if (&A == pB) throw "UnsignedMultiply: A and B are the same object";
|
||||||
int blocks_of_A = ((A.num_length + int2048::kNum - 1) / int2048::kNum);
|
int blocks_of_A = ((A.num_length + int2048::kNum - 1) / int2048::kNum);
|
||||||
int blocks_of_B = ((pB->num_length + int2048::kNum - 1) / int2048::kNum);
|
int blocks_of_B = ((pB->num_length + int2048::kNum - 1) / int2048::kNum);
|
||||||
|
if (inverse) {
|
||||||
|
assert(pB->num_length % int2048::kNum == 0);
|
||||||
|
lenght_limit = std::min(lenght_limit, pB->num_length);
|
||||||
|
blocks_of_B = lenght_limit / int2048::kNum;
|
||||||
|
// assert(blocks_of_B ==
|
||||||
|
// ((pB->num_length + int2048::kNum - 1) / int2048::kNum));
|
||||||
|
}
|
||||||
int max_blocks = blocks_of_A + blocks_of_B;
|
int max_blocks = blocks_of_A + blocks_of_B;
|
||||||
int NTT_blocks = 1;
|
int NTT_blocks = 2;
|
||||||
while (NTT_blocks < (max_blocks << 1)) NTT_blocks <<= 1;
|
while (NTT_blocks < (max_blocks << 1)) NTT_blocks <<= 1;
|
||||||
__int128_t *pDA = new __int128_t[NTT_blocks]();
|
__int128_t *pDA = new __int128_t[NTT_blocks]();
|
||||||
__int128_t *pDB = new __int128_t[NTT_blocks]();
|
__int128_t *pDB = new __int128_t[NTT_blocks]();
|
||||||
__int128_t *pDC = new __int128_t[NTT_blocks]();
|
__int128_t *pDC = new __int128_t[NTT_blocks]();
|
||||||
|
if (!inverse) {
|
||||||
for (int i = 0; i < blocks_of_A; i++) {
|
for (int i = 0; i < blocks_of_A; i++) {
|
||||||
pDA[i << 1] = A.val[i] % int2048::kNTTBlcokBase;
|
pDA[i << 1] = A.val[i] % int2048::kNTTBlockBase;
|
||||||
pDA[(i << 1) | 1] = A.val[i] / int2048::kNTTBlcokBase;
|
pDA[(i << 1) | 1] = A.val[i] / int2048::kNTTBlockBase;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < blocks_of_B; i++) {
|
for (int i = 0; i < blocks_of_B; i++) {
|
||||||
pDB[i << 1] = pB->val[i] % int2048::kNTTBlcokBase;
|
pDB[i << 1] = pB->val[i] % int2048::kNTTBlockBase;
|
||||||
pDB[(i << 1) | 1] = pB->val[i] / int2048::kNTTBlcokBase;
|
pDB[(i << 1) | 1] = pB->val[i] / int2048::kNTTBlockBase;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(A.num_length % int2048::kNum == 0);
|
||||||
|
assert(pB->num_length % int2048::kNum == 0);
|
||||||
|
pDA[0] = A.val[0];
|
||||||
|
for (int i = 1; i < blocks_of_A; i++) {
|
||||||
|
pDA[i << 1] = A.val[i] % int2048::kNTTBlockBase;
|
||||||
|
pDA[(i << 1) - 1] = A.val[i] / int2048::kNTTBlockBase;
|
||||||
|
}
|
||||||
|
pDB[0] = pB->val[0];
|
||||||
|
for (int i = 1; i < blocks_of_B; i++) {
|
||||||
|
pDB[i << 1] = pB->val[i] % int2048::kNTTBlockBase;
|
||||||
|
pDB[(i << 1) - 1] = pB->val[i] / int2048::kNTTBlockBase;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
A.NTTTransform(pDA, NTT_blocks);
|
A.NTTTransform(pDA, NTT_blocks);
|
||||||
A.NTTTransform(pDB, NTT_blocks);
|
A.NTTTransform(pDB, NTT_blocks);
|
||||||
for (int i = 0; i < NTT_blocks; i++)
|
for (int i = 0; i < NTT_blocks; i++)
|
||||||
pDC[i] = (pDA[i] * pDB[i]) % int2048::kNTTMod;
|
pDC[i] = (pDA[i] * pDB[i]) % int2048::kNTTMod;
|
||||||
A.NTTTransform(pDC, NTT_blocks, true);
|
A.NTTTransform(pDC, NTT_blocks, true);
|
||||||
|
if (!inverse) {
|
||||||
for (int i = 0; i < NTT_blocks - 1; i++) {
|
for (int i = 0; i < NTT_blocks - 1; i++) {
|
||||||
pDC[i + 1] += pDC[i] / int2048::kNTTBlcokBase;
|
pDC[i + 1] += pDC[i] / int2048::kNTTBlockBase;
|
||||||
pDC[i] %= int2048::kNTTBlcokBase;
|
pDC[i] %= int2048::kNTTBlockBase;
|
||||||
}
|
}
|
||||||
if (pDC[NTT_blocks - 1] >= int2048::kNTTBlcokBase)
|
if (pDC[NTT_blocks - 1] >= int2048::kNTTBlockBase)
|
||||||
throw "UnsignedMultiply: NTT result overflow";
|
throw "UnsignedMultiply: NTT result overflow";
|
||||||
|
} else {
|
||||||
|
for (int i = NTT_blocks - 1; i > 0; i--) {
|
||||||
|
if (i - 1 >= 0) pDC[i - 1] += pDC[i] / int2048::kNTTBlockBase;
|
||||||
|
pDC[i] %= int2048::kNTTBlockBase;
|
||||||
|
}
|
||||||
|
if (pDC[0] >= int2048::kNTTBlockBase)
|
||||||
|
throw "UnsignedMultiply: NTT result overflow";
|
||||||
|
}
|
||||||
int flag_store = A.flag;
|
int flag_store = A.flag;
|
||||||
A.ClaimMem(NTT_blocks * 4);
|
A.ClaimMem(NTT_blocks * 4);
|
||||||
memset(A.val, 0, A.buf_length * sizeof(int));
|
memset(A.val, 0, A.buf_length * sizeof(int));
|
||||||
|
if (!inverse) {
|
||||||
for (int i = 0; i < NTT_blocks / 2; i++) {
|
for (int i = 0; i < NTT_blocks / 2; i++) {
|
||||||
A.val[i] = pDC[(i << 1) | 1] * int2048::kNTTBlcokBase + pDC[i << 1];
|
A.val[i] = pDC[(i << 1) | 1] * int2048::kNTTBlockBase + pDC[i << 1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
A.val[0] = pDC[0];
|
||||||
|
for (int i = 1; i < NTT_blocks / 2; i++) {
|
||||||
|
A.val[i] = pDC[(i << 1) - 1] * int2048::kNTTBlockBase + pDC[i << 1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
A.num_length = NTT_blocks * 4;
|
A.num_length = NTT_blocks * 4;
|
||||||
|
if (!inverse) {
|
||||||
const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
||||||
100000, 1000000, 10000000, 100000000};
|
100000, 1000000, 10000000, 100000000};
|
||||||
while (A.val[(A.num_length - 1) / int2048::kNum] /
|
while (A.val[(A.num_length - 1) / int2048::kNum] /
|
||||||
@ -438,6 +587,12 @@ inline void UnsignedMultiply(int2048 &A, const int2048 *pB) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
while (A.num_length > int2048::kNum &&
|
||||||
|
A.val[A.num_length / int2048::kNum - 1] == 0)
|
||||||
|
A.num_length -= int2048::kNum;
|
||||||
|
A.ClaimMem(A.num_length);
|
||||||
|
}
|
||||||
delete[] pDA;
|
delete[] pDA;
|
||||||
delete[] pDB;
|
delete[] pDB;
|
||||||
delete[] pDC;
|
delete[] pDC;
|
||||||
@ -474,35 +629,31 @@ int2048 operator*(int2048 A, const int2048 &B) {
|
|||||||
A.Multiply(B);
|
A.Multiply(B);
|
||||||
return std::move(A);
|
return std::move(A);
|
||||||
}
|
}
|
||||||
|
void int2048::ProcessHalfBlock() {
|
||||||
void int2048::RightMoveBy(int L) {
|
assert(this->num_length % int2048::kNum == 0);
|
||||||
if (L >= this->num_length) {
|
this->ClaimMem(this->num_length + int2048::kNum);
|
||||||
this->num_length = 1;
|
this->num_length = this->num_length + int2048::kNum;
|
||||||
this->val[0] = 0;
|
assert(this->num_length % int2048::kNum == 0);
|
||||||
return;
|
int blocks_num = this->num_length / int2048::kNum;
|
||||||
|
for (int i = blocks_num - 1; i >= 1; i--) {
|
||||||
|
val[i] /= int2048::kNTTBlockBase;
|
||||||
|
val[i] += (val[i - 1] % int2048::kNTTBlockBase) * int2048::kNTTBlockBase;
|
||||||
}
|
}
|
||||||
int big_move = L / int2048::kNum;
|
val[0] /= int2048::kNTTBlockBase;
|
||||||
int small_move = L % int2048::kNum;
|
}
|
||||||
for (int i = 0; i < this->buf_length - big_move; i++) {
|
void int2048::RestoreHalfBlock() {
|
||||||
this->val[i] = this->val[i + big_move];
|
assert(this->num_length % int2048::kNum == 0);
|
||||||
}
|
int blocks_num = this->num_length / int2048::kNum;
|
||||||
for (int i = this->buf_length - big_move; i < this->buf_length; i++) {
|
for (int i = 0; i < blocks_num - 1; i++) {
|
||||||
this->val[i] = 0;
|
val[i] = ((long long)val[i] * int2048::kNTTBlockBase) % int2048::kStoreBase;
|
||||||
}
|
val[i] += val[i + 1] / int2048::kNTTBlockBase;
|
||||||
this->num_length -= big_move * int2048::kNum;
|
}
|
||||||
if (small_move == 0) return;
|
val[blocks_num - 1] =
|
||||||
const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
((long long)val[blocks_num - 1] * int2048::kNTTBlockBase) %
|
||||||
100000, 1000000, 10000000, 100000000};
|
int2048::kStoreBase;
|
||||||
for (int i = 0; i < this->buf_length; i++) {
|
while (this->num_length > 0 && val[this->num_length / int2048::kNum - 1] == 0)
|
||||||
this->val[i] /= kPow10[small_move];
|
this->num_length -= int2048::kNum;
|
||||||
if (i + 1 < this->buf_length) {
|
|
||||||
this->val[i] += this->val[i + 1] % kPow10[small_move] *
|
|
||||||
kPow10[int2048::kNum - small_move];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->num_length -= small_move;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void UnsignedDivide(int2048 &A, const int2048 *pB) {
|
inline void UnsignedDivide(int2048 &A, const int2048 *pB) {
|
||||||
int L1 = A.num_length, L2 = pB->num_length;
|
int L1 = A.num_length, L2 = pB->num_length;
|
||||||
if (&A == pB) throw "UnsignedDivide: A and B are the same object";
|
if (&A == pB) throw "UnsignedDivide: A and B are the same object";
|
||||||
@ -514,89 +665,136 @@ inline void UnsignedDivide(int2048 &A, const int2048 *pB) {
|
|||||||
A = std::move(int2048(0));
|
A = std::move(int2048(0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int2048 x;
|
/**
|
||||||
/*init x as 10^(L1-L2)*/
|
* Now pre-process has done. We can start the main algorithm:
|
||||||
x.ClaimMem(L1 - L2 + 1);
|
* 1. Convert B to scientific counting method and process the index.
|
||||||
x.num_length = L1 - L2 + 1;
|
* 2. In the state of reversing, calculate 1/B' using Newton-Raphson method.
|
||||||
memset(x.val, 0, x.buf_length * sizeof(int));
|
* 3. Reverse the iterative results again and calculate the answer.
|
||||||
|
*
|
||||||
|
* Warning: in reversed mode, num_length has no exact meaning, just operate a
|
||||||
|
* block as a whole
|
||||||
|
*/
|
||||||
|
int2048 origin_A(A);
|
||||||
|
int pow_A = (L1 + int2048::kNum - 1) / int2048::kNum - 1;
|
||||||
|
int pow_B = (L2 + int2048::kNum - 1) / int2048::kNum - 1;
|
||||||
|
// pow_B+1 is the number of blocks (with number) of B'
|
||||||
|
int2048 inverse_B(*pB);
|
||||||
|
inverse_B.num_length = (inverse_B.num_length + int2048::kNum - 1) /
|
||||||
|
int2048::kNum * int2048::kNum;
|
||||||
|
for (int i = 0; (i << 1) < (pow_B + 1); i++)
|
||||||
|
std::swap(inverse_B.val[i], inverse_B.val[pow_B - i]);
|
||||||
|
int2048 x(
|
||||||
|
int2048::kStoreBase *
|
||||||
|
(long long)std::max(1, int2048::kStoreBase / (inverse_B.val[0] + 1)));
|
||||||
|
assert(x.val[1] == std::max(1, int2048::kStoreBase / (inverse_B.val[0] + 1)));
|
||||||
|
x.num_length = 2 * int2048::kNum;
|
||||||
|
int *store[2];
|
||||||
|
store[0] = new int[pow_A + 5]();
|
||||||
|
store[1] = new int[pow_A + 5]();
|
||||||
|
int tot = 0;
|
||||||
|
for (int i = 0; i < pow_A + 1; i++) {
|
||||||
|
store[0][i] = A.val[i];
|
||||||
|
store[1][i] = -1;
|
||||||
|
}
|
||||||
|
int inverseB_error = 0;
|
||||||
|
if (inverse_B.val[0] >= int2048::kNTTBlockBase) {
|
||||||
|
inverseB_error = 1;
|
||||||
|
inverse_B.ProcessHalfBlock();
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
int2048 inverse_two(2), tmp_x(x);
|
||||||
|
inverse_two.num_length = int2048::kNum;
|
||||||
|
int tmp_x_error = 0;
|
||||||
|
if (tmp_x.val[0] >= int2048::kNTTBlockBase) {
|
||||||
|
tmp_x_error = 1;
|
||||||
|
tmp_x.ProcessHalfBlock();
|
||||||
|
}
|
||||||
|
assert(tmp_x.num_length % int2048::kNum == 0);
|
||||||
|
assert(inverse_B.num_length % int2048::kNum == 0);
|
||||||
|
UnsignedMultiply(tmp_x, &inverse_B, true,
|
||||||
|
tmp_x.num_length + 3 * int2048::kNum);
|
||||||
|
for (int i = 0; i < tmp_x_error + inverseB_error; i++)
|
||||||
|
tmp_x.RestoreHalfBlock();
|
||||||
|
UnsignedMinus(inverse_two, &tmp_x, true);
|
||||||
|
int inverse_two_error = 0, x_error = 0;
|
||||||
|
if (inverse_two.val[0] >= int2048::kNTTBlockBase) {
|
||||||
|
inverse_two_error = 1;
|
||||||
|
inverse_two.ProcessHalfBlock();
|
||||||
|
}
|
||||||
|
if (x.val[0] >= int2048::kNTTBlockBase) {
|
||||||
|
x_error = 1;
|
||||||
|
x.ProcessHalfBlock();
|
||||||
|
}
|
||||||
|
UnsignedMultiply(x, &inverse_two, true, inverse_two.num_length);
|
||||||
|
for (int i = 0; i < x_error + inverse_two_error; i++) x.RestoreHalfBlock();
|
||||||
|
/**
|
||||||
|
* now x is the next x, store[tot] stores last x, store[tot^1] stores the x
|
||||||
|
* previous to store[x]
|
||||||
|
*/
|
||||||
|
int blocks_of_x = (x.num_length + int2048::kNum - 1) / int2048::kNum;
|
||||||
|
if (blocks_of_x > pow_A + 3) {
|
||||||
|
x.ClaimMem((pow_A + 3) * int2048::kNum);
|
||||||
|
x.num_length = (pow_A + 3) * int2048::kNum;
|
||||||
|
blocks_of_x = pow_A + 3;
|
||||||
|
}
|
||||||
|
bool pre_same = true, pre_pre_same = true;
|
||||||
|
for (int i = 0; i < pow_A + 3; i++) {
|
||||||
|
if (store[tot][i] != (i < blocks_of_x ? x.val[i] : 0)) {
|
||||||
|
pre_same = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < pow_A + 3; i++) {
|
||||||
|
if (store[tot ^ 1][i] != (i < blocks_of_x ? x.val[i] : 0)) {
|
||||||
|
pre_pre_same = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pre_pre_same || pre_same) break;
|
||||||
|
tot ^= 1;
|
||||||
|
for (int i = 0; i < pow_A + 3; i++) {
|
||||||
|
if (i < blocks_of_x)
|
||||||
|
store[tot][i] = x.val[i];
|
||||||
|
else
|
||||||
|
store[tot][i] = 0;
|
||||||
|
}
|
||||||
|
// std::cerr << "length of x" << x.num_length << std::endl;
|
||||||
|
// fprintf(stderr, "x: ");
|
||||||
|
// for (int i = 0; i < blocks_of_x; i++) fprintf(stderr, "%08d ", x.val[i]);
|
||||||
|
// fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
delete[] store[0];
|
||||||
|
delete[] store[1];
|
||||||
|
/**
|
||||||
|
* Now reverse x back.
|
||||||
|
*/
|
||||||
|
int blocks_of_x = (x.num_length + int2048::kNum - 1) / int2048::kNum;
|
||||||
|
int pow_x = blocks_of_x - 1;
|
||||||
|
for (int i = 0; i < blocks_of_x / 2; i++)
|
||||||
|
std::swap(x.val[i], x.val[blocks_of_x - i - 1]);
|
||||||
|
x.num_length = blocks_of_x * int2048::kNum;
|
||||||
const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
const static int kPow10[9] = {1, 10, 100, 1000, 10000,
|
||||||
100000, 1000000, 10000000, 100000000};
|
100000, 1000000, 10000000, 100000000};
|
||||||
x.val[(x.num_length - 1) / int2048::kNum] =
|
/*Now get the accurate x.num_length for future computing*/
|
||||||
kPow10[(x.num_length - 1) % int2048::kNum];
|
while (x.num_length > 0 &&
|
||||||
/*reset x.num_length*/
|
x.val[(x.num_length - 1) / int2048::kNum] /
|
||||||
while (x.val[(x.num_length - 1) / int2048::kNum] /
|
|
||||||
kPow10[(x.num_length - 1) % int2048::kNum] ==
|
kPow10[(x.num_length - 1) % int2048::kNum] ==
|
||||||
0) {
|
0)
|
||||||
x.num_length--;
|
x.num_length--;
|
||||||
if (x.num_length == 0) throw "UnsignedMultiply: num_length==0";
|
UnsignedMultiply(A, &x);
|
||||||
|
A.RightMoveBy((pow_B + pow_x) * int2048::kNum);
|
||||||
|
/*Now we begin to process error*/
|
||||||
|
int2048 tmp(*pB), kOne(1);
|
||||||
|
UnsignedMultiply(tmp, &A);
|
||||||
|
while (UnsignedCmp(origin_A, tmp) < 0) {
|
||||||
|
UnsignedMinus(A, &kOne);
|
||||||
|
UnsignedMinus(tmp, pB);
|
||||||
}
|
}
|
||||||
/*check the highest number of B*/
|
UnsignedMinus(origin_A, &tmp);
|
||||||
if (pB->val[(pB->num_length - 1) / int2048::kNum] /
|
while (UnsignedCmp(origin_A, *pB) >= 0) {
|
||||||
kPow10[(pB->num_length - 1) % int2048::kNum] ==
|
UnsignedAdd(A, &kOne);
|
||||||
1) {
|
UnsignedMinus(origin_A, pB);
|
||||||
/* x=5*x */
|
|
||||||
int2048 tmp(x);
|
|
||||||
tmp.add(tmp);
|
|
||||||
tmp.add(tmp);
|
|
||||||
x.add(tmp);
|
|
||||||
} else if (pB->val[(pB->num_length - 1) / int2048::kNum] /
|
|
||||||
kPow10[(pB->num_length - 1) % int2048::kNum] <
|
|
||||||
3) {
|
|
||||||
/* x=3*x */
|
|
||||||
int2048 tmp(x);
|
|
||||||
tmp.add(tmp);
|
|
||||||
x.add(tmp);
|
|
||||||
} else if (pB->val[(pB->num_length - 1) / int2048::kNum] /
|
|
||||||
kPow10[(pB->num_length - 1) % int2048::kNum] <
|
|
||||||
5) {
|
|
||||||
/* x=2*x */
|
|
||||||
x.add(x);
|
|
||||||
}
|
}
|
||||||
int2048 x_pre(x);
|
|
||||||
int2048 kOne(1);
|
|
||||||
UnsignedMinus(x_pre, &kOne);
|
|
||||||
// int cnt = 0;
|
|
||||||
while (true) {
|
|
||||||
/**
|
|
||||||
* x_{n+1}=2*x_n-x_n*x_n*B/(10^L1))
|
|
||||||
*/
|
|
||||||
int2048 tmp = *pB;
|
|
||||||
UnsignedMultiply(tmp, &x);
|
|
||||||
UnsignedMultiply(tmp, &x);
|
|
||||||
// std::cerr << "max length ratio during computing"
|
|
||||||
// << (double)tmp.num_length / (double)L1 << std::endl;
|
|
||||||
tmp.RightMoveBy(L1);
|
|
||||||
int2048 x_next = x;
|
|
||||||
UnsignedAdd(x_next, &x);
|
|
||||||
UnsignedMinus(x_next, &tmp);
|
|
||||||
if (UnsignedCmp(x_next, x) == 0) break;
|
|
||||||
if (UnsignedCmp(x_next, x_pre) == 0) break;
|
|
||||||
x_pre = std::move(x);
|
|
||||||
x = std::move(x_next);
|
|
||||||
// std::cerr << "length ratio of x after each step"
|
|
||||||
// << (double)x.num_length / (double)L1 << std::endl;
|
|
||||||
// cnt++;
|
|
||||||
}
|
|
||||||
/*ret=A*x/10^(L1)*/
|
|
||||||
UnsignedMultiply(x, &A);
|
|
||||||
x.RightMoveBy(L1);
|
|
||||||
/*remain=A -B*ret*/
|
|
||||||
int2048 tmp = *pB;
|
|
||||||
UnsignedMultiply(tmp, &x);
|
|
||||||
if (UnsignedCmp(A, tmp) < 0) {
|
|
||||||
x -= 1;
|
|
||||||
tmp = *pB;
|
|
||||||
UnsignedMultiply(tmp, &x);
|
|
||||||
}
|
|
||||||
UnsignedMinus(A, &tmp);
|
|
||||||
int2048 remain = std::move(A);
|
|
||||||
while (UnsignedCmp(remain, *pB) >= 0) {
|
|
||||||
UnsignedMinus(remain, pB);
|
|
||||||
UnsignedAdd(x, &kOne);
|
|
||||||
// cnt++;
|
|
||||||
}
|
|
||||||
// std::cerr << cnt << std::endl;
|
|
||||||
A = std::move(x);
|
|
||||||
}
|
}
|
||||||
int2048 &int2048::Divide(const int2048 &B) {
|
int2048 &int2048::Divide(const int2048 &B) {
|
||||||
if (this == &B) {
|
if (this == &B) {
|
||||||
|
@ -74,7 +74,7 @@ for opt in opt_cpp:
|
|||||||
print(opt,file=sourc_cpp)
|
print(opt,file=sourc_cpp)
|
||||||
print(code_cpp_suf,file=sourc_cpp)
|
print(code_cpp_suf,file=sourc_cpp)
|
||||||
sourc_cpp.close()
|
sourc_cpp.close()
|
||||||
system("g++ /tmp/1.cpp -I /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/include/ -L /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/build/src/ -lint2048 -o /tmp/1")
|
system("g++ /tmp/1.cpp -I /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/include/ -L /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/build/src/ -lint2048 -fsanitize=address -g -o /tmp/1")
|
||||||
system("/tmp/1 > /tmp/1_cpp.out")
|
system("/tmp/1 > /tmp/1_cpp.out")
|
||||||
|
|
||||||
sourc_python=open("/tmp/1.py","w")
|
sourc_python=open("/tmp/1.py","w")
|
||||||
|
@ -36,7 +36,7 @@ opt_python=[]
|
|||||||
|
|
||||||
if True:
|
if True:
|
||||||
for i in range(0,10):
|
for i in range(0,10):
|
||||||
val=randint(-10**2,10**2)
|
val=randint(-10**200,10**200)
|
||||||
opt_cpp.append("a_"+str(i)+"=int2048(\""+str(val)+"\");")
|
opt_cpp.append("a_"+str(i)+"=int2048(\""+str(val)+"\");")
|
||||||
opt_python.append("a_"+str(i)+"="+str(val))
|
opt_python.append("a_"+str(i)+"="+str(val))
|
||||||
opt_cpp.append("a_"+str(i)+".print(); puts(\"\");")
|
opt_cpp.append("a_"+str(i)+".print(); puts(\"\");")
|
||||||
@ -80,7 +80,7 @@ for opt in opt_cpp:
|
|||||||
print(opt,file=sourc_cpp)
|
print(opt,file=sourc_cpp)
|
||||||
print(code_cpp_suf,file=sourc_cpp)
|
print(code_cpp_suf,file=sourc_cpp)
|
||||||
sourc_cpp.close()
|
sourc_cpp.close()
|
||||||
system("g++ /tmp/2.cpp -I /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/include/ -L /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/build/src/ -lint2048 -g -o /tmp/2")
|
system("g++ /tmp/2.cpp -I /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/include/ -L /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/build/src/ -lint2048 -fsanitize=address -g -o /tmp/2")
|
||||||
system("/tmp/2 > /tmp/2_cpp.out")
|
system("/tmp/2 > /tmp/2_cpp.out")
|
||||||
|
|
||||||
sourc_python=open("/tmp/2.py","w")
|
sourc_python=open("/tmp/2.py","w")
|
||||||
|
@ -36,7 +36,7 @@ opt_python=[]
|
|||||||
|
|
||||||
if True:
|
if True:
|
||||||
for i in range(0,10):
|
for i in range(0,10):
|
||||||
val=randint(-10**100,10**100)
|
val=randint(-10**2,10**2)
|
||||||
opt_cpp.append("a_"+str(i)+"=int2048(\""+str(val)+"\");")
|
opt_cpp.append("a_"+str(i)+"=int2048(\""+str(val)+"\");")
|
||||||
opt_python.append("a_"+str(i)+"="+str(val))
|
opt_python.append("a_"+str(i)+"="+str(val))
|
||||||
opt_cpp.append("a_"+str(i)+".print(); puts(\"\");")
|
opt_cpp.append("a_"+str(i)+".print(); puts(\"\");")
|
||||||
@ -80,7 +80,7 @@ for opt in opt_cpp:
|
|||||||
print(opt,file=sourc_cpp)
|
print(opt,file=sourc_cpp)
|
||||||
print(code_cpp_suf,file=sourc_cpp)
|
print(code_cpp_suf,file=sourc_cpp)
|
||||||
sourc_cpp.close()
|
sourc_cpp.close()
|
||||||
system("g++ /tmp/3.cpp -I /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/include/ -L /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/build/src/ -lint2048 -g -o /tmp/3")
|
system("g++ /tmp/3.cpp -I /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/include/ -L /home/happyzym/CSWorkSpace/Proc/BigHomework/BH-int2048-2023/build/src/ -lint2048 -fsanitize=address -g -o /tmp/3")
|
||||||
system("/tmp/3 > /tmp/3_cpp.out")
|
system("/tmp/3 > /tmp/3_cpp.out")
|
||||||
|
|
||||||
sourc_python=open("/tmp/3.py","w")
|
sourc_python=open("/tmp/3.py","w")
|
||||||
|
Reference in New Issue
Block a user