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

Quelle  txtrange.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 <editeng/txtrange.hxx>
#include <math.h>
#include <tools/poly.hxx>
#include <tools/debug.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>

#include <vector>

TextRanger::TextRanger( const basegfx::B2DPolyPolygon& rPolyPolygon,
                        const basegfx::B2DPolyPolygon* pLinePolyPolygon,
                        sal_uInt16 nCacheSz, sal_uInt16 nLft, sal_uInt16 nRght,
                        bool bSimpl, bool bInnr, bool bVert ) :
    maPolyPolygon( rPolyPolygon.count() ),
    nCacheSize( nCacheSz ),
    nRight( nRght ),
    nLeft( nLft ),
    nUpper( 0 ),
    nLower( 0 ),
    nPointCount( 0 ),
    bSimple( bSimpl ),
    bInner( bInnr ),
    bVertical( bVert )
{
    sal_uInt32 nCount(rPolyPolygon.count());

    for(sal_uInt32 i(0); i < nCount; i++)
    {
        const basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(i).getDefaultAdaptiveSubdivision());
        nPointCount += aCandidate.count();
        maPolyPolygon.Insert( tools::Polygon(aCandidate), static_cast<sal_uInt16>(i) );
    }

    if( pLinePolyPolygon )
    {
        nCount = pLinePolyPolygon->count();
        mpLinePolyPolygon = tools::PolyPolygon(nCount);

        for(sal_uInt32 i(0); i < nCount; i++)
        {
            const basegfx::B2DPolygon aCandidate(pLinePolyPolygon->getB2DPolygon(i).getDefaultAdaptiveSubdivision());
            nPointCount += aCandidate.count();
            mpLinePolyPolygon->Insert( tools::Polygon(aCandidate), static_cast<sal_uInt16>(i) );
        }
    }
    else
        mpLinePolyPolygon.reset();
}


TextRanger::~TextRanger()
{
    mRangeCache.clear();
}

/* TextRanger::SetVertical(..)
   If there's is a change in the writing direction,
   the cache has to be cleared.
*/

void TextRanger::SetVertical( bool bNew )
{
    if( IsVertical() != bNew )
    {
        bVertical = bNew;
        mRangeCache.clear();
    }
}

namespace {

//! SvxBoundArgs is used to perform temporary calculations on a range array.
//! Temporary instances are created in TextRanger::GetTextRanges()
class SvxBoundArgs
{
    std::vector<bool> aBoolArr;
    std::deque<tools::Long>* pLongArr;
    TextRanger *pTextRanger;
    tools::Long nMin;
    tools::Long nMax;
    tools::Long nTop;
    tools::Long nBottom;
    tools::Long nUpDiff;
    tools::Long nLowDiff;
    tools::Long nUpper;
    tools::Long nLower;
    tools::Long nStart;
    tools::Long nEnd;
    sal_uInt16 nCut;
    sal_uInt16 nLast;
    sal_uInt16 nNext;
    sal_uInt8 nAct;
    sal_uInt8 nFirst;
    bool bClosed : 1;
    bool bInner : 1;
    bool bMultiple : 1;
    bool bConcat : 1;
    bool bRotate : 1;
    void NoteRange( bool bToggle );
    tools::Long Cut( tools::Long nY, const Point& rPt1, const Point& rPt2 );
    void Add();
    void NoteFarPoint_( tools::Long nPx, tools::Long nPyDiff, tools::Long nDiff );
    void NoteFarPoint( tools::Long nPx, tools::Long nPyDiff, tools::Long nDiff )
        { if( nDiff ) NoteFarPoint_( nPx, nPyDiff, nDiff ); }
    tools::Long CalcMax( const Point& rPt1, const Point& rPt2, tools::Long nRange, tools::Long nFar );
    void CheckCut( const Point& rLst, const Point& rNxt );
    tools::Long A( const Point& rP ) const { return bRotate ? rP.Y() : rP.X(); }
    tools::Long B( const Point& rP ) const { return bRotate ? rP.X() : rP.Y(); }
public:
    SvxBoundArgs( TextRanger* pRanger, std::deque<tools::Long>* pLong, const Range& rRange );
    void NotePoint( const tools::Long nA ) { NoteMargin( nA - nStart, nA + nEnd ); }
    void NoteMargin( const tools::Long nL, const tools::Long nR )
        { if( nMin > nL ) nMin = nL; if( nMax < nR ) nMax = nR; }
    sal_uInt16 Area( const Point& rPt );
    void NoteUpLow( tools::Long nA, const sal_uInt8 nArea );
    void Calc( const tools::PolyPolygon& rPoly );
    void Concat( const tools::PolyPolygon* pPoly );
    // inlines
    void NoteLast() { if( bMultiple ) NoteRange( nAct == nFirst ); }
    void SetConcat( const bool bNew ){ bConcat = bNew; }
    bool IsConcat() const { return bConcat; }
};

}

