#include "CNumber.h"
#include <cstring>
#include <algorithm>
#include <cctype>


CNumber::CNumber()
    : i_length(NUMBER_DEFAULT_LENGTH), is_negative(false)
{
    pi_number = new int[i_length];
    std::memset(pi_number, 0, sizeof(int) * i_length);
}

CNumber::CNumber(int iValue)
    : i_length(NUMBER_DEFAULT_LENGTH)
{
    pi_number = new int[i_length];
    std::memset(pi_number, 0, sizeof(int) * i_length);
    initializeDigits(iValue);
}

CNumber::CNumber(const char* cstrValue)
    : i_length(NUMBER_DEFAULT_LENGTH), is_negative(false)
{
    pi_number = new int[i_length];
    std::memset(pi_number, 0, sizeof(int) * i_length);
    initializeDigits(cstrValue);
}

CNumber::CNumber(const CNumber &pcOther)
    : i_length(pcOther.i_length), is_negative(pcOther.is_negative)
{
    pi_number = new int[i_length];
    std::memcpy(pi_number, pcOther.pi_number, sizeof(int) * i_length);
}

CNumber::~CNumber()
{
    delete[] pi_number;
}

CNumber& CNumber::operator=(const CNumber &pcOther)
{
    if (this != &pcOther)
    {
        delete[] pi_number;
        i_length = pcOther.i_length;
        is_negative = pcOther.is_negative;
        pi_number = new int[i_length];
        std::memcpy(pi_number, pcOther.pi_number, sizeof(int) * i_length);
    }
    return *this;
}

CNumber& CNumber::operator=(const int iValue)
{
    delete[] pi_number;
    pi_number = new int[i_length];
    std::memset(pi_number, 0, sizeof(int) * i_length);
    initializeDigits(iValue);
    return *this;
}


CNumber& CNumber::operator=(const char* cstrValue)
{
    delete[] pi_number;
    pi_number = new int[i_length];
    std::memset(pi_number, 0, sizeof(int) * i_length);
    initializeDigits(cstrValue);
    return *this;
}

void CNumber::initializeDigits(int iValue)
{
    if (iValue < 0)
    {
        is_negative = true;
        iValue = -iValue;
    }
    else
    {
        is_negative = false;
    }

    if (iValue == 0)
    {
        pi_number[0] = 0;
        return;
    }

    int index = 0;
    while (iValue > 0 && index < i_length)
    {
        pi_number[index++] = iValue % 10;
        iValue /= 10;
    }
}

void CNumber::initializeDigits(const char* cstrValue)
{
    is_negative = false;
    int start = 0;

    if (cstrValue != nullptr && cstrValue[0] == '-')
    {
        is_negative = true;
        start = 1;
    }

    int index = 0;
    int length = std::strlen(cstrValue);
    for (int i = length - 1; index < i_length && i >= start; --i)
    {
        if (std::isdigit(static_cast<unsigned char>(cstrValue[i])))
        {
            pi_number[index++] = cstrValue[i] - '0';
        }
        else
        {
            std::memset(pi_number, 0, sizeof(int) * i_length);
            is_negative = false;
            return;
        }
    }

    if (index == 0)
    {
        pi_number[0] = 0;
    }
}

int CNumber::compareAbsolute(const CNumber &pcOther) const
{
    int this_ms = i_length - 1;
    while (this_ms > 0 && pi_number[this_ms] == 0)
        this_ms--;

    int other_ms = pcOther.i_length - 1;
    while (other_ms > 0 && pcOther.pi_number[other_ms] == 0)
        other_ms--;

    if (this_ms > other_ms)
        return 1;
    if (this_ms < other_ms)
        return -1;

    for (int i = this_ms; i >= 0; --i)
    {
        if (pi_number[i] > pcOther.pi_number[i])
            return 1;
        if (pi_number[i] < pcOther.pi_number[i])
            return -1;
    }

    return 0;
}

void CNumber::addAbsolute(const CNumber &pcOther, CNumber &result) const
{
    int carry = 0;
    for (int i = 0; i < i_length; ++i)
    {
        int sum = pi_number[i] + pcOther.pi_number[i] + carry;
        result.pi_number[i] = sum % 10;
        carry = sum / 10;
    }
}

// *this >= pcOther
void CNumber::subtractAbsolute(const CNumber &pcOther, CNumber &result) const
{
    int borrow = 0;
    for (int i = 0; i < i_length; ++i)
    {
        int diff = this->pi_number[i] - pcOther.pi_number[i] - borrow;
        if (diff < 0)
        {
            diff += 10;
            borrow = 1;
        }
        else
        {
            borrow = 0;
        }
        result.pi_number[i] = diff;
    }
}

