Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/tools/source/generic/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 18 kB image not shown  

Quelle  bigint.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <osl/diagnose.h>
#include <tools/bigint.hxx>

#include <algorithm>
#include <cmath>
#include <cstring>
#include <limits>
#include <span>

/**
 * The range in which we can perform add/sub without fear of overflow
 */

const sal_Int32 MY_MAXLONG  = 0x3fffffff;
const sal_Int32 MY_MINLONG  = -MY_MAXLONG;

/*
 * The algorithms for Addition, Subtraction, Multiplication and Division
 * of large numbers originate from SEMINUMERICAL ALGORITHMS by
 * DONALD E. KNUTH in the series The Art of Computer Programming:
 * chapter 4.3.1. The Classical Algorithms.
 */


BigInt BigInt::MakeBig() const
{
    if (IsBig())
    {
        BigInt ret(*this);
        while ( ret.nLen > 1 && ret.nNum[ret.nLen-1] == 0 )
            ret.nLen--;
        return ret;
    }

    BigInt ret;
    if (nVal < 0)
    {
        ret.bIsNeg = true;
        ret.nNum[0] = -static_cast<sal_Int64>(nVal);
    }
    else
    {
        ret.bIsNeg = false;
        ret.nNum[0] = nVal;
    }
    ret.nLen = 1;
    return ret;
}

void BigInt::Normalize()
{
    if (IsBig())
    {
        while ( nLen > 1 && nNum[nLen-1] == 0 )
            nLen--;

        if (nLen < 2)
        {
            static constexpr sal_uInt32 maxForPosInt32 = std::numeric_limits<sal_Int32>::max();
            static constexpr sal_uInt32 maxForNegInt32 = -sal_Int64(std::numeric_limits<sal_Int32>::min());
            sal_uInt32 nNum0 = nNum[0];
            if (bIsNeg && nNum0 <= maxForNegInt32)
            {
                nVal = -sal_Int64(nNum0);
                nLen = 0;
            }
            else if (!bIsNeg && nNum0 <= maxForPosInt32)
            {
                nVal = nNum0;
                nLen = 0;
            }
        }
    }
}

// Normalization in DivLong requires that dividend is multiplied by a number, and the resulting
// value has 1 more 32-bit "digits". 'ret' provides enough room for that. Use of std::span gives
// run-time index checking in debug builds.
static std::span<sal_uInt32> Mult(std::span<const sal_uInt32> aNum, sal_uInt32 nMul, std::span<sal_uInt32> retBuf)
{
    assert(retBuf.size() >= aNum.size());
    sal_uInt64 nK = 0;
    for (size_t i = 0; i < aNum.size(); i++)
    {
        sal_uInt64 nTmp = static_cast<sal_uInt64>(aNum[i]) * nMul + nK;
        nK = nTmp >> 32;
        retBuf[i] = static_cast<sal_uInt32>(nTmp);
    }

    if ( nK )
    {
        assert(retBuf.size() > aNum.size());
        retBuf[aNum.size()] = nK;
        return retBuf.subspan(0, aNum.size() + 1);
    }

    return retBuf.subspan(0, aNum.size());
}

static size_t DivInPlace(std::span<sal_uInt32> aNum, sal_uInt32 nDiv, sal_uInt32& rRem)
{
    assert(aNum.size() > 0);
    sal_uInt64 nK = 0;
    for (int i = aNum.size() - 1; i >= 0; i--)
    {
        sal_uInt64 nTmp = aNum[i] + (nK << 32);
        aNum[i] = nTmp / nDiv;
        nK            = nTmp % nDiv;
    }
    rRem = nK;

    if (aNum[aNum.size() - 1] == 0)
        return aNum.size() - 1;

    return aNum.size();
}

bool BigInt::ABS_IsLessBig(const BigInt& rVal) const
{
    assert(IsBig() && rVal.IsBig());
    if ( rVal.nLen < nLen)
        return false;
    if ( rVal.nLen > nLen )
        return true;

    int i = nLen - 1;
    while (i > 0 && nNum[i] == rVal.nNum[i])
        --i;
    return nNum[i] < rVal.nNum[i];
}