SvxBoundArgs::SvxBoundArgs( TextRanger* pRanger, std::deque<tools::Long>* pLong,
    const Range& rRange )
    : pLongArr(pLong)
    , pTextRanger(pRanger)
    , nMin(0)
    , nMax(0)
    , nTop(rRange.Min())
    , nBottom(rRange.Max())
    , nCut(0)
    , nLast(0)
    , nNext(0)
    , nAct(0)
    , nFirst(0)
    , bClosed(false)
    , bInner(pRanger->IsInner())
    , bMultiple(bInner || !pRanger->IsSimple())
    , bConcat(false)
    , bRotate(pRanger->IsVertical())
{
    if( bRotate )
    {
        nStart = pRanger->GetUpper();
        nEnd = pRanger->GetLower();
        nLowDiff = pRanger->GetLeft();
        nUpDiff = pRanger->GetRight();
    }
    else
    {
        nStart = pRanger->GetLeft();
        nEnd = pRanger->GetRight();
        nLowDiff = pRanger->GetUpper();
        nUpDiff = pRanger->GetLower();
    }
    nUpper = nTop - nUpDiff;
    nLower = nBottom + nLowDiff;
    pLongArr->clear();
}

tools::Long SvxBoundArgs::CalcMax( const Point& rPt1, const Point& rPt2,
    tools::Long nRange, tools::Long nFarRange )
{
    double nDa = Cut( nRange, rPt1, rPt2 ) - Cut( nFarRange, rPt1, rPt2 );
    double nB;
    if( nDa < 0 )
    {
        nDa = -nDa;
        nB = nEnd;
    }
    else
        nB = nStart;

    nB = std::hypot(nB, nDa);

    if (nB == 0) // avoid div / 0
        return 0;

    nB = nRange + nDa * ( nFarRange - nRange ) / nB;

    bool bNote;
    if( nB < B(rPt2) )
        bNote = nB > B(rPt1);
    else
        bNote = nB < B(rPt1);
    if( bNote )
        return( tools::Long( nB ) );
    return 0;
}

void SvxBoundArgs::CheckCut( const Point& rLst, const Point& rNxt )
{
    if( nCut & 1 )
        NotePoint( Cut( nBottom, rLst, rNxt ) );
    if( nCut & 2 )
        NotePoint( Cut( nTop, rLst, rNxt ) );
    if( rLst.X() == rNxt.X() || rLst.Y() == rNxt.Y() )
        return;

    tools::Long nYps;
    if( nLowDiff && ( ( nCut & 1 ) || nLast == 1 || nNext == 1 ) )
    {
        nYps = CalcMax( rLst, rNxt, nBottom, nLower );
        if( nYps )
            NoteFarPoint_( Cut( nYps, rLst, rNxt ), nLower-nYps, nLowDiff );
    }
    if( nUpDiff && ( ( nCut & 2 ) || nLast == 2 || nNext == 2 ) )
    {
        nYps = CalcMax( rLst, rNxt, nTop, nUpper );
        if( nYps )
            NoteFarPoint_( Cut( nYps, rLst, rNxt ), nYps-nUpper, nUpDiff );
    }
}

