#include "bignum.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define MAXVAL 10000

// Фукнции максимума и минимума для удобства.
int min(int a, int b)
{
    return (a < b) ? a : b;
}

int max(int a, int b)
{
    return (a < b) ? b : a;
}


BigNum *BigNum_make(char *source)
{
    BigNum *bn = (BigNum *) malloc(sizeof(BigNum));
    if (source[0] == '-') {
	bn->sign = -1;
	source++;		// пропустить знак '-'
    } else {
	bn->sign = 1;
    }
    // Чтобы при целочисленном делении a / n сделать оругление 
    // в большую сторону, используем (a + (n-1)) / n.
    bn->size = (strlen(source) + 3) / 4;
    bn->data = (int *) malloc(sizeof(int) * bn->size);
    // Считываем из массива все разряды по основанию 10000,
    // начиная с младшего. Старший разряд обработаем позже.
    int i, k = 0;
    for (i = strlen(source) - 1; i >= 4; i -= 4) {
	bn->data[k] = 1000 * (source[i - 3] - '0') +
	    100 * (source[i - 2] - '0') +
	    10 * (source[i - 1] - '0') + (source[i] - '0');
	k++;
    }
    // Так как в старшем разряде может быть менее 4 десятичных
    // цифр, обработаем его отдельно.
    bn->data[k] = 0;
    int power = 1;
    for (; i >= 0; i--) {
	bn->data[k] = bn->data[k] + (source[i] - '0') * power;
        power *= 10;
    }
    return bn;
}

BigNum *BigNum_copy(BigNum * bn)
{
    // Надо выделить память под новую структуру BigNum и
    // под её внутренний массив, и скопировать в них
    // соответсвующее содержание из bn.
    
    // ВАШ КОД ЗДЕСЬ.
}

void BigNum_free(BigNum * bn)
{
    free(bn->data);
    free(bn);
}

void BigNum_print(BigNum * bn)
{
    if (bn->sign < 0) {
	printf("-\n");
    }
    int i;
    // Печатаем разряды, начиная со старшего.
    for (i = bn->size - 1; i >= 0; i--) {
	printf("%04d", bn->data[i]);
    }
    printf("\n");
}

void BigNum_set_size(BigNum * bn, size_t new_size)
{
    // Функция для установки нового размера внутреннего массива.
    // Не забудте, что если размер массива увеличился, нужно
    // занулить новые элементы.

    // ВАШ КОД ЗДЕСЬ.
}

void BigNum_add(BigNum * bn1, BigNum * bn2)	// +=
{
// Example1:
//      1 2312 3123
//      9 8798 7987
// Example2:
//                1
//        9999 9999
// Example3:
//        9999 9999 9999
//                     1

    // Функия неправильно работает при сложении положительного и отрицательного числа.
    // Доработайте её, чтобы она работала корректна и добавьте соответсвующие тесты в
    // run_tests() в main.c
    BigNum_set_size(bn1, max(bn1->size, bn2->size) + 1);
    int i, x = 0;
    for (i = 0; i < bn2->size; i++) {
	bn1->data[i] += bn2->data[i] + x;
	x = bn1->data[i] / MAXVAL; // Получаем 1 (при переполнении) или 0 для переноса в следующий разряд.
	bn1->data[i] %= MAXVAL; // Убираем всё лишнее.
    }
    for (; i < bn1->size && x != 0; i++) { // продолжаем распространять единицу, если она есть.
	bn1->data[i] += x;
	x = bn1->data[i] / MAXVAL;
	bn1->data[i] %= MAXVAL;
    }
}

int BigNum_eq(BigNum * bn1, BigNum * bn2)	// ==
{
    // Функция проверки на равенство.
    // Не забывайте, что числа разной длины тоже могут быть равны,
    // если в числе большего размера в "лишних" разрядах стоят нули.

    // ВАШ КОД ЗДЕСЬ.
}

void BigNum_check_add(char *bn1_str, char *bn2_str, char *result_str)
{
    // Создаём большие числа из строк.
    BigNum *bn1 = BigNum_make(bn1_str),
           *bn2 = BigNum_make(bn2_str),
           *result = BigNum_make(result_str);
    // Делаем копию bn1, чтобы иметь неизменённое число,
    // если нужно будет вывести сообщение об ошибке.
    BigNum *bn1_copy = (BigNum *) BigNum_copy(bn1);
    // Складываем числа, результат в bn1_copy.
    BigNum_add(bn1_copy, bn2);
    if (!BigNum_eq(bn1_copy, result)) { // Если результат перемножения не равен заданному,
                                // напечатаем ошибку и выйдем из функции.
	printf("Can not add\n  ");
	BigNum_print(bn1);
	printf("  to\n  ");
	BigNum_print(bn2);
	printf("  Expected\n  ");
	BigNum_print(result);
	printf("  got\n  ");
	BigNum_print(bn1_copy);
	exit(1);
    }
    // Освободим память, чтобы не было утечек.
    BigNum_free(bn1_copy);
    BigNum_free(bn1),
    BigNum_free(bn2),
    BigNum_free(result);
}

void BigNum_sub(BigNum * bn1, BigNum * bn2)
{
    // Операция -=

    // ВАШ КОД ЗДЕСЬ.
}

void BigNum_mul(BigNum * bn1, BigNum * bn2)
{
    // Операция *=

    // ВАШ КОД ЗДЕСЬ.
}

// Для тестирования функций BigNum_sub и BigNum_mul нужно либо дописать
// новые тестирующие функции, либо модифицировать BigNum_check_add так,
// чтобы она умела тестировать вычитание и умножение.