#include #include #include #include #include #include #include #include #define todo(msg) \ std::cerr << msg << std::endl; \ assert(false); // TODO: try to maximize rvalue usage to reuse resources // "Use rvalue references to reuse the resources of temporaries when possible, // particularly for binary operands." class Integer { using byte = unsigned char; // bytes are litle endian byte *bytes; bool isNegative; std::size_t size; public: Integer(long long num = 0) : bytes{new byte[sizeof(long long)]}, isNegative{num < 0}, size{sizeof(long long)} { unsigned long long n = std::llabs(num); for (std::size_t i = 0; i < size; i++) { bytes[i] = n % 256; n /= 256; } } Integer(Integer const &rhs) : bytes{new byte[rhs.size]}, isNegative{rhs.isNegative}, size{rhs.size} { for (std::size_t i = 0; i < size; i++) { bytes[i] = rhs.bytes[i]; } } Integer(Integer &&rhs) : bytes{rhs.bytes}, isNegative{rhs.isNegative}, size{rhs.size} { rhs.bytes = nullptr; } ~Integer() { delete[] bytes; bytes = nullptr; } Integer &operator=(Integer const &rhs) { delete[] bytes; bytes = new byte[rhs.size]; size = rhs.size; isNegative = rhs.isNegative; for (std::size_t i = 0; i < size; i++) { bytes[i] = rhs.bytes[i]; } return *this; } Integer &operator=(Integer &&rhs) { bytes = rhs.bytes; rhs.bytes = nullptr; size = rhs.size; isNegative = rhs.isNegative; return *this; } void expand() { byte *new_buf = new byte[size + 1]; std::copy(bytes, bytes + size, new_buf); size++; delete[] bytes; bytes = new_buf; } std::size_t true_size() const { std::size_t ret = size; if (bytes == nullptr) { return 0; } while (bytes[ret - 1] == 0 && ret > 1) { ret--; } return ret; } friend bool abs_lt(Integer const &lhs, Integer const &rhs) { std::size_t true_lhs_size = lhs.true_size(); std::size_t true_rhs_size = rhs.true_size(); if (true_lhs_size != true_rhs_size) { return true_lhs_size < true_rhs_size; } if (true_lhs_size == 0) { return false; } auto idx = true_lhs_size - 1; while (true) { if (lhs.bytes[idx] != rhs.bytes[idx]) { return lhs.bytes[idx] < rhs.bytes[idx]; } if (idx == 0) { break; } idx--; } return false; } friend Integer &abs_add(Integer &lhs, Integer const &rhs) { bool overflow = false; bool prev_overflow; for (std::size_t i = 0; i < std::min(lhs.size, rhs.size); i++) { prev_overflow = overflow; overflow = false; if (std::numeric_limits::max() - lhs.bytes[i] < rhs.bytes[i]) { overflow = true; } else if (std::numeric_limits::max() - lhs.bytes[i] - (rhs.bytes[i]) < (prev_overflow ? 1 : 0)) { overflow = true; } lhs.bytes[i] += rhs.bytes[i] + (prev_overflow ? 1 : 0); } if (overflow) { std::size_t idx = std::min(lhs.size, rhs.size); if (idx == lhs.size) { lhs.expand(); } lhs.bytes[idx]++; if (lhs.bytes[idx] == 0) { idx++; if (idx == lhs.size) { lhs.expand(); } lhs.bytes[idx]++; } } return lhs; } friend Integer &abs_sub(Integer &lhs, Integer const &rhs) { assert(abs_lt(rhs, lhs)); auto r_size = rhs.true_size(); auto t_size = r_size + (lhs.true_size() > r_size ? 1 : 0); bool overflow = false; for (std::size_t i = 0; i < t_size; i++) { byte to_sub = 0; byte overflow_handle = (overflow ? 1 : 0); overflow = false; if (r_size > i) { to_sub = rhs.bytes[i]; if (to_sub + overflow_handle > lhs.bytes[i] || // overflow detection (to_sub + overflow_handle == 0 && to_sub != 0)) { overflow = true; } } lhs.bytes[i] -= to_sub + overflow_handle; } return lhs; } friend Integer &operator+=(Integer &lhs, Integer const &rhs) { if (lhs.isNegative == rhs.isNegative) { return abs_add(lhs, rhs); } else { if (abs_lt(rhs, lhs)) { return abs_sub(lhs, rhs); } else if (abs_lt(lhs, rhs)) { Integer tmp = std::move(lhs); lhs = rhs; return abs_sub(lhs, tmp); } else { lhs = 0; return lhs; } } } friend Integer &operator-=(Integer &lhs, Integer const &rhs) { return lhs += -rhs; } friend Integer &operator*=(Integer &lhs, Integer const &rhs) { Integer tmp = std::move(lhs); Integer tmp_r = rhs; lhs = 0; while (tmp_r > Integer{0}) { lhs += tmp; tmp_r -= 1; } return lhs; } friend Integer &operator/=(Integer &lhs, Integer const &rhs) { bool sign = lhs.isNegative != rhs.isNegative; lhs.isNegative = rhs.isNegative; Integer tmp = std::move(lhs); lhs = 0; while (tmp > rhs || tmp == rhs) { tmp -= rhs; lhs += 1; } lhs.isNegative = sign; return lhs; } friend Integer &operator%=(Integer &lhs, Integer const &rhs) { while (lhs >= rhs) { lhs -= rhs; } return lhs; } friend Integer operator+(Integer const &lhs, Integer const &rhs) { Integer ret{lhs}; ret += rhs; return ret; } friend Integer operator-(Integer const &lhs, Integer const &rhs) { Integer ret{lhs}; ret -= rhs; return ret; } friend Integer operator*(Integer const &lhs, Integer const &rhs) { Integer ret{lhs}; ret *= rhs; return ret; } friend Integer operator/(Integer const &lhs, Integer const &rhs) { Integer ret{lhs}; ret /= rhs; return ret; } friend Integer operator%(Integer const &lhs, Integer const &rhs) { Integer ret{lhs}; ret %= rhs; return ret; } friend Integer operator-(Integer const &val) { Integer ret = val; ret.isNegative = !ret.isNegative; return ret; } friend bool operator<(Integer const &lhs, Integer const &rhs) { if (lhs.isNegative != rhs.isNegative && lhs != Integer{0} && rhs != Integer{0}) { return lhs.isNegative; } else { return abs_lt(lhs, rhs); } } friend bool operator>(Integer const &lhs, Integer const &rhs) { return rhs < lhs; } friend bool operator>=(Integer const &lhs, Integer const &rhs) { return lhs > rhs || lhs == rhs; } friend bool operator<=(Integer const &lhs, Integer const &rhs) { return rhs < lhs || rhs == lhs; } friend bool operator==(Integer const &lhs, Integer const &rhs) { auto l_true = lhs.true_size(); auto r_true = rhs.true_size(); if (l_true == 1 && r_true == 1) { if (lhs.bytes[0] == rhs.bytes[0] && lhs.bytes[0] == 0) { return true; } } return l_true == rhs.true_size() && lhs.isNegative == rhs.isNegative && std::equal(lhs.bytes, lhs.bytes + l_true, rhs.bytes); } friend bool operator!=(Integer const &lhs, Integer const &rhs) { return !(lhs == rhs); } friend std::ostream &operator<<(std::ostream &lhs, Integer const &rhs) { if (rhs == 0) { lhs << 0; return lhs; } Integer discard = rhs; std::stringstream to_output{}; while (discard != Integer{0}) { int a = discard % Integer{10}; to_output << a; discard /= 10; } std::string out; to_output >> out; std::reverse(out.begin(), out.end()); lhs << out; return lhs; } operator unsigned long() { unsigned long ret = 0; for (std::size_t i = 0; i < std::min(size, sizeof(unsigned long)); i++) { ret += (static_cast(this->bytes[i]) << (i * 8)); } return ret; } }; int main(void) { Integer a = std::numeric_limits::max(); Integer b = a; a += a; assert(a / b == Integer{2}); a = std::numeric_limits::min(); b = a; a += a; assert(a / b == Integer{2}); a = std::numeric_limits::max(); b = a; a = -a; a += a; assert(a / b == Integer{-2}); a = std::numeric_limits::min(); b = a; a = -a; a += a; assert(a / b == Integer{-2}); a = std::numeric_limits::max(); b = a; for (std::size_t i = 2; i < 20; i++) { a += b; assert(b * Integer{static_cast(i)} == a); } for (std::size_t i = 0; i <= 4500; i++) { Integer n = i; std::cout << n << std::endl; Integer base_n = n; n *= 1; assert(n == base_n); n += 0; assert(n == base_n); n /= 1; assert(n == base_n); n -= 0; assert(n == base_n); assert(n - n == Integer{0}); assert(-n + n == Integer{0}); assert(-n + (n - Integer{1}) == Integer{-1}); assert(n * Integer{0} == Integer{0}); assert(Integer{-static_cast(i)} + n == Integer{0}); if (n == Integer{0}) { continue; } assert(n - Integer{1} == Integer{static_cast(i - 1)}); assert(Integer{-1} + n == Integer{static_cast(i - 1)}); assert((n + n) / n == Integer{2}); assert((n * n) / n == n); assert(n / n == Integer{1}); } }