void SvxBoundArgs::NoteFarPoint_( tools::Long nPa, tools::Long nPbDiff, tools::Long nDiff )
{
    tools::Long nTmpA;
    double nQuot = 2 * nDiff - nPbDiff;
    nQuot *= nPbDiff;
    nQuot = sqrt( nQuot );
    nQuot /= nDiff;
    nTmpA = nPa - tools::Long( nStart * nQuot );
    nPbDiff = nPa + tools::Long( nEnd * nQuot );
    NoteMargin( nTmpA, nPbDiff );
}

void SvxBoundArgs::NoteRange( bool bToggle )
{
    DBG_ASSERT( nMax >= nMin || bInner, "NoteRange: Min > Max?");
    if( nMax < nMin )
        return;
    if( !bClosed )
        bToggle = false;
    sal_uInt16 nIdx = 0;
    sal_uInt16 nCount = pLongArr->size();
    DBG_ASSERT( nCount == 2 * aBoolArr.size(), "NoteRange: Incompatible Sizes" );
    while( nIdx < nCount && (*pLongArr)[ nIdx ] < nMin )
        ++nIdx;
    bool bOdd = (nIdx % 2) != 0;
    // No overlap with existing intervals?
    if( nIdx == nCount || ( !bOdd && nMax < (*pLongArr)[ nIdx ] ) )
    {   // Then a new one is inserted ...
        pLongArr->insert( pLongArr->begin() + nIdx, nMin );
        pLongArr->insert( pLongArr->begin() + nIdx + 1, nMax );
        aBoolArr.insert( aBoolArr.begin() + (nIdx/2), bToggle );
    }
    else
    {   // expand an existing interval ...
        sal_uInt16 nMaxIdx = nIdx;
        // If we end up on a left interval boundary, it must be reduced to nMin.
        if( bOdd )
            --nIdx;
        else
            (*pLongArr)[ nIdx ] = nMin;
        while( nMaxIdx < nCount && (*pLongArr)[ nMaxIdx ] < nMax )
            ++nMaxIdx;
        DBG_ASSERT( nMaxIdx > nIdx || nMin == nMax, "NoteRange: Funny Situation." );
        if( nMaxIdx )
            --nMaxIdx;
        if( nMaxIdx < nIdx )
            nMaxIdx = nIdx;
        // If we end up on a right interval boundary, it must be raised to nMax.
        if( nMaxIdx % 2 )
            (*pLongArr)[ nMaxIdx-- ] = nMax;
        // Possible merge of intervals.
        sal_uInt16 nDiff = nMaxIdx - nIdx;
        nMaxIdx = nIdx / 2; // From here on is nMaxIdx the Index in BoolArray.
        if( nDiff )
        {
            pLongArr->erase( pLongArr->begin() + nIdx + 1, pLongArr->begin() + nIdx + 1 + nDiff );
            nDiff /= 2;
            sal_uInt16 nStop = nMaxIdx + nDiff;
            for( sal_uInt16 i = nMaxIdx; i < nStop; ++i )
                bToggle ^= aBoolArr[ i ];
            aBoolArr.erase( aBoolArr.begin() + nMaxIdx, aBoolArr.begin() + (nMaxIdx + nDiff) );
        }
        DBG_ASSERT( nMaxIdx < aBoolArr.size(), "NoteRange: Too much deleted" );
        aBoolArr[ nMaxIdx ] = aBoolArr[ nMaxIdx ] != bToggle;
    }
}

