This repository has been archived on 2023-05-13. You can view files and clone it, but cannot push or open issues or pull requests.
mipt_cpp/seminar02_encapsulation/homework/code/1number/number.cpp
2022-09-14 19:05:27 +03:00

307 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <iostream>
#include <cstring>
#include <iomanip>
#include <vector>
#include <algorithm>
/*
Класс Number -- класс положительных больших чисел
Большое число будет храниться в динамическом массиве data
Каждый элемент этого массива содержит разряд числа в 100-ричной системе счисления
(так как base = 100)
По сути, каждый элемент data хранит две цифры числа в десятичной записи
Значение 100 для системы счисления выбрано как компромис между
эффективностью и удобством написания программы.
Если выбрать значения базы 10 - то программа будет не так эффективна по памяти
Если выбрать значения базы 256 (максимально эффективное использование памяти для типа char),
то алгоритм печати на экран сильно усложнится
В качестве альтернативы, можно было выбрать базу 1e9,
изменив при этом тип элементов c char на int
capacity - размер массива data
size - сколько ячеек занимет число в массиве data
size <= capacity
Для удобства разряды числа хранятся в обратном порядке
Например, число 12345678 соответствует массиву
data = {78, 56, 34, 12}
(это упрощает многие алгоритмы с такими числами)
*/
class Number
{
private:
static const int base = 100;
std::size_t size;
std::size_t capacity;
char* data;
public:
Number(int a)
{
#ifdef _DEBUG_CONSTRUCTOR
std::cout << "(Number constructor " << a << " -> ";
#endif
// Находим размер необходимой памяти под это число
int temp = a;
capacity = 0;
while (temp != 0)
{
temp /= base;
capacity += 1;
}
// Отдельно обрабатываем случай, когда число равно 0
if (capacity == 0)
capacity = 1;
// Выделяем память и записывем число a в массив data
// Например, число 12345678 представится в виде массива [78, 56, 34, 12]
data = new char[capacity];
for (int i = 0; i < capacity; ++i)
{
data[i] = a % base;
a /= base;
}
// В данном случае размер будет равен вместимости
size = capacity;
#ifdef _DEBUG_CONSTRUCTOR
std::cout << *this << ")" << std::endl;
#endif
}
// Конструктор по умолчанию
Number() : Number(0) {}
// Конструктор копирования
Number(const Number& n) {
size = n.size;
capacity = n.capacity;
data = new char[capacity];
for (int i = 0; i < size; i++) {
data[i] = n.data[i];
}
}
Number(const char* str) {
int len = std::strlen(str);
size = (len + len % 2) / 2;
capacity = size;
data = new char[capacity];
char buf[2];
for (int i = 0; i < size; i++) {
buf[1] = str[len - 2 * i - 1];
if (len - 2 * i - 1 > 0) {
buf[0] = str[len - 2 * i - 2];
}
else {
buf[0] = '0';
}
data[i] = std::stoi(buf);
}
}
~Number()
{
delete [] data;
}
Number& operator=(const Number& right) {
capacity = right.capacity;
size = right.size;
data = new char[capacity];
for (int i = 0; i < size; i++) {
data[i] = right.data[i];
}
return *this;
}
Number operator+(const Number& a) {
Number result;
int i;
char carry = 0;
int max_size = size > a.size ? size : a.size;
result.capacity = max_size + 1;
result.data = new char[capacity];
for (i = 0; i < max_size; ++i) {
result.data[i] = (data[i] + a.data[i] + carry) % base;
carry = (data[i] + a.data[i] + carry) / base;
}
if (carry) {
result.data[i] = carry;
result.size = max_size + 1;
}
else {
result.size = max_size;
}
return result;
}
void operator+=(const Number& a) {
*this = *this + a;
}
bool isEven() const {
if (data[0] % 2) {
return false;
}
return true;
}
Number operator*(const Number& right) const {
#ifdef _DEBUG_MUL
std::cout << "arg1=" << *this << "(capacity=" << capacity << ",size=" << size << ")" << " " << "arg2=" << right << "(capacity=" << right.capacity << ",size=" << right.size << ")"<< std::endl;
#endif
int i, j;
int temp;
Number result;
result.capacity = capacity + right.capacity;
int *carry = (int*)std::calloc(result.capacity, sizeof(int));
result.data = (char*)calloc(result.capacity, sizeof(char));
#ifdef _DEBUG_MUL
std::cout << "carry:[" << carry[0];
for (int k = 1; k < result.capacity; ++k) {
std::cout << "," << carry[k];
}
std::cout << "]" << std::endl;
#endif
for (i = 0; i < size; ++i) {
for (j = 0; j < right.size; ++j) {
#ifdef _DEBUG_MUL
std::cout << i + j << ":" << static_cast<int>(result.data[i + j]) << " + " << static_cast<int>(data[i]) << " * " << static_cast<int>(right.data[j]) << " + " << carry[i+j] << std::endl;
#endif
temp = (result.data[i + j] + data[i] * right.data[j] + carry[i + j]);
result.data[i + j] = temp % base;
carry[i + j + 1] += temp / base;
carry[i + j] = 0;
}
}
#ifdef _DEBUG_MUL
std::cout << "result before applying carry:" << result << std::endl;
std::cout << "carry:[" << carry[0];
for (int k = 1; k < result.capacity; ++k) {
std::cout << "," << carry[k];
}
std::cout << "]" << std::endl;
#endif
if (carry[i + j - 1]) {
result.data[i + j - 1] = carry[i + j - 1];
result.size = i + j;
carry[i + j - 1] = 0;
}
else {
result.size = i + j - 1;
}
#ifdef _DEBUG_MUL
std::cout << "before correcting capacity, result=" << result << std::endl;
#endif
// correcting capacity
/*char* temp_data = (char *)calloc(result.size, sizeof(char));
for (i = 0; i < result.size; ++i) {
temp_data[i] = result.data[i];
}
free(result.data);
result.capacity = result.size;
result.data = (char*)calloc(result.size,sizeof(char));
for (i = 0; i < result.size; ++i) {
result.data[i] = temp_data[i];
}
free(temp_data);*/
free(carry);
#ifdef _DEBUG_MUL
std::cout << "return value=" << result << "(capacity=" << result.capacity << ",size=" << result.size << ")" << std::endl << "======" << std::endl;
#endif
return result;
}
void operator*=(const Number& a) {
*this = *this * a;
}
friend std::ostream& operator<<(std::ostream& stream, const Number& right);
friend int main();
friend Number factorial(int n);
};
std::ostream& operator<<(std::ostream& stream, const Number& right)
{
#ifdef _DEBUG_COUT
stream << "[";
for (std::size_t i = 0; i < right.size; ++i) {
stream << static_cast<int>(right.data[right.size - 2 - i]) << ",";
}
stream << "]";
#else
// Печатаем самый большой разряд
stream << (int)right.data[right.size - 1];
// Печатаем остальные разряды с заполнением нулями до 2-х цифр
// setfill и setw это то же самое, что и в языке C спецификатор %02d
for (std::size_t i = 0; i < right.size - 1; ++i)
stream << std::setfill('0') << std::setw(2) << (int)right.data[right.size - 2 - i];
#endif
return stream;
}
Number fib(int n) {
Number a = 0; // F0
Number b = 1; // F1
for (int i = 1; i <=n; ++i) {
if (i % 2) {
a += b;
}
else {
b += a;
}
}
if (n % 2) {
return b;
}
return a;
}
Number factorial(int n) {
Number result{1};
for (int i = 2; i < n + 1; ++i) {
result = Number(i) * result;
}
return result;
}
int main()
{
Number x = Number("25852016738884976640000");
Number y = Number("24");
//char s[3];
//Number result = "1";
//for (int i = 1; i < 26; ++i) {
//s = std::to_string(i);
//sprintf(s, "%d", i);
// result = (Number{i} * result);
//}
//x += y;
//Number z = x + y;
//std::cout << fib(1000) << std::endl;
//x = x * Number(24)*Number{25};
//y = factorial(5);
//std::cout << x << " " << x.capacity << " " << x.size << std::endl;
//std::cout << y << " "<< y.capacity << " " << y.size << std::endl;
// 90405070506200618121707-18-13-05-18-08
//std::cout << "===" << std::endl << Number(2) * Number(3) << " "<< Number(3) * Number(2) << std::endl;
//std::cout << "5! = " << Number(2) * Number(3) * Number(4) * Number(5) << std::endl;
std::cout << factorial(1000) << std::endl;
//std::cout << Number("620448401733239439360000") * Number(25) << std::endl;
}