void BigInt::AddBig(BigInt& rB, BigInt& rRes)
{
    assert(IsBig() && rB.IsBig());
    if ( bIsNeg == rB.bIsNeg )
    {
        int  i;
        char len;

        // if length of the two values differ, fill remaining positions
        // of the smaller value with zeros.
        if (nLen >= rB.nLen)
        {
            len = nLen;
            for (i = rB.nLen; i < len; i++)
                rB.nNum[i] = 0;
        }
        else
        {
            len = rB.nLen;
            for (i = nLen; i < len; i++)
                nNum[i] = 0;
        }

        // Add numerals, starting from the back
        sal_Int64 k = 0;
        for (i = 0; i < len; i++) {
            sal_Int64 nZ = static_cast<sal_Int64>(nNum[i]) + static_cast<sal_Int64>(rB.nNum[i]) + k;
            if (nZ > sal_Int64(std::numeric_limits<sal_uInt32>::max()))
                k = 1;
            else
                k = 0;
            rRes.nNum[i] = static_cast<sal_uInt32>(nZ);
        }
        // If an overflow occurred, add to solution
        if (k)
        {
            assert(i < MAX_DIGITS);
            rRes.nNum[i] = 1;
            len++;
        }
        // Set length and sign
        rRes.nLen = len;
        rRes.bIsNeg = bIsNeg;
    }
    // If one of the values is negative, perform subtraction instead
    else
    {
        bIsNeg = !bIsNeg;
        rB.SubBig(*this, rRes);
        bIsNeg = !bIsNeg;
    }
}

void BigInt::SubBig(BigInt& rB, BigInt& rRes)
{
    assert(IsBig() && rB.IsBig());
    if ( bIsNeg == rB.bIsNeg )
    {
        char len;

        // if length of the two values differ, fill remaining positions
        // of the smaller value with zeros.
        if (nLen >= rB.nLen)
        {
            len = nLen;
            for (int i = rB.nLen; i < len; i++)
                rB.nNum[i] = 0;
        }
        else
        {
            len = rB.nLen;
            for (int i = nLen; i < len; i++)
                nNum[i] = 0;
        }

        const bool bThisIsLess = ABS_IsLessBig(rB);
        BigInt& rGreater = bThisIsLess ? rB : *this;
        BigInt& rSmaller = bThisIsLess ? *this : rB;

        sal_Int64 k = 0;
        for (int i = 0; i < len; i++)
        {
            sal_Int64 nZ = static_cast<sal_Int64>(rGreater.nNum[i]) - static_cast<sal_Int64>(rSmaller.nNum[i]) + k;
            if (nZ < 0)
                k = -1;
            else
                k = 0;
            rRes.nNum[i] = static_cast<sal_uInt32>(nZ);
        }

        // if a < b, revert sign
        rRes.bIsNeg = bThisIsLess ? !bIsNeg : bIsNeg;
        rRes.nLen   = len;
    }
    // If one of the values is negative, perform addition instead
    else
    {
        bIsNeg = !bIsNeg;
        AddBig(rB, rRes);
        bIsNeg = !bIsNeg;
        rRes.bIsNeg = bIsNeg;
    }
}

void BigInt::MultBig(const BigInt& rB, BigInt& rRes) const
{
    assert(IsBig() && rB.IsBig());

    rRes.bIsNeg = bIsNeg != rB.bIsNeg;
    rRes.nLen = nLen + rB.nLen;
    assert(rRes.nLen <= MAX_DIGITS);

    for (int i = 0; i < rRes.nLen; i++)
        rRes.nNum[i] = 0;

    for (int j = 0; j < rB.nLen; j++)
    {
        sal_uInt64 k = 0;
        int i;
        for (i = 0; i < nLen; i++)
        {
            sal_uInt64 nZ = static_cast<sal_uInt64>(nNum[i]) * static_cast<sal_uInt64>(rB.nNum[j]) +
                 static_cast<sal_uInt64>(rRes.nNum[i + j]) + k;
            rRes.nNum[i + j] = static_cast<sal_uInt32>(nZ);
            k = nZ >> 32;
        }
        rRes.nNum[i + j] = k;
    }
}