void SvxBoundArgs::Calc( const tools::PolyPolygon& rPoly )
{
    sal_uInt16 nCount;
    nAct = 0;
    forauto const& rPol : rPoly )
    {
        nCount = rPol.GetSize();
        if( nCount )
        {
            const Point& rNull = rPol[ 0 ];
            bClosed = IsConcat() || ( rNull == rPol[ nCount - 1 ] );
            nLast = Area( rNull );
            if( nLast & 12 )
            {
                nFirst = 3;
                if( bMultiple )
                    nAct = 0;
            }
            else
            {
                // The first point of the polygon is within the line.
                if( nLast )
                {
                    if( bMultiple || !nAct )
                    {
                        nMin = USHRT_MAX;
                        nMax = 0;
                    }
                    if( nLast & 1 )
                        NoteFarPoint( A(rNull), nLower - B(rNull), nLowDiff );
                    else
                        NoteFarPoint( A(rNull), B(rNull) - nUpper, nUpDiff );
                }
                else
                {
                    if( bMultiple || !nAct )
                    {
                        nMin = A(rNull);
                        nMax = nMin + nEnd;
                        nMin -= nStart;
                    }
                    else
                        NotePoint( A(rNull) );
                }
                nFirst = 0; // leaving the line in which direction?
                nAct = 3;   // we are within the line at the moment.
            }
            if( nCount > 1 )
            {
                sal_uInt16 nIdx = 1;
                whiletrue )
                {
                    const Point& rLast = rPol[ nIdx - 1 ];
                    if( nIdx == nCount )
                        nIdx = 0;
                    const Point& rNext = rPol[ nIdx ];
                    nNext = Area( rNext );
                    nCut = nNext ^ nLast;
                    sal_uInt16 nOldAct = nAct;
                    if( nAct )
                        CheckCut( rLast, rNext );
                    if( nCut & 4 )
                    {
                        NoteUpLow( Cut( nLower, rLast, rNext ), 2 );
                        if( nAct && nAct != nOldAct )
                        {
                            nOldAct = nAct;
                            CheckCut( rLast, rNext );
                        }
                    }
                    if( nCut & 8 )
                    {
                        NoteUpLow( Cut( nUpper, rLast, rNext ), 1 );
                        if( nAct && nAct != nOldAct )
                            CheckCut( rLast, rNext );
                    }
                    if( !nIdx )
                    {
                        if( !( nNext & 12 ) )
                            NoteLast();
                        break;
                    }
                    if( !( nNext & 12 ) )
                    {
                        if( !nNext )
                            NotePoint( A(rNext) );
                        else if( nNext & 1 )
                            NoteFarPoint( A(rNext), nLower-B(rNext), nLowDiff );
                        else
                            NoteFarPoint( A(rNext), B(rNext)-nUpper, nUpDiff );
                    }
                    nLast = nNext;
                    if( ++nIdx == nCount && !bClosed )
                    {
                        if( !( nNext & 12 ) )
                            NoteLast();
                        break;
                    }
                }
            }
            if( bMultiple && IsConcat() )
            {
                Add();
                nAct = 0;
            }
        }
    }
    if( !bMultiple )
    {
        DBG_ASSERT( pLongArr->empty(), "I said: Simple!" );
        if( nAct )
        {
            if( bInner )
            {
                tools::Long nTmpMin = nMin + 2 * nStart;
                tools::Long nTmpMax = nMax - 2 * nEnd;
                if( nTmpMin <= nTmpMax )
                {
                    pLongArr->push_front(nTmpMax);
                    pLongArr->push_front(nTmpMin);
                }
            }
            else
            {
                pLongArr->push_front(nMax);
                pLongArr->push_front(nMin);
            }
        }
    }
    else if( !IsConcat() )
        Add();
}