CNumber CNumber::operator+(const CNumber &pcOther) const
{
    CNumber result;
    if (is_negative == pcOther.is_negative)
    {
        result.is_negative = is_negative;
        addAbsolute(pcOther, result);
    }
    else
    {
        // Different signs so we do subtraction
        if (compareAbsolute(pcOther) >= 0)
        {
            result.is_negative = is_negative;
            subtractAbsolute(pcOther, result);
        }
        else
        {
            result.is_negative = pcOther.is_negative;
            CNumber temp = pcOther;
            temp.subtractAbsolute(*this, result);
        }
    }
    return result;
}

CNumber CNumber::operator-(const CNumber &pcOther) const
{
    CNumber result;
    if (is_negative != pcOther.is_negative)    // Different signs so we do addition
    {
        result.is_negative = is_negative;
        addAbsolute(pcOther, result);
    }
    else
    {
        if (compareAbsolute(pcOther) >= 0)
        {
            result.is_negative = this->is_negative;
            this->subtractAbsolute(pcOther, result);
        }
        else
        {
            result.is_negative = !pcOther.is_negative;
            CNumber temp = pcOther;
            temp.subtractAbsolute(*this, result);
        }
    }
    return result;
}

CNumber CNumber::operator*(const CNumber &pcOther) const
{
    CNumber result;
    std::memset(result.pi_number, 0, sizeof(int) * result.i_length);

    for (int i = 0; i < this->i_length; ++i)
    {
        if (this->pi_number[i] == 0)
            continue;

        int carry = 0;
        for (int j = 0; j < pcOther.i_length && (i + j) < result.i_length; ++j)
        {
            int temp = result.pi_number[i + j] + this->pi_number[i] * pcOther.pi_number[j] + carry;
            result.pi_number[i + j] = temp % 10;
            carry = temp / 10;
        }
        if ((i + pcOther.i_length) < result.i_length)
        {
            result.pi_number[i + pcOther.i_length] += carry;
        }
    }

    result.is_negative = this->is_negative ^ pcOther.is_negative;

    bool all_zero = true;
    for (int i = 0; all_zero && i < result.i_length; ++i)
    {
        if (result.pi_number[i] != 0)
        {
            all_zero = false;
        }
    }
    if (all_zero)
    {
        result.is_negative = false;
    }

    return result;
}

CNumber CNumber::operator/(const CNumber &pcOther) const
{
    CNumber result;
    if (pcOther.compareAbsolute(*this) == 0 && pcOther.is_negative == is_negative)
    {
        result = 1;
        return result;
    }

    result.is_negative = this->is_negative ^ pcOther.is_negative;

    CNumber dividend = *this;
    dividend.is_negative = false;
    CNumber divisor = pcOther;
    divisor.is_negative = false;

    bool divisor_zero = true;
    for (int i = 0; divisor_zero && i < divisor.i_length; ++i)
    {
        if (divisor.pi_number[i] != 0)
        {
            divisor_zero = false;
        }
    }
    if (divisor_zero)
    {
        // Undefined behavior return zero
        std::memset(result.pi_number, 0, sizeof(int) * result.i_length);
        result.is_negative = false;
        return result;
    }

    CNumber quotient;
    CNumber current;

    for (int i = dividend.i_length - 1; i >= 0; --i)
    {
        // Shift current left
        for (int j = current.i_length - 1; j > 0; --j)
        {
            current.pi_number[j] = current.pi_number[j - 1];
        }
        current.pi_number[0] = dividend.pi_number[i];

        int cnt = 0; // How many times divisor fits into current
        while (current.compareAbsolute(divisor) >= 0)
        {
            current.subtractAbsolute(divisor, current);
            cnt++;
        }

        quotient.pi_number[i] = cnt;
    }

    bool all_zero = true;
    for (int i = 0; all_zero && i < quotient.i_length; ++i)
    {
        if (quotient.pi_number[i] != 0)
        {
            all_zero = false;
        }
    }
    if (all_zero)
    {
        quotient.is_negative = false;
    }

    return quotient;
}

std::string CNumber::sToStr() const
{
    std::string s;
    if (is_negative)
        s += '-';

    int iMostSignificant = i_length - 1;
    while (iMostSignificant > 0 && pi_number[iMostSignificant] == 0)
        iMostSignificant--;

    for (int i = iMostSignificant; i >= 0; --i)
    {
        s += static_cast<char>('0' + pi_number[i]);
    }

    return s;
}
 modify this code to work with any length numbers even bigger than const size