void BigInt::DivModBig(const BigInt& rB, BigInt* pDiv, BigInt* pMod) const
{
    assert(IsBig() && rB.IsBig());
    assert(nLen >= rB.nLen);

    assert(rB.nNum[rB.nLen - 1] != 0);
    sal_uInt32 nMult = static_cast<sal_uInt32>(0x100000000 / (static_cast<sal_Int64>(rB.nNum[rB.nLen - 1]) + 1));

    sal_uInt32 numBuf[MAX_DIGITS + 1]; // normalized dividend
    auto num = Mult({ nNum, nLen }, nMult, numBuf);
    if (num.size() == nLen)
    {
        num = std::span(numBuf, nLen + 1);
        num[nLen] = 0;
    }

    sal_uInt32 denBuf[MAX_DIGITS + 1]; // normalized divisor
    const auto den = Mult({ rB.nNum, rB.nLen }, nMult, denBuf);
    assert(den.size() == rB.nLen);
    const sal_uInt64 nDenMostSig = den[rB.nLen - 1];
    assert(nDenMostSig >= 0x100000000 / 2);
    const sal_uInt64 nDen2ndSig = rB.nLen > 1 ? den[rB.nLen - 2] : 0;

    BigInt aTmp;
    BigInt& rRes = pDiv ? *pDiv : aTmp;

    for (size_t j = num.size() - 1; j >= den.size(); j--)
    { // guess divisor
        assert(num[j] < nDenMostSig || (num[j] == nDenMostSig && num[j - 1] == 0));
        sal_uInt64 nTmp = ( static_cast<sal_uInt64>(num[j]) << 32 ) + num[j - 1];
        sal_uInt32 nQ;
        if (num[j] == nDenMostSig)
            nQ = 0xFFFFFFFF;
        else
            nQ = static_cast<sal_uInt32>(nTmp / nDenMostSig);

        if (nDen2ndSig && (nDen2ndSig * nQ) > ((nTmp - nDenMostSig * nQ) << 32) + num[j - 2])
            nQ--;
        // Start division
        sal_uInt32 nK = 0;
        size_t i;
        for (i = 0; i < den.size(); i++)
        {
            nTmp = static_cast<sal_uInt64>(num[j - den.size() + i])
                   - (static_cast<sal_uInt64>(den[i]) * nQ)
                   - nK;
            num[j - den.size() + i] = static_cast<sal_uInt32>(nTmp);
            nK = static_cast<sal_uInt32>(nTmp >> 32);
            if ( nK )
                nK = static_cast<sal_uInt32>(0x100000000 - nK);
        }
        sal_uInt32& rNum(num[j - den.size() + i]);
        rNum -= nK;
        if (num[j - den.size() + i] == 0)
            rRes.nNum[j - den.size()] = nQ;
        else
        {
            rRes.nNum[j - den.size()] = nQ - 1;
            nK = 0;
            for (i = 0; i < den.size(); i++)
            {
                nTmp = num[j - den.size() + i] + den[i] + nK;
                num[j - den.size() + i] = static_cast<sal_uInt32>(nTmp & 0xFFFFFFFF);
                if (nTmp > std::numeric_limits<sal_uInt32>::max())
                    nK = 1;
                else
                    nK = 0;
            }
        }
    }

    if (pMod)
    {
        pMod->nLen = DivInPlace(num, nMult, nMult);
        assert(pMod->nLen <= MAX_DIGITS);
        pMod->bIsNeg = bIsNeg;
        std::copy_n(num.begin(), pMod->nLen, pMod->nNum);
        pMod->Normalize();
    }
    if (pDiv) // rRes references pDiv
    {
        pDiv->bIsNeg = bIsNeg != rB.bIsNeg;
        pDiv->nLen = nLen - rB.nLen + 1;
        pDiv->Normalize();
    }
}

BigInt::BigInt( std::u16string_view rString )
    : nLen(0)
{
    bIsNeg = false;
    nVal   = 0;

    bool bNeg = false;
    auto p = rString.begin();
    auto pEnd = rString.end();
    if (p == pEnd)
        return;
    if ( *p == '-' )
    {
        bNeg = true;
        p++;
    }
    if (p == pEnd)
        return;
    while( p != pEnd && *p >= '0' && *p <= '9' )
    {
        *this *= 10;
        *this += *p - '0';
        p++;
    }
    if (IsBig())
        bIsNeg = bNeg;
    else if( bNeg )
        nVal = -nVal;
}

BigInt::BigInt( double nValue )
    : nVal(0)
{
    if ( nValue < 0 )
    {
        nValue *= -1;
        bIsNeg  = true;
    }
    else
    {
        bIsNeg  = false;
    }

    if ( nValue < 1 )
    {
        nVal   = 0;
        nLen   = 0;
    }
    else
    {
        int i=0;

        while ( ( nValue > 0x100000000 ) && ( i < MAX_DIGITS ) )
        {
            nNum[i] = static_cast<sal_uInt32>(fmod( nValue, 0x100000000 ));
            nValue -= nNum[i];
            nValue /= 0x100000000;
            i++;
        }
        if ( i < MAX_DIGITS )
            nNum[i++] = static_cast<sal_uInt32>(nValue);

        nLen = i;

        if ( i < 2 )
            Normalize();
    }
}