void SvxBoundArgs::Add()
{
    size_t nCount = aBoolArr.size();
    if( nCount && ( !bInner || !pTextRanger->IsSimple() ) )
    {
        bool bDelete = aBoolArr.front();
        if( bInner )
            bDelete = !bDelete;
        sal_uInt16 nLongIdx = 1;
        for( size_t nBoolIdx = 1; nBoolIdx < nCount; ++nBoolIdx )
        {
            if( bDelete )
            {
                sal_uInt16 next = 2;
                while( nBoolIdx < nCount && !aBoolArr[ nBoolIdx++ ] &&
                       (!bInner || nBoolIdx < nCount ) )
                    next += 2;
                pLongArr->erase( pLongArr->begin() + nLongIdx, pLongArr->begin() + nLongIdx + next );
                next /= 2;
                nBoolIdx = nBoolIdx - next;
                nCount = nCount - next;
                aBoolArr.erase( aBoolArr.begin() + nBoolIdx, aBoolArr.begin() + (nBoolIdx + next) );
                if (nBoolIdx > 0)
                    aBoolArr[ nBoolIdx - 1 ] = false;
#if OSL_DEBUG_LEVEL > 1
                else
                    ++next;
#endif
            }
            bDelete = nBoolIdx < nCount && aBoolArr[ nBoolIdx ];
            nLongIdx += 2;
            DBG_ASSERT( nLongIdx == 2*nBoolIdx+1, "BoundArgs: Array-Idx Confusion" );
            DBG_ASSERT( aBoolArr.size()*2 == pLongArr->size(),
                        "BoundArgs: Array-Count: Confusion" );
        }
    }
    if( pLongArr->empty() )
        return;

    if( !bInner )
        return;

    pLongArr->pop_front();
    pLongArr->pop_back();

    // Here the line is held inside a large rectangle for "simple"
    // contour wrap. Currently (April 1999) the EditEngine evaluates
    // only the first rectangle. If it one day is able to output a line
    // in several parts, it may be advisable to delete the following lines.
    if( pTextRanger->IsSimple() && pLongArr->size() > 2 )
        pLongArr->erase( pLongArr->begin() + 1, pLongArr->end() - 1 );
}

void SvxBoundArgs::Concat( const tools::PolyPolygon* pPoly )
{
    SetConcat( true );
    DBG_ASSERT( pPoly, "Nothing to do?" );
    std::deque<tools::Long>* pOld = pLongArr;
    pLongArr = new std::deque<tools::Long>;
    aBoolArr.clear();
    bInner = false;
    Calc( *pPoly ); // Note that this updates pLongArr, which is why we swapped it out earlier.
    std::deque<tools::Long>::size_type nCount = pLongArr->size();
    std::deque<tools::Long>::size_type nIdx = 0;
    std::deque<tools::Long>::size_type i = 0;
    bool bSubtract = pTextRanger->IsInner();
    while( i < nCount )
    {
        std::deque<tools::Long>::size_type nOldCount = pOld->size();
        if( nIdx == nOldCount )
        {   // Reached the end of the old Array...
            if( !bSubtract )
                pOld->insert( pOld->begin() + nIdx, pLongArr->begin() + i, pLongArr->end() );
            break;
        }
        tools::Long nLeft = (*pLongArr)[ i++ ];
        tools::Long nRight = (*pLongArr)[ i++ ];
        std::deque<tools::Long>::size_type nLeftPos = nIdx + 1;
        while( nLeftPos < nOldCount && nLeft > (*pOld)[ nLeftPos ] )
            nLeftPos += 2;
        if( nLeftPos >= nOldCount )
        {   // The current interval belongs to the end of the old array ...
            if( !bSubtract )
                pOld->insert( pOld->begin() + nOldCount, pLongArr->begin() + i - 2, pLongArr->end() );
            break;
        }
        std::deque<tools::Long>::size_type nRightPos = nLeftPos - 1;
        while( nRightPos < nOldCount && nRight >= (*pOld)[ nRightPos ] )
            nRightPos += 2;
        if( nRightPos < nLeftPos )
        {   // The current interval belongs between two old intervals
            if( !bSubtract )
                pOld->insert( pOld->begin() + nRightPos, pLongArr->begin() + i - 2, pLongArr->begin() + i );
        }
        else if( bSubtract ) // Subtract, if necessary separate
        {
            const tools::Long nOld = (*pOld)[nLeftPos - 1];
            if (nLeft > nOld)
            {   // Now we split the left part...
                if( nLeft - 1 > nOld )
                {
                    pOld->insert( pOld->begin() + nLeftPos - 1, nOld );
                    pOld->insert( pOld->begin() + nLeftPos, nLeft - 1 );
                    nLeftPos += 2;
                    nRightPos += 2;
                }
            }
            if( nRightPos - nLeftPos > 1 )
                pOld->erase( pOld->begin() + nLeftPos, pOld->begin() + nRightPos - 1 );
            if (++nRight >= (*pOld)[nLeftPos])
                pOld->erase( pOld->begin() + nLeftPos - 1, pOld->begin() + nLeftPos + 1 );
            else
                (*pOld)[ nLeftPos - 1 ] = nRight;
        }
        else // Merge
        {
            if( nLeft < (*pOld)[ nLeftPos - 1 ] )
                (*pOld)[ nLeftPos - 1 ] = nLeft;
            if( nRight > (*pOld)[ nRightPos - 1 ] )
                (*pOld)[ nRightPos - 1 ] = nRight;
            if( nRightPos - nLeftPos > 1 )
                pOld->erase( pOld->begin() + nLeftPos, pOld->begin() + nRightPos - 1 );

        }
        nIdx = nLeftPos - 1;
    }
    delete pLongArr;
}

