/* This file is part of the KDE project
   Copyright (C) 2002 Lennart Kudling <kudling@kde.org>
   Copyright (C) 2004 Laurent Montel <montel@kde.org>
   Copyright (C) 2005-2006 Thomas Zander <zander@kde.org>
   Copyright (C) 2006 Tim Beaulen <tbscope@gmail.com>
   Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "KarbonGlobal.h"
#include <math.h>

int KarbonGlobal::binomialCoeff(unsigned n, unsigned k)
{
    return
        static_cast<int>(
            0.5 +
            exp(
                factorialLn(n) -
                factorialLn(k) -
                factorialLn(n - k)));
}

qreal KarbonGlobal::factorialLn(unsigned n)
{
    const unsigned cacheSize = 100;

    // A static array is initialized to zero.
    static qreal cache[ cacheSize ];


    if (n <= 1)
        return 0.0;

    if (n <= cacheSize - 1) {
        return cache[ n ]
               ? cache[ n ]
               : (cache[ n ] = gammaLn(n + 1.0));
    } else {
        return gammaLn(n + 1.0);
    }
}

qreal KarbonGlobal::gammaLn(qreal x)
{
    static const qreal coeff[ 6 ] = {
        76.18009172947146,
        -86.50532032941677,
        24.01409824083091,
        -1.231739572450155,
        0.1208650973866179e-2,
        -0.5395239384953e-5
    };

    qreal y = x;

    qreal tmp = x + 5.5;
    tmp -= (x + 0.5) * log(tmp);

    qreal ser = 1.000000000190015;

    for (int i = 0; i < 5; ++i) {
        ser += coeff[ i ] / ++y;
    }

    return -tmp + log(2.5066282746310005 * ser / x);
}

qreal KarbonGlobal::scalarProduct(const QPointF &p1, const QPointF &p2)
{
    return p1.x() * p2.x() + p1.y() * p2.y();
}

bool KarbonGlobal::pointsAreNear(const QPointF &p1, const QPointF &p2, qreal range)
{
    return (p2.x() >= p1.x() - range && p2.x() <= p1.x() + range && p2.y() >= p1.y() - range && p2.y() <= p1.y() + range);
}

QPointF KarbonGlobal::crossProduct(const QPointF &v1, const QPointF &v2)
{
    return QPointF(v1.x() * v2.y(), -v1.y()*v2.x());
}