BigInt::BigInt( sal_uInt32 nValue )
    : nVal(0)
    , bIsNeg(false)
{
    if ( nValue & 0x80000000U )
    {
        nNum[0] = nValue;
        nLen = 1;
    }
    else
    {
        nVal   = nValue;
        nLen   = 0;
    }
}

BigInt::BigInt( sal_Int64 nValue )
    : nVal(0)
{
    bIsNeg = nValue < 0;
    nLen = 0;

    if ((nValue >= SAL_MIN_INT32) && (nValue <= SAL_MAX_INT32))
    {
        nVal = static_cast<sal_Int32>(nValue);
    }
    else
    {
        for (sal_uInt64 n = static_cast<sal_uInt64>(
                 (bIsNeg && nValue != std::numeric_limits<sal_Int64>::min()) ? -nValue : nValue);
             n != 0; n >>= 32)
            nNum[nLen++] = static_cast<sal_uInt32>(n);
    }
}

BigInt::operator double() const
{
    if (!IsBig())
        return static_cast<double>(nVal);
    else
    {
        int     i = nLen-1;
        double  nRet = static_cast<double>(nNum[i]);

        while ( i )
        {
            nRet *= 0x100000000;
            i--;
            nRet += static_cast<double>(nNum[i]);
        }

        if ( bIsNeg )
            nRet *= -1;

        return nRet;
    }
}

BigInt& BigInt::operator=( const BigInt& rBigInt )
{
    if (this == &rBigInt)
        return *this;

    if (rBigInt.IsBig())
        memcpy( static_cast<void*>(this), static_cast<const void*>(&rBigInt), sizeof( BigInt ) );
    else
    {
        nLen = 0;
        nVal = rBigInt.nVal;
    }
    return *this;
}

BigInt& BigInt::operator+=( const BigInt& rVal )
{
    if (!IsBig() && !rVal.IsBig())
    {
        if( nVal <= MY_MAXLONG && rVal.nVal <= MY_MAXLONG
            && nVal >= MY_MINLONG && rVal.nVal >= MY_MINLONG )
        { // No overflows may occur here
            nVal += rVal.nVal;
            return *this;
        }

        if( (nVal < 0) != (rVal.nVal < 0) )
        { // No overflows may occur here
            nVal += rVal.nVal;
            return *this;
        }
    }

    BigInt aTmp2 = rVal.MakeBig();
    MakeBig().AddBig(aTmp2, *this);
    Normalize();
    return *this;
}

BigInt& BigInt::operator-=( const BigInt& rVal )
{
    if (!IsBig() && !rVal.IsBig())
    {
        if ( nVal <= MY_MAXLONG && rVal.nVal <= MY_MAXLONG &&
             nVal >= MY_MINLONG && rVal.nVal >= MY_MINLONG )
        { // No overflows may occur here
            nVal -= rVal.nVal;
            return *this;
        }

        if ( (nVal < 0) == (rVal.nVal < 0) )
        { // No overflows may occur here
            nVal -= rVal.nVal;
            return *this;
        }
    }

    BigInt aTmp2 = rVal.MakeBig();
    MakeBig().SubBig(aTmp2, *this);
    Normalize();
    return *this;
}

BigInt& BigInt::operator*=( const BigInt& rVal )
{
    static const sal_Int32 MY_MAXSHORT = 0x00007fff;
    static const sal_Int32 MY_MINSHORT = -MY_MAXSHORT;

    if (!IsBig() && !rVal.IsBig()
         && nVal <= MY_MAXSHORT && rVal.nVal <= MY_MAXSHORT
         && nVal >= MY_MINSHORT && rVal.nVal >= MY_MINSHORT )
         // TODO: not optimal !!! W.P.
    { // No overflows may occur here
        nVal *= rVal.nVal;
    }
    else
    {
        rVal.MakeBig().MultBig(MakeBig(), *this);
        Normalize();
    }
    return *this;
}