/*************************************************************************
 * SvxBoundArgs::Area returns the area in which the point is located.
 * 0 = within the line
 * 1 = below, but within the upper edge
 * 2 = above, but within the lower edge
 * 5 = below the upper edge
 *10 = above the lower edge
 *************************************************************************/


sal_uInt16 SvxBoundArgs::Area( const Point& rPt )
{
    tools::Long nB = B( rPt );
    if( nB >= nBottom )
    {
        if( nB >= nLower )
            return 5;
        return 1;
    }
    if( nB <= nTop )
    {
        if( nB <= nUpper )
            return 10;
        return 2;
    }
    return 0;
}

/*************************************************************************
 * lcl_Cut calculates the X-Coordinate of the distance (Pt1-Pt2) at the
 * Y-Coordinate nY.
 * It is assumed that the one of the points are located above and the other
 * one below the Y-Coordinate.
 *************************************************************************/


tools::Long SvxBoundArgs::Cut( tools::Long nB, const Point& rPt1, const Point& rPt2 )
{
    if( pTextRanger->IsVertical() )
    {
        double nQuot = nB - rPt1.X();
        nQuot /= ( rPt2.X() - rPt1.X() );
        nQuot *= ( rPt2.Y() - rPt1.Y() );
        return tools::Long( rPt1.Y() + nQuot );
    }
    double nQuot = nB - rPt1.Y();
    nQuot /= ( rPt2.Y() - rPt1.Y() );
    nQuot *= ( rPt2.X() - rPt1.X() );
    return tools::Long( rPt1.X() + nQuot );
}

void SvxBoundArgs::NoteUpLow( tools::Long nA, const sal_uInt8 nArea )
{
    if( nAct )
    {
        NoteMargin( nA, nA );
        if( bMultiple )
        {
            NoteRange( nArea != nAct );
            nAct = 0;
        }
        if( !nFirst )
            nFirst = nArea;
    }
    else
    {
        nAct = nArea;
        nMin = nA;
        nMax = nA;
    }
}

std::deque<tools::Long>* TextRanger::GetTextRanges( const Range& rRange )
{
    DBG_ASSERT( rRange.Min() || rRange.Max(), "Zero-Range not allowed, Bye Bye" );
    //Can we find the result we need in the cache?
    for (auto & elem : mRangeCache)
    {
        if (elem.range == rRange)
            return &(elem.results);
    }
    //Calculate a new result
    RangeCacheItem rngCache(rRange);
    SvxBoundArgs aArg( this, &(rngCache.results), rRange );
    aArg.Calc( maPolyPolygon );
    if( mpLinePolyPolygon )
        aArg.Concat( &*mpLinePolyPolygon );
    //Add new result to the cache
    mRangeCache.push_back(std::move(rngCache));
    if (mRangeCache.size() > nCacheSize)
        mRangeCache.pop_front();
    return &(mRangeCache.back().results);
}

const tools::Rectangle& TextRanger::GetBoundRect_() const
{
    DBG_ASSERT( !mxBound, "Don't call twice." );
    mxBound = maPolyPolygon.GetBoundRect();
    return *mxBound;
}


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

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.17 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.