upd: write comments
This commit is contained in:
@ -14,6 +14,7 @@ class int2048 {
|
||||
* If the interger is negative, flag = -1, otherwise flag = 1.
|
||||
* 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.
|
||||
* buf_length is the accessiable length of val.
|
||||
*/
|
||||
const static int kStoreBase = 100000000, kNum = 8, kDefaultLength = 10;
|
||||
const static int kMemAdditionScalar = 2, kMemDeleteScalar = 4;
|
||||
@ -59,15 +60,27 @@ root= 6
|
||||
int2048(int2048 &&) noexcept;
|
||||
~int2048();
|
||||
|
||||
/**
|
||||
* @brief Move the integer left (*10^k) or right(//10^k).
|
||||
*/
|
||||
void LeftMoveBy(int);
|
||||
void RightMoveBy(int);
|
||||
|
||||
void read(const std::string &);
|
||||
void print();
|
||||
void print_debug();
|
||||
// function print_debug is used to print the data in val to stderr(with '\n'),
|
||||
// for debug
|
||||
|
||||
void ClaimMem(size_t);
|
||||
// something like vector's reserve
|
||||
|
||||
/**
|
||||
* Fuctions whose name starts with Unsigned are basic operation. It receives
|
||||
* int2048& A and const int2048* pB(UnsignedCmp receives const int2048& B),
|
||||
* and do the operation A op= *pB. Warning: the address of A and pB must be
|
||||
* different.
|
||||
*/
|
||||
inline friend int UnsignedCmp(const int2048 &, const int2048 &);
|
||||
inline friend void UnsignedAdd(int2048 &, const int2048 *);
|
||||
inline friend void UnsignedMinus(int2048 &, const int2048 *);
|
||||
@ -100,6 +113,11 @@ root= 6
|
||||
int2048 &Divide(const int2048 &);
|
||||
friend int2048 Divide(int2048, const int2048 &);
|
||||
|
||||
/**
|
||||
* If the denominator is zero, the result is undefined.
|
||||
* But actually it will return 0 with no exception thrown.
|
||||
* And % operation is also based on this rule.
|
||||
*/
|
||||
int2048 &operator/=(const int2048 &);
|
||||
friend int2048 operator/(int2048, const int2048 &);
|
||||
|
||||
|
@ -100,6 +100,13 @@ int2048::int2048(const int2048 &input_value) {
|
||||
num_length = input_value.num_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
*
|
||||
* @param input_value
|
||||
*
|
||||
* @details Note that after moving, the input_value will be set to 0.
|
||||
*/
|
||||
int2048::int2048(int2048 &&input_value) noexcept {
|
||||
buf_length = input_value.buf_length;
|
||||
val = input_value.val;
|
||||
@ -164,6 +171,15 @@ void int2048::print_debug() {
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Claim memory for the number
|
||||
*
|
||||
* @param number_length The length of the number
|
||||
*
|
||||
* @details If the number_length is smaller than the current buf_length/4 or
|
||||
* bigger than current buf_length, then val will be reset to accomodate
|
||||
* number_length*2 numbers. Something like std::vector.
|
||||
*/
|
||||
void int2048::ClaimMem(size_t number_length) {
|
||||
size_t new_number_blocks = (number_length + kNum - 1) / kNum;
|
||||
if (new_number_blocks > buf_length) {
|
||||
@ -181,6 +197,17 @@ void int2048::ClaimMem(size_t number_length) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two unsigned numbers
|
||||
*
|
||||
* @param A The first number
|
||||
* @param B The second number
|
||||
*
|
||||
* @return -1 if A < B, 0 if A == B, 1 if A > B
|
||||
*
|
||||
* @details This function is used to compare two numbers wittout considering the
|
||||
* sign.
|
||||
*/
|
||||
inline int UnsignedCmp(const int2048 &A, const int2048 &B) {
|
||||
if (A.num_length != B.num_length) return A.num_length < B.num_length ? -1 : 1;
|
||||
int number_of_blocks = (A.num_length + int2048::kNum - 1) / int2048::kNum;
|
||||
@ -440,6 +467,16 @@ void int2048::RightMoveBy(int L) {
|
||||
this->num_length -= small_move;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief the definition of NTTTransform
|
||||
*
|
||||
* @param a the array to be transformed
|
||||
* @param NTT_blocks the length of the array
|
||||
* @param inverse whether to do inverse transform
|
||||
*
|
||||
* @details the array a will be transformed to a new array a', which is the NTT.
|
||||
* Note that the base in NTT is 10000.
|
||||
*/
|
||||
void int2048::NTTTransform(__int128_t *a, int NTT_blocks,
|
||||
bool inverse = false) {
|
||||
for (int i = 1, j = 0; i < NTT_blocks; i++) {
|
||||
@ -469,6 +506,16 @@ void int2048::NTTTransform(__int128_t *a, int NTT_blocks,
|
||||
for (int i = 0; i < NTT_blocks; i++) (a[i] *= inv) %= int2048::kNTTMod;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsigned multiply two numbers
|
||||
*
|
||||
* @param A The first number
|
||||
* @param pB The second number
|
||||
*
|
||||
* @details This function will multiply two numbers without considering the
|
||||
* sign.
|
||||
*/
|
||||
inline void UnsignedMultiply(int2048 &A, const int2048 *pB) {
|
||||
if (&A == pB) throw "UnsignedMultiply: A and B are the same object";
|
||||
int blocks_of_A = ((A.num_length + int2048::kNum - 1) / int2048::kNum);
|
||||
@ -552,6 +599,15 @@ int2048 operator*(int2048 A, const int2048 &B) {
|
||||
A.Multiply(B);
|
||||
return std::move(A);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsigned multiply a number by an integer
|
||||
*
|
||||
* @param v The integer
|
||||
*
|
||||
* @details This function will multiply a number by an integer without
|
||||
* considering the sign.
|
||||
*/
|
||||
void int2048::UnsignedMultiplyByInt(int v) {
|
||||
if (v < 0) throw "UnsignedMultiplyByInt: v < 0";
|
||||
if (v == 0) {
|
||||
@ -586,6 +642,9 @@ int2048 GetInv(const int2048 &B, int n) {
|
||||
100000, 1000000, 10000000, 100000000};
|
||||
int total_blocks = (B.num_length + int2048::kNum - 1) / int2048::kNum;
|
||||
if (n <= 16) {
|
||||
/**
|
||||
* This code block is used to calculate the inverse of B when n is small.
|
||||
*/
|
||||
long long b = 0;
|
||||
for (int j = B.num_length - 1, i = 0; i < n; i++, j--)
|
||||
b = b * 10 + (B.val[j / int2048::kNum] / kPow10[j % int2048::kNum]) % 10;
|
||||
@ -614,6 +673,7 @@ int2048 GetInv(const int2048 &B, int n) {
|
||||
int2048 sub_soluton_copy_2(sub_soluton);
|
||||
sub_soluton_copy_1.UnsignedMultiplyByInt(2);
|
||||
sub_soluton_copy_1.LeftMoveBy((n - k));
|
||||
// Now sub_soluton_copy_1 is 2*sub_soluton*10^(n-k)
|
||||
/*now we get the current B*/
|
||||
int2048 current_B;
|
||||
current_B.ClaimMem(n);
|
||||
@ -632,8 +692,11 @@ int2048 GetInv(const int2048 &B, int n) {
|
||||
UnsignedMultiply(sub_soluton_copy_2, ¤t_B);
|
||||
UnsignedMultiply(sub_soluton_copy_2, &sub_soluton);
|
||||
sub_soluton_copy_2.RightMoveBy(2 * k);
|
||||
// Now sub_soluton_copy_2 is sub_soluton^2*current_B/(10^2k)
|
||||
UnsignedMinus(sub_soluton_copy_1, &sub_soluton_copy_2);
|
||||
int2048 res = sub_soluton_copy_1;
|
||||
// Now we get an estimation for [10^2n/current_B]
|
||||
/*Now begin doing some ajustments*/
|
||||
int2048 remain;
|
||||
remain.ClaimMem((2 * n) + 1);
|
||||
remain.val[2 * n / int2048::kNum] = kPow10[2 * n % int2048::kNum];
|
||||
@ -667,14 +730,21 @@ int2048 GetInv(const int2048 &B, int n) {
|
||||
return std::move(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsigned divide two numbers
|
||||
*
|
||||
* @param A The first number
|
||||
* @param pB The second number
|
||||
*
|
||||
* @details This function will divide two numbers without considering the sign.
|
||||
* reference:
|
||||
* <https://github.com/lzyrapx/Competitive-Programming-Docs/blob/master/WC%E8%AE%B2%E8%AF%BE%E8%B5%84%E6%96%99/%E7%90%86%E6%80%A7%E6%84%89%E6%82%A6%E2%80%94%E2%80%94%E9%AB%98%E7%B2%BE%E5%BA%A6%E6%95%B0%E5%80%BC%E8%AE%A1%E7%AE%97%EF%BC%882012WC%EF%BC%89.pdf>
|
||||
*/
|
||||
inline void UnsignedDivide(int2048 &A, const int2048 *pB) {
|
||||
int2048 B(*pB);
|
||||
int L1 = A.num_length;
|
||||
int L2 = B.num_length;
|
||||
/**
|
||||
* reference:
|
||||
* https://github.com/lzyrapx/Competitive-Programming-Docs/blob/master/WC%E8%AE%B2%E8%AF%BE%E8%B5%84%E6%96%99/%E7%90%86%E6%80%A7%E6%84%89%E6%82%A6%E2%80%94%E2%80%94%E9%AB%98%E7%B2%BE%E5%BA%A6%E6%95%B0%E5%80%BC%E8%AE%A1%E7%AE%97%EF%BC%882012WC%EF%BC%89.pdf
|
||||
*/
|
||||
if (L2 <= 16) {
|
||||
long long b = B.val[0] + (long long)B.val[1] * int2048::kStoreBase;
|
||||
__uint128_t c = 0;
|
||||
@ -709,12 +779,16 @@ inline void UnsignedDivide(int2048 &A, const int2048 *pB) {
|
||||
assert(L1 == A.num_length);
|
||||
assert(L2 == B.num_length);
|
||||
assert(2 * L2 >= L1);
|
||||
/*Now we calculate [10^2n/B]*/
|
||||
int2048 res_hat = std::move(GetInv(B, L2));
|
||||
UnsignedMultiply(res_hat, &A);
|
||||
res_hat.RightMoveBy(2 * L2);
|
||||
// now res_hat is [[10^2n/B]*A/(10^2L2)]
|
||||
/*Now begin doing ajustments*/
|
||||
int2048 remain(A), tmp_B(B);
|
||||
UnsignedMultiply(tmp_B, &res_hat);
|
||||
UnsignedMinus(remain, &tmp_B);
|
||||
// now remain is A-res_hat*B
|
||||
for (int i = 8; i > 0; i >>= 1) {
|
||||
tmp_B = B;
|
||||
tmp_B.UnsignedMultiplyByInt(i);
|
||||
|
Reference in New Issue
Block a user