void BigInt::DivMod(const BigInt& rVal, BigInt* pDiv, BigInt* pMod) const
{
    assert(pDiv || pMod); // Avoid useless calls
    if (!rVal.IsBig())
    {
        if ( rVal.nVal == 0 )
        {
            OSL_FAIL( "BigInt::operator/ --> divide by zero" );
            return;
        }

        if (rVal.nVal == 1)
        {
            if (pDiv)
            {
                *pDiv = *this;
                pDiv->Normalize();
            }
            if (pMod)
                *pMod = 0;
            return;
        }

        if (!IsBig())
        {
            // No overflows may occur here
            const sal_Int32 nDiv = nVal / rVal.nVal; // Compilers usually optimize adjacent
            const sal_Int32 nMod = nVal % rVal.nVal; // / and % into a single instruction
            if (pDiv)
                *pDiv = nDiv;
            if (pMod)
                *pMod = nMod;
            return;
        }

        if ( rVal.nVal == -1 )
        {
            if (pDiv)
            {
                *pDiv = *this;
                pDiv->bIsNeg = !bIsNeg;
                pDiv->Normalize();
            }
            if (pMod)
                *pMod = 0;
            return;
        }

        // Divide BigInt with an sal_uInt32
        sal_uInt32 nTmp;
        bool bNegate;
        if ( rVal.nVal < 0 )
        {
            nTmp = static_cast<sal_uInt32>(-rVal.nVal);
            bNegate = true;
        }
        else
        {
            nTmp = static_cast<sal_uInt32>(rVal.nVal);
            bNegate = false;
        }

        BigInt aTmp;
        BigInt& rDiv = pDiv ? *pDiv : aTmp;
        rDiv = *this;
        rDiv.nLen = DivInPlace({ rDiv.nNum, rDiv.nLen }, nTmp, nTmp);
        if (pDiv)
        {
            if (bNegate)
                pDiv->bIsNeg = !pDiv->bIsNeg;
            pDiv->Normalize();
        }
        if (pMod)
        {
            *pMod = BigInt(nTmp);
        }
        return;
    }

    BigInt tmpA = MakeBig(), tmpB = rVal.MakeBig();
    if (tmpA.ABS_IsLessBig(tmpB))
    {
        // First store *this to *pMod, then nullify *pDiv, to handle 'pDiv == this' case
        if (pMod)
        {
            *pMod = *this;
            pMod->Normalize();
        }
        if (pDiv)
            *pDiv = 0;
        return;
    }

    // Divide BigInt with BigInt
    tmpA.DivModBig(tmpB, pDiv, pMod);
}

BigInt& BigInt::operator/=( const BigInt& rVal )
{
    DivMod(rVal, this, nullptr);
    return *this;
}

BigInt& BigInt::operator%=( const BigInt& rVal )
{
    DivMod(rVal, nullptr, this);
    return *this;
}

bool operator==( const BigInt& rVal1, const BigInt& rVal2 )
{
    if (!rVal1.IsBig() && !rVal2.IsBig())
        return rVal1.nVal == rVal2.nVal;

    BigInt nA = rVal1.MakeBig(), nB = rVal2.MakeBig();
    return nA.bIsNeg == nB.bIsNeg && nA.nLen == nB.nLen
           && std::equal(nA.nNum, nA.nNum + nA.nLen, nB.nNum);
}

std::strong_ordering operator<=>(const BigInt& rVal1, const BigInt& rVal2)
{
    if (!rVal1.IsBig() && !rVal2.IsBig())
        return rVal1.nVal <=> rVal2.nVal;

    BigInt nA = rVal1.MakeBig(), nB = rVal2.MakeBig();
    if (nA.bIsNeg != nB.bIsNeg)
        return nB.bIsNeg ? std::strong_ordering::less : std::strong_ordering::greater;
    if (nA.nLen < nB.nLen)
        return nB.bIsNeg ? std::strong_ordering::greater : std::strong_ordering::less;
    if (nA.nLen > nB.nLen)
        return nB.bIsNeg ? std::strong_ordering::less : std::strong_ordering::greater;
    int i = nA.nLen - 1;
    while (i > 0 && nA.nNum[i] == nB.nNum[i])
        --i;
    return nB.bIsNeg ? (nB.nNum[i] <=> nA.nNum[i]) : (nA.nNum[i] <=> nB.nNum[i]);
}

tools::Long BigInt::Scale( tools::Long nVal, tools::Long nMul, tools::Long nDiv )
{
    BigInt aVal( nVal );

    aVal *= nMul;

    if ( aVal.IsNeg() != ( nDiv < 0 ) )
        aVal -= nDiv / 2; // for correct rounding
    else
        aVal += nDiv / 2; // for correct rounding

    aVal /= nDiv;

    return tools::Long( aVal );
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=96 H=92 G=93

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.