You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1993 lines
47 KiB
1993 lines
47 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
|
|
|
|
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 "valuecalc.h"
|
|
|
|
#include "valueconverter.h"
|
|
|
|
#include <kdebug.h>
|
|
#include <errno.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
using namespace KSpread;
|
|
|
|
|
|
// Array-walk functions registered on ValueCalc object
|
|
|
|
void awSum (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if ((!val.isEmpty()) && (!val.isBoolean()) && (!val.isString()))
|
|
res = c->add (res, val);
|
|
}
|
|
|
|
void awSumA (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if (!val.isEmpty())
|
|
res = c->add (res, val);
|
|
}
|
|
|
|
void awSumSq (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if (!val.isEmpty())
|
|
res = c->add (res, c->sqr (val));
|
|
}
|
|
|
|
void awSumSqA (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if ((!val.isEmpty()) && (!val.isBoolean()) && (!val.isString()))
|
|
res = c->add (res, c->sqr (val));
|
|
}
|
|
|
|
void awCount (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if ((!val.isEmpty()) && (!val.isBoolean()) && (!val.isString()))
|
|
res = c->add (res, 1);
|
|
}
|
|
|
|
void awCountA (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if (!val.isEmpty())
|
|
res = c->add (res, 1);
|
|
}
|
|
|
|
void awMax (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if ((!val.isEmpty()) && (!val.isBoolean()) && (!val.isString()))
|
|
if (res.isEmpty())
|
|
res = val;
|
|
else
|
|
if (c->greater (val, res)) res = val;
|
|
}
|
|
|
|
void awMaxA (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if (!val.isEmpty())
|
|
if (res.isEmpty())
|
|
// convert to number, so that we don't return string/bool
|
|
res = c->conv()->asNumeric (val);
|
|
else
|
|
if (c->greater (val, res))
|
|
// convert to number, so that we don't return string/bool
|
|
res = c->conv()->asNumeric (val);
|
|
}
|
|
|
|
void awMin (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if ((!val.isEmpty()) && (!val.isBoolean()) && (!val.isString()))
|
|
if (res.isEmpty())
|
|
res = val;
|
|
else
|
|
if (c->lower (val, res)) res = val;
|
|
}
|
|
|
|
void awMinA (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if (!val.isEmpty())
|
|
if (res.isEmpty())
|
|
// convert to number, so that we don't return string/bool
|
|
res = c->conv()->asNumeric (val);
|
|
else
|
|
if (c->lower (val, res))
|
|
// convert to number, so that we don't return string/bool
|
|
res = c->conv()->asNumeric (val);
|
|
}
|
|
|
|
void awProd (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if ((!val.isEmpty()) && (!val.isBoolean()) && (!val.isString()))
|
|
res = c->mul (res, val);
|
|
}
|
|
|
|
void awProdA (ValueCalc *c, Value &res, Value val, Value)
|
|
{
|
|
if (!val.isEmpty())
|
|
res = c->mul (res, val);
|
|
}
|
|
|
|
// sum of squares of deviations, used to compute standard deviation
|
|
void awDevSq (ValueCalc *c, Value &res, Value val,
|
|
Value avg)
|
|
{
|
|
if (!val.isEmpty())
|
|
res = c->add (res, c->sqr (c->sub (val, avg)));
|
|
}
|
|
|
|
// sum of squares of deviations, used to compute standard deviation
|
|
void awDevSqA (ValueCalc *c, Value &res, Value val,
|
|
Value avg)
|
|
{
|
|
if ((!val.isEmpty()) && (!val.isBoolean()) && (!val.isString()))
|
|
res = c->add (res, c->sqr (c->sub (val, avg)));
|
|
}
|
|
|
|
|
|
bool isDate (Value val) {
|
|
Value::Format fmt = val.format();
|
|
if ((fmt == Value::fmt_Date) || (fmt == Value::fmt_DateTime))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// ***********************
|
|
// ****** ValueCalc ******
|
|
// ***********************
|
|
|
|
ValueCalc::ValueCalc (ValueConverter* c): converter( c )
|
|
{
|
|
// initialize the random number generator
|
|
srand (time (0));
|
|
|
|
// register array-walk functions
|
|
registerAwFunc ("sum", awSum);
|
|
registerAwFunc ("suma", awSumA);
|
|
registerAwFunc ("sumsq", awSumSq);
|
|
registerAwFunc ("sumsqa", awSumSqA);
|
|
registerAwFunc ("count", awCount);
|
|
registerAwFunc ("counta", awCountA);
|
|
registerAwFunc ("max", awMax);
|
|
registerAwFunc ("maxa", awMaxA);
|
|
registerAwFunc ("min", awMin);
|
|
registerAwFunc ("mina", awMinA);
|
|
registerAwFunc ("prod", awProd);
|
|
registerAwFunc ("proda", awProdA);
|
|
registerAwFunc ("devsq", awDevSq);
|
|
registerAwFunc ("devsqa", awDevSq);
|
|
}
|
|
|
|
Value ValueCalc::add (const Value &a, const Value &b)
|
|
{
|
|
if (a.isError()) return a;
|
|
if (b.isError()) return b;
|
|
Value res;
|
|
if (a.isInteger() && b.isEmpty() || a.isEmpty() && b.isInteger()
|
|
|| a.isInteger() && b.isInteger())
|
|
{
|
|
int aa, bb;
|
|
aa = converter->asInteger (a).asInteger();
|
|
bb = converter->asInteger (b).asInteger();
|
|
res = Value (aa + bb);
|
|
}
|
|
else
|
|
{
|
|
double aa, bb;
|
|
aa = converter->asFloat (a).asFloat();
|
|
bb = converter->asFloat (b).asFloat();
|
|
res = Value (aa + bb);
|
|
}
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (format (a.format(), b.format()));
|
|
// operation on two dates should produce a number
|
|
if (isDate(a) && isDate(b))
|
|
res.setFormat (Value::fmt_Number);
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::sub (const Value &a, const Value &b)
|
|
{
|
|
if (a.isError()) return a;
|
|
if (b.isError()) return b;
|
|
double aa, bb;
|
|
aa = converter->asFloat (a).asFloat();
|
|
bb = converter->asFloat (b).asFloat();
|
|
Value res = Value (aa - bb);
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (format (a.format(), b.format()));
|
|
// operation on two dates should produce a number
|
|
if (isDate(a) && isDate(b))
|
|
res.setFormat (Value::fmt_Number);
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::mul (const Value &a, const Value &b)
|
|
{
|
|
if (a.isError()) return a;
|
|
if (b.isError()) return b;
|
|
double aa, bb;
|
|
aa = converter->asFloat (a).asFloat();
|
|
bb = converter->asFloat (b).asFloat();
|
|
Value res = Value (aa * bb);
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (format (a.format(), b.format()));
|
|
// operation on two dates should produce a number
|
|
if (isDate(a) && isDate(b))
|
|
res.setFormat (Value::fmt_Number);
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::div (const Value &a, const Value &b)
|
|
{
|
|
if (a.isError()) return a;
|
|
if (b.isError()) return b;
|
|
double aa, bb;
|
|
aa = converter->asFloat (a).asFloat();
|
|
bb = converter->asFloat (b).asFloat();
|
|
Value res;
|
|
if (bb == 0.0)
|
|
return Value::errorDIV0();
|
|
else
|
|
res = Value (aa / bb);
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (format (a.format(), b.format()));
|
|
// operation on two dates should produce a number
|
|
if (isDate(a) && isDate(b))
|
|
res.setFormat (Value::fmt_Number);
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::mod (const Value &a, const Value &b)
|
|
{
|
|
if (a.isError()) return a;
|
|
if (b.isError()) return b;
|
|
double aa, bb;
|
|
aa = converter->asFloat (a).asFloat();
|
|
bb = converter->asFloat (b).asFloat();
|
|
Value res;
|
|
if (bb == 0.0)
|
|
return Value::errorDIV0();
|
|
else
|
|
{
|
|
double m = fmod (aa, bb);
|
|
// the following adjustments are needed by OpenFormula:
|
|
// can't simply use fixed increases/decreases, because the implementation
|
|
// of fmod may differ on various platforms, and we should always return
|
|
// the same results ...
|
|
if ((bb > 0) && (aa < 0)) // result must be positive here
|
|
while (m < 0) m += bb;
|
|
if (bb < 0) // result must be negative here, but not lower than bb
|
|
{
|
|
// bb is negative, hence the following two are correct
|
|
while (m < bb) m -= bb; // same as m+=fabs(bb)
|
|
while (m > 0) m += bb; // same as m-=fabs(bb)
|
|
}
|
|
|
|
res = Value (m);
|
|
}
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (format (a.format(), b.format()));
|
|
if (isDate(a) && isDate(b))
|
|
res.setFormat (Value::fmt_Number);
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::pow (const Value &a, const Value &b)
|
|
{
|
|
if (a.isError()) return a;
|
|
if (b.isError()) return b;
|
|
double aa, bb;
|
|
aa = converter->asFloat (a).asFloat();
|
|
bb = converter->asFloat (b).asFloat();
|
|
Value res = Value (::pow (aa, bb));
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (format (a.format(), b.format()));
|
|
// operation on date(s) should produce a number
|
|
if (isDate(a) || isDate(b))
|
|
res.setFormat (Value::fmt_Number);
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::sqr (const Value &a)
|
|
{
|
|
if (a.isError()) return a;
|
|
return mul (a, a);
|
|
}
|
|
|
|
Value ValueCalc::sqrt (const Value &a)
|
|
{
|
|
if (a.isError()) return a;
|
|
Value res = Value (::sqrt (converter->asFloat(a).asFloat()));
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (a.format());
|
|
// operation on date(s) should produce a number
|
|
if (isDate(a))
|
|
res.setFormat (Value::fmt_Number);
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::add (const Value &a, double b)
|
|
{
|
|
if (a.isError()) return a;
|
|
Value res = Value (converter->asFloat(a).asFloat() + b);
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (a.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::sub (const Value &a, double b)
|
|
{
|
|
if (a.isError()) return a;
|
|
Value res = Value (converter->asFloat(a).asFloat() - b);
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (a.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::mul (const Value &a, double b)
|
|
{
|
|
if (a.isError()) return a;
|
|
Value res = Value (converter->asFloat(a).asFloat() * b);
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (a.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::div (const Value &a, double b)
|
|
{
|
|
if (a.isError()) return a;
|
|
Value res;
|
|
if (b == 0.0)
|
|
return Value::errorDIV0();
|
|
|
|
res = Value (converter->asFloat(a).asFloat() / b);
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (a.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::pow (const Value &a, double b)
|
|
{
|
|
if (a.isError()) return a;
|
|
Value res = Value (::pow (converter->asFloat(a).asFloat(), b));
|
|
|
|
if (a.isNumber() || a.isEmpty())
|
|
res.setFormat (a.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::abs (const Value &a)
|
|
{
|
|
if (a.isError()) return a;
|
|
return Value (fabs (converter->asFloat (a).asFloat()));
|
|
}
|
|
|
|
bool ValueCalc::isZero (const Value &a)
|
|
{
|
|
if (a.isError()) return false;
|
|
return (converter->asFloat (a).asFloat() == 0.0);
|
|
}
|
|
|
|
bool ValueCalc::isEven (const Value &a)
|
|
{
|
|
if (a.isError()) return false;
|
|
return ((converter->asInteger (a).asInteger() % 2) == 0);
|
|
}
|
|
|
|
bool ValueCalc::equal (const Value &a, const Value &b)
|
|
{
|
|
return (converter->asFloat (a).asFloat() == converter->asFloat (b).asFloat());
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Helper function to avoid problems with rounding floating point
|
|
* values. Idea for this kind of solution taken from Openoffice.
|
|
*
|
|
*********************************************************************/
|
|
bool ValueCalc::approxEqual (const Value &a, const Value &b)
|
|
{
|
|
double aa = converter->asFloat (a).asFloat();
|
|
double bb = converter->asFloat (b).asFloat();
|
|
if (aa == bb)
|
|
return true;
|
|
double x = aa - bb;
|
|
return (x < 0.0 ? -x : x) < ((aa < 0.0 ? -aa : aa) * DBL_EPSILON);
|
|
}
|
|
|
|
bool ValueCalc::strEqual (const Value &a, const Value &b)
|
|
{
|
|
return (converter->asString (a).asString() == converter->asString (b).asString());
|
|
}
|
|
|
|
bool ValueCalc::greater (const Value &a, const Value &b)
|
|
{
|
|
double aa = converter->asFloat (a).asFloat();
|
|
double bb = converter->asFloat (b).asFloat();
|
|
return (aa > bb);
|
|
}
|
|
|
|
bool ValueCalc::gequal (const Value &a, const Value &b)
|
|
{
|
|
return (greater (a,b) || approxEqual (a,b));
|
|
}
|
|
|
|
bool ValueCalc::lower (const Value &a, const Value &b)
|
|
{
|
|
return greater (b, a);
|
|
}
|
|
|
|
Value ValueCalc::roundDown (const Value &a,
|
|
const Value &digits) {
|
|
return roundDown (a, converter->asInteger (digits).asInteger());
|
|
}
|
|
|
|
Value ValueCalc::roundUp (const Value &a,
|
|
const Value &digits) {
|
|
return roundUp (a, converter->asInteger (digits).asInteger());
|
|
}
|
|
|
|
Value ValueCalc::round (const Value &a,
|
|
const Value &digits) {
|
|
return round (a, converter->asInteger (digits).asInteger());
|
|
}
|
|
|
|
Value ValueCalc::roundDown (const Value &a, int digits)
|
|
{
|
|
// shift in one direction, round, shift back
|
|
Value val = a;
|
|
if (digits > 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = mul (val, 10);
|
|
if (digits < 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = div (val, 10);
|
|
|
|
val = Value (floor (converter->asFloat (val).asFloat()));
|
|
|
|
if (digits > 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = div (val, 10);
|
|
if (digits < 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = mul (val, 10);
|
|
return val;
|
|
}
|
|
|
|
Value ValueCalc::roundUp (const Value &a, int digits)
|
|
{
|
|
// shift in one direction, round, shift back
|
|
Value val = a;
|
|
if (digits > 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = mul (val, 10);
|
|
if (digits < 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = div (val, 10);
|
|
|
|
val = Value (ceil (converter->asFloat (val).asFloat()));
|
|
|
|
if (digits > 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = div (val, 10);
|
|
if (digits < 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = mul (val, 10);
|
|
return val;
|
|
}
|
|
|
|
Value ValueCalc::round (const Value &a, int digits)
|
|
{
|
|
// shift in one direction, round, shift back
|
|
Value val = a;
|
|
if (digits > 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = mul (val, 10);
|
|
if (digits < 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = div (val, 10);
|
|
|
|
val = Value (int(converter->asFloat (val).asFloat()+0.5));
|
|
|
|
if (digits > 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = div (val, 10);
|
|
if (digits < 0)
|
|
for (int i = 0; i < digits; ++i)
|
|
val = mul (val, 10);
|
|
return val;
|
|
}
|
|
|
|
int ValueCalc::sign (const Value &a)
|
|
{
|
|
double val = converter->asFloat (a).asFloat ();
|
|
if (val == 0) return 0;
|
|
if (val > 0) return 1;
|
|
return -1;
|
|
}
|
|
|
|
|
|
Value ValueCalc::log (const Value &number,
|
|
const Value &base)
|
|
{
|
|
double logbase = converter->asFloat (base).asFloat();
|
|
if (logbase == 1.0)
|
|
return Value::errorDIV0();
|
|
if (logbase <= 0.0)
|
|
return Value::errorNA();
|
|
|
|
logbase = log10 (logbase);
|
|
Value res = Value (log10 (converter->asFloat (number).asFloat()) / logbase);
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::ln (const Value &number)
|
|
{
|
|
Value res = Value (::log (converter->asFloat (number).asFloat()));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::log (const Value &number, double base)
|
|
{
|
|
if (base <= 0.0)
|
|
return Value::errorNA();
|
|
if (base == 1.0)
|
|
return Value::errorDIV0();
|
|
|
|
double num = converter->asFloat (number).asFloat();
|
|
Value res = Value (log10 (num) / log10 (base));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::exp (const Value &number)
|
|
{
|
|
return Value (::exp (converter->asFloat (number).asFloat()));
|
|
}
|
|
|
|
Value ValueCalc::pi ()
|
|
{
|
|
// retun PI in double-precision
|
|
// if arbitrary precision gets in, this should be extended to return
|
|
// more if need be
|
|
return Value (M_PI);
|
|
}
|
|
|
|
Value ValueCalc::eps ()
|
|
{
|
|
// #### This should adjust according to the actual number system used
|
|
// (float, double, long double, ...)
|
|
return Value (DBL_EPSILON);
|
|
}
|
|
|
|
Value ValueCalc::random (double range)
|
|
{
|
|
return Value (range * (double) rand() / (RAND_MAX + 1.0));
|
|
}
|
|
|
|
Value ValueCalc::random (Value range)
|
|
{
|
|
return random (converter->asFloat (range).asFloat());
|
|
}
|
|
|
|
Value ValueCalc::fact (const Value &which)
|
|
{
|
|
// we can simply use integers - no one is going to compute factorial of
|
|
// anything bigger than 2^32
|
|
return fact (converter->asInteger (which).asInteger());
|
|
}
|
|
|
|
Value ValueCalc::fact (const Value &which,
|
|
const Value &end)
|
|
{
|
|
// we can simply use integers - no one is going to compute factorial of
|
|
// anything bigger than 2^32
|
|
return fact (converter->asInteger (which).asInteger(),
|
|
converter->asInteger (end).asInteger ());
|
|
}
|
|
|
|
Value ValueCalc::fact (int which, int end) {
|
|
if (which < 0)
|
|
return Value (-1);
|
|
if (which == 0)
|
|
return Value (1);
|
|
// no multiplication if val==end
|
|
if (which == end)
|
|
return Value (1);
|
|
|
|
return (mul (fact (which-1, end), which));
|
|
}
|
|
|
|
Value ValueCalc::factDouble (int which)
|
|
{
|
|
if (which < 0)
|
|
return Value (-1);
|
|
if ((which == 0) || (which == 1))
|
|
return Value (1);
|
|
|
|
return (mul (factDouble (which-2), which));
|
|
}
|
|
|
|
Value ValueCalc::factDouble (Value which)
|
|
{
|
|
return factDouble (converter->asInteger (which).asInteger());
|
|
}
|
|
|
|
Value ValueCalc::combin (int n, int k)
|
|
{
|
|
if (n >= 15)
|
|
{
|
|
double result = ::exp(lgamma (n + 1) - lgamma (k + 1) - lgamma (n-k+1));
|
|
return Value (floor(result + 0.5));
|
|
}
|
|
else
|
|
return div (div (fact (n), fact (k)), fact (n - k));
|
|
}
|
|
|
|
Value ValueCalc::combin (Value n, Value k)
|
|
{
|
|
int nn = converter->asInteger (n).asInteger();
|
|
int kk = converter->asInteger (k).asInteger();
|
|
return combin (nn, kk);
|
|
}
|
|
|
|
Value ValueCalc::gcd (const Value &a, const Value &b)
|
|
{
|
|
// Euler's GCD algorithm
|
|
Value aa = round (a);
|
|
Value bb = round (b);
|
|
|
|
if (approxEqual (aa, bb)) return aa;
|
|
|
|
if (aa.isZero()) return bb;
|
|
if (bb.isZero()) return aa;
|
|
|
|
|
|
if (greater (aa, bb))
|
|
return gcd (bb, mod (aa, bb));
|
|
else
|
|
return gcd (aa, mod (bb, aa));
|
|
}
|
|
|
|
Value ValueCalc::lcm (const Value &a, const Value &b)
|
|
{
|
|
Value aa = round (a);
|
|
Value bb = round (b);
|
|
|
|
if (approxEqual (aa, bb)) return aa;
|
|
|
|
if (aa.isZero()) return bb;
|
|
if (bb.isZero()) return aa;
|
|
|
|
Value g = gcd (aa, bb);
|
|
if (g.isZero()) // GCD is zero for some weird reason
|
|
return mul (aa, bb);
|
|
|
|
return div (mul (aa, bb), g);
|
|
}
|
|
|
|
Value ValueCalc::base (const Value &val, int base, int prec)
|
|
{
|
|
if (base == 10) return round (val, prec);
|
|
if (prec < 0) prec = 2;
|
|
if ((base < 2) || (base > 36))
|
|
return Value::errorVALUE();
|
|
|
|
double value = converter->asFloat (val).asFloat();
|
|
QString result = QString::number ((int)value, base);
|
|
|
|
if (prec > 0)
|
|
{
|
|
result += "."; value = value - (int)value;
|
|
|
|
int ix;
|
|
for( int i = 0; i < prec; i++ )
|
|
{
|
|
ix = (int) value * base;
|
|
result += "0123456789abcdefghijklmnopqrstuvwxyz"[ix];
|
|
value = base * (value - (double)ix/base);
|
|
}
|
|
}
|
|
|
|
return Value (result.upper());
|
|
}
|
|
|
|
Value ValueCalc::fromBase (const Value &val, int base)
|
|
{
|
|
QString str = converter->asString (val).asString();
|
|
bool ok;
|
|
double num = str.toLong (&ok, base);
|
|
if (ok)
|
|
return Value (num);
|
|
return Value::errorVALUE();
|
|
}
|
|
|
|
|
|
Value ValueCalc::sin (const Value &number)
|
|
{
|
|
Value res = Value (::sin (converter->asFloat (number).asFloat()));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::cos (const Value &number)
|
|
{
|
|
Value res = Value (::cos (converter->asFloat (number).asFloat()));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::tg (const Value &number)
|
|
{
|
|
Value res = Value (::tan (converter->asFloat (number).asFloat()));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::cotg (const Value &number)
|
|
{
|
|
Value res = Value (div (1, ::tan (converter->asFloat (number).asFloat())));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::asin (const Value &number)
|
|
{
|
|
errno = 0;
|
|
Value res = Value (::asin (converter->asFloat (number).asFloat()));
|
|
if (errno)
|
|
return Value::errorVALUE();
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::acos (const Value &number)
|
|
{
|
|
errno = 0;
|
|
Value res = Value (::acos (converter->asFloat (number).asFloat()));
|
|
if (errno)
|
|
return Value::errorVALUE();
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::atg (const Value &number)
|
|
{
|
|
errno = 0;
|
|
Value res = Value (::atan (converter->asFloat (number).asFloat()));
|
|
if (errno)
|
|
return Value::errorVALUE();
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::atan2 (const Value &y, const Value &x)
|
|
{
|
|
double yy = converter->asFloat (y).asFloat();
|
|
double xx = converter->asFloat (x).asFloat();
|
|
return Value (::atan2 (yy, xx));
|
|
}
|
|
|
|
Value ValueCalc::sinh (const Value &number)
|
|
{
|
|
Value res = Value (::sinh (converter->asFloat (number).asFloat()));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::cosh (const Value &number)
|
|
{
|
|
Value res = Value (::cosh (converter->asFloat (number).asFloat()));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::tgh (const Value &number)
|
|
{
|
|
Value res = Value (::tanh (converter->asFloat (number).asFloat()));
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::asinh (const Value &number)
|
|
{
|
|
errno = 0;
|
|
Value res = Value (::asinh (converter->asFloat (number).asFloat()));
|
|
if (errno)
|
|
return Value::errorVALUE();
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::acosh (const Value &number)
|
|
{
|
|
errno = 0;
|
|
Value res = Value (::acosh (converter->asFloat (number).asFloat()));
|
|
if (errno)
|
|
return Value::errorVALUE();
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::atgh (const Value &number)
|
|
{
|
|
errno = 0;
|
|
Value res = Value (::atanh (converter->asFloat (number).asFloat()));
|
|
if (errno)
|
|
return Value::errorVALUE();
|
|
|
|
if (number.isNumber() || number.isEmpty())
|
|
res.setFormat (number.format());
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::phi (Value x)
|
|
{
|
|
Value constant (0.39894228040143268);
|
|
|
|
// constant * exp(-(x * x) / 2.0);
|
|
Value x2neg = mul (sqr (x), -1);
|
|
return mul (constant, exp (div (x2neg, 2.0)));
|
|
}
|
|
|
|
static double taylor_helper (double* pPolynom, uint nMax, double x)
|
|
{
|
|
double nVal = pPolynom[nMax];
|
|
for (int i = nMax-1; i >= 0; i--) {
|
|
nVal = pPolynom[i] + (nVal * x);
|
|
}
|
|
return nVal;
|
|
}
|
|
|
|
Value ValueCalc::gauss (Value xx)
|
|
// this is a weird function
|
|
{
|
|
double x = converter->asFloat (xx).asFloat();
|
|
|
|
double t0[] =
|
|
{ 0.39894228040143268, -0.06649038006690545, 0.00997355701003582,
|
|
-0.00118732821548045, 0.00011543468761616, -0.00000944465625950,
|
|
0.00000066596935163, -0.00000004122667415, 0.00000000227352982,
|
|
0.00000000011301172, 0.00000000000511243, -0.00000000000021218 };
|
|
double t2[] =
|
|
{ 0.47724986805182079, 0.05399096651318805, -0.05399096651318805,
|
|
0.02699548325659403, -0.00449924720943234, -0.00224962360471617,
|
|
0.00134977416282970, -0.00011783742691370, -0.00011515930357476,
|
|
0.00003704737285544, 0.00000282690796889, -0.00000354513195524,
|
|
0.00000037669563126, 0.00000019202407921, -0.00000005226908590,
|
|
-0.00000000491799345, 0.00000000366377919, -0.00000000015981997,
|
|
-0.00000000017381238, 0.00000000002624031, 0.00000000000560919,
|
|
-0.00000000000172127, -0.00000000000008634, 0.00000000000007894 };
|
|
double t4[] =
|
|
{ 0.49996832875816688, 0.00013383022576489, -0.00026766045152977,
|
|
0.00033457556441221, -0.00028996548915725, 0.00018178605666397,
|
|
-0.00008252863922168, 0.00002551802519049, -0.00000391665839292,
|
|
-0.00000074018205222, 0.00000064422023359, -0.00000017370155340,
|
|
0.00000000909595465, 0.00000000944943118, -0.00000000329957075,
|
|
0.00000000029492075, 0.00000000011874477, -0.00000000004420396,
|
|
0.00000000000361422, 0.00000000000143638, -0.00000000000045848 };
|
|
double asympt[] = { -1.0, 1.0, -3.0, 15.0, -105.0 };
|
|
|
|
double xAbs = fabs(x);
|
|
uint xShort = static_cast<uint>(floor(xAbs));
|
|
double nVal = 0.0;
|
|
if (xShort == 0)
|
|
nVal = taylor_helper(t0, 11, (xAbs * xAbs)) * xAbs;
|
|
else if ((xShort >= 1) && (xShort <= 2))
|
|
nVal = taylor_helper(t2, 23, (xAbs - 2.0));
|
|
else if ((xShort >= 3) && (xShort <= 4))
|
|
nVal = taylor_helper(t4, 20, (xAbs - 4.0));
|
|
else
|
|
{
|
|
double phiAbs = converter->asFloat (phi (xAbs)).asFloat();
|
|
nVal = 0.5 + phiAbs * taylor_helper(asympt, 4, 1.0 / (xAbs * xAbs)) / xAbs;
|
|
}
|
|
|
|
if (x < 0.0)
|
|
return Value (-nVal);
|
|
else
|
|
return Value (nVal);
|
|
}
|
|
|
|
Value ValueCalc::gaussinv (Value xx)
|
|
// this is a weird function
|
|
{
|
|
double x = converter->asFloat (xx).asFloat();
|
|
|
|
double q,t,z;
|
|
|
|
q=x-0.5;
|
|
|
|
if(fabs(q)<=.425)
|
|
{
|
|
t=0.180625-q*q;
|
|
|
|
z=
|
|
q*
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
t*2509.0809287301226727+33430.575583588128105
|
|
)
|
|
*t+67265.770927008700853
|
|
)
|
|
*t+45921.953931549871457
|
|
)
|
|
*t+13731.693765509461125
|
|
)
|
|
*t+1971.5909503065514427
|
|
)
|
|
*t+133.14166789178437745
|
|
)
|
|
*t+3.387132872796366608
|
|
)
|
|
/
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
t*5226.495278852854561+28729.085735721942674
|
|
)
|
|
*t+39307.89580009271061
|
|
)
|
|
*t+21213.794301586595867
|
|
)
|
|
*t+5394.1960214247511077
|
|
)
|
|
*t+687.1870074920579083
|
|
)
|
|
*t+42.313330701600911252
|
|
)
|
|
*t+1.0
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
if(q>0) t=1-x;
|
|
else t=x;
|
|
|
|
t=::sqrt(-::log(t));
|
|
|
|
if(t<=5.0)
|
|
{
|
|
t+=-1.6;
|
|
|
|
z=
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
t*7.7454501427834140764e-4+0.0227238449892691845833
|
|
)
|
|
*t+0.24178072517745061177
|
|
)
|
|
*t+1.27045825245236838258
|
|
)
|
|
*t+3.64784832476320460504
|
|
)
|
|
*t+5.7694972214606914055
|
|
)
|
|
*t+4.6303378461565452959
|
|
)
|
|
*t+1.42343711074968357734
|
|
)
|
|
/
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
t*1.05075007164441684324e-9+5.475938084995344946e-4
|
|
)
|
|
*t+0.0151986665636164571966
|
|
)
|
|
*t+0.14810397642748007459
|
|
)
|
|
*t+0.68976733498510000455
|
|
)
|
|
*t+1.6763848301838038494
|
|
)
|
|
*t+2.05319162663775882187
|
|
)
|
|
*t+1.0
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
t+=-5.0;
|
|
|
|
z=
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
t*2.01033439929228813265e-7+2.71155556874348757815e-5
|
|
)
|
|
*t+0.0012426609473880784386
|
|
)
|
|
*t+0.026532189526576123093
|
|
)
|
|
*t+0.29656057182850489123
|
|
)
|
|
*t+1.7848265399172913358
|
|
)
|
|
*t+5.4637849111641143699
|
|
)
|
|
*t+6.6579046435011037772
|
|
)
|
|
/
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
(
|
|
t*2.04426310338993978564e-15+1.4215117583164458887e-7
|
|
)
|
|
*t+1.8463183175100546818e-5
|
|
)
|
|
*t+7.868691311456132591e-4
|
|
)
|
|
*t+0.0148753612908506148525
|
|
)
|
|
*t+0.13692988092273580531
|
|
)
|
|
*t+0.59983220655588793769
|
|
)
|
|
*t+1.0
|
|
);
|
|
|
|
}
|
|
|
|
if(q<0.0) z=-z;
|
|
}
|
|
|
|
return Value (z);
|
|
}
|
|
|
|
//helper for GetGamma and GetLogGamma
|
|
static double GammaHelp(double& x, bool& bReflect)
|
|
{
|
|
double c[6] = {76.18009173, -86.50532033, 24.01409822,
|
|
-1.231739516, 0.120858003E-2, -0.536382E-5};
|
|
if (x >= 1.0)
|
|
{
|
|
bReflect = false;
|
|
x -= 1.0;
|
|
}
|
|
else
|
|
{
|
|
bReflect = true;
|
|
x = 1.0 - x;
|
|
}
|
|
double s, anum;
|
|
s = 1.0;
|
|
anum = x;
|
|
for (uint i = 0; i < 6; i++)
|
|
{
|
|
anum += 1.0;
|
|
s += c[i]/anum;
|
|
}
|
|
s *= 2.506628275; // sqrt(2*PI)
|
|
return s;
|
|
}
|
|
|
|
Value ValueCalc::GetGamma (Value _x)
|
|
{
|
|
double x = converter->asFloat (_x).asFloat();
|
|
|
|
bool bReflect;
|
|
double G = GammaHelp(x, bReflect);
|
|
G = ::pow(x+5.5,x+0.5)*G/::exp(x+5.5);
|
|
if (bReflect)
|
|
G = M_PI*x/(G*::sin(M_PI*x));
|
|
return Value (G);
|
|
}
|
|
|
|
Value ValueCalc::GetLogGamma (Value _x)
|
|
{
|
|
double x = converter->asFloat (_x).asFloat();
|
|
|
|
bool bReflect;
|
|
double G = GammaHelp(x, bReflect);
|
|
G = (x+0.5)*::log(x+5.5)+::log(G)-(x+5.5);
|
|
if (bReflect)
|
|
G = ::log(M_PI*x)-G-::log(::sin(M_PI*x));
|
|
return Value (G);
|
|
}
|
|
|
|
Value ValueCalc::GetGammaDist (Value _x, Value _alpha,
|
|
Value _beta)
|
|
{
|
|
double x = converter->asFloat (_x).asFloat();
|
|
double alpha = converter->asFloat (_alpha).asFloat();
|
|
double beta = converter->asFloat (_beta).asFloat();
|
|
|
|
if (x == 0.0)
|
|
return Value (0.0);
|
|
|
|
x /= beta;
|
|
double gamma = alpha;
|
|
|
|
double c = 0.918938533204672741;
|
|
double d[10] = {
|
|
0.833333333333333333E-1,
|
|
-0.277777777777777778E-2,
|
|
0.793650793650793651E-3,
|
|
-0.595238095238095238E-3,
|
|
0.841750841750841751E-3,
|
|
-0.191752691752691753E-2,
|
|
0.641025641025641025E-2,
|
|
-0.295506535947712418E-1,
|
|
0.179644372368830573,
|
|
-0.139243221690590111E1
|
|
};
|
|
|
|
double dx = x;
|
|
double dgamma = gamma;
|
|
int maxit = 10000;
|
|
|
|
double z = dgamma;
|
|
double den = 1.0;
|
|
while ( z < 10.0 ) {
|
|
den *= z;
|
|
z += 1.0;
|
|
}
|
|
|
|
double z2 = z*z;
|
|
double z3 = z*z2;
|
|
double z4 = z2*z2;
|
|
double z5 = z2*z3;
|
|
double a = ( z - 0.5 ) * ::log(z) - z + c;
|
|
double b = d[0]/z + d[1]/z3 + d[2]/z5 + d[3]/(z2*z5) + d[4]/(z4*z5) +
|
|
d[5]/(z*z5*z5) + d[6]/(z3*z5*z5) + d[7]/(z5*z5*z5) + d[8]/(z2*z5*z5*z5);
|
|
// double g = exp(a+b) / den;
|
|
|
|
double sum = 1.0 / dgamma;
|
|
double term = 1.0 / dgamma;
|
|
double cut1 = dx - dgamma;
|
|
double cut2 = dx * 10000000000.0;
|
|
|
|
for ( int i=1; i<=maxit; i++ ) {
|
|
double ai = i;
|
|
term = dx * term / ( dgamma + ai );
|
|
sum += term;
|
|
double cutoff = cut1 + ( cut2 * term / sum );
|
|
if ( ai > cutoff ) {
|
|
double t = sum;
|
|
// return pow( dx, dgamma ) * exp( -dx ) * t / g;
|
|
return Value (::exp( dgamma * ::log(dx) - dx - a - b ) * t * den);
|
|
}
|
|
}
|
|
|
|
return Value (1.0); // should not happen ...
|
|
}
|
|
|
|
Value ValueCalc::GetBeta (Value _x, Value _alpha,
|
|
Value _beta)
|
|
{
|
|
if (equal (_beta, 1.0))
|
|
return pow (_x, _alpha);
|
|
else if (equal (_alpha, 1.0))
|
|
// 1.0 - pow (1.0-_x, _beta)
|
|
return sub (1.0, pow (sub (1.0, _x), _beta));
|
|
|
|
double x = converter->asFloat (_x).asFloat();
|
|
double alpha = converter->asFloat (_alpha).asFloat();
|
|
double beta = converter->asFloat (_beta).asFloat();
|
|
|
|
double fEps = 1.0E-8;
|
|
bool bReflect;
|
|
double cf, fA, fB;
|
|
|
|
if (x < (alpha+1.0)/(alpha+beta+1.0)) {
|
|
bReflect = false;
|
|
fA = alpha;
|
|
fB = beta;
|
|
}
|
|
else {
|
|
bReflect = true;
|
|
fA = beta;
|
|
fB = alpha;
|
|
x = 1.0 - x;
|
|
}
|
|
if (x < fEps)
|
|
cf = 0.0;
|
|
else {
|
|
double a1, b1, a2, b2, fnorm, rm, apl2m, d2m, d2m1, cfnew;
|
|
a1 = 1.0; b1 = 1.0;
|
|
b2 = 1.0 - (fA+fB)*x/(fA+1.0);
|
|
if (b2 == 0.0) {
|
|
a2 = b2;
|
|
fnorm = 1.0;
|
|
cf = 1.0;
|
|
}
|
|
else {
|
|
a2 = 1.0;
|
|
fnorm = 1.0/b2;
|
|
cf = a2*fnorm;
|
|
}
|
|
cfnew = 1.0;
|
|
for (uint j = 1; j <= 100; j++) {
|
|
rm = (double) j;
|
|
apl2m = fA + 2.0*rm;
|
|
d2m = rm*(fB-rm)*x/((apl2m-1.0)*apl2m);
|
|
d2m1 = -(fA+rm)*(fA+fB+rm)*x/(apl2m*(apl2m+1.0));
|
|
a1 = (a2+d2m*a1)*fnorm;
|
|
b1 = (b2+d2m*b1)*fnorm;
|
|
a2 = a1 + d2m1*a2*fnorm;
|
|
b2 = b1 + d2m1*b2*fnorm;
|
|
if (b2 != 0.0) {
|
|
fnorm = 1.0/b2;
|
|
cfnew = a2*fnorm;
|
|
if (fabs(cf-cfnew)/cf < fEps)
|
|
j = 101;
|
|
else
|
|
cf = cfnew;
|
|
}
|
|
}
|
|
if (fB < fEps)
|
|
b1 = 1.0E30;
|
|
else
|
|
b1 = ::exp(GetLogGamma(fA).asFloat()+GetLogGamma(fB).asFloat()-
|
|
GetLogGamma(fA+fB).asFloat());
|
|
|
|
cf *= ::pow(x, fA)*::pow(1.0-x,fB)/(fA*b1);
|
|
}
|
|
if (bReflect)
|
|
return Value (1.0-cf);
|
|
else
|
|
return Value (cf);
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
|
|
|
|
/*
|
|
*
|
|
* The code for calculating Bessel functions is taken
|
|
* from CCMATH, a mathematics library source.code.
|
|
*
|
|
* Original copyright follows:
|
|
*
|
|
* Copyright (C) 2000 Daniel A. Atkinson All rights reserved.
|
|
* This code may be redistributed under the terms of the GNU library
|
|
* public license (LGPL).
|
|
*/
|
|
|
|
static double ccmath_gaml(double x)
|
|
{ double g,h;
|
|
for(g=1.; x<30. ;g*=x,x+=1.); h=x*x;
|
|
g=(x-.5)*log(x)-x+.918938533204672-log(g);
|
|
g+=(1.-(1./6.-(1./3.-1./(4.*h))/(7.*h))/(5.*h))/(12.*x);
|
|
return g;
|
|
}
|
|
|
|
static double ccmath_psi(int m)
|
|
{ double s= -.577215664901533; int k;
|
|
for(k=1; k<m ;++k) s+=1./k;
|
|
return s;
|
|
}
|
|
|
|
static double ccmath_ibes(double v,double x)
|
|
{ double y,s,t,tp; int p,m;
|
|
y=x-9.; if(y>0.) y*=y; tp=v*v*.2+25.;
|
|
if(y<tp){ x/=2.; m=(int)x;
|
|
if(x>0.) s=t=exp(v*log(x)-ccmath_gaml(v+1.));
|
|
else{ if(v>0.) return 0.; else if(v==0.) return 1.;}
|
|
for(p=1,x*=x;;++p){ t*=x/(p*(v+=1.)); s+=t;
|
|
if(p>m && t<1.e-13*s) break;
|
|
}
|
|
}
|
|
else{ double u,a0=1.57079632679490;
|
|
s=t=1./sqrt(x*a0); x*=2.; u=0.;
|
|
for(p=1,y=.5; (tp=fabs(t))>1.e-14 ;++p,y+=1.){
|
|
t*=(v+y)*(v-y)/(p*x); if(y>v && fabs(t)>=tp) break;
|
|
if(!(p&1)) s+=t; else u-=t;
|
|
}
|
|
x/=2.; s=cosh(x)*s+sinh(x)*u;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static double ccmath_kbes(double v,double x)
|
|
{ double y,s,t,tp,f,a0=1.57079632679490;
|
|
int p,k,m;
|
|
if(x==0.) return HUGE_VAL;
|
|
y=x-10.5; if(y>0.) y*=y; tp=25.+.185*v*v;
|
|
if(y<tp && modf(v+.5,&t)!=0.){ y=1.5+.5*v;
|
|
if(x<y){ x/=2.; m=(int)x; tp=t=exp(v*log(x)-ccmath_gaml(v+1.));
|
|
if(modf(v,&y)==0.){ k=(int)y; tp*=v;
|
|
f=2.*log(x)-ccmath_psi(1)-ccmath_psi(k+1);
|
|
t/=2.; if(!(k&1)) t= -t; s=f*t;
|
|
for(p=1,x*=x;;++p){ f-=1./p+1./(v+=1.);
|
|
t*=x/(p*v); s+=(y=t*f);
|
|
if(p>m && fabs(y)<1.e-14) break; }
|
|
if(k>0){ x= -x; s+=(t=1./(tp*2.));
|
|
for(p=1,--k; k>0 ;++p,--k) s+=(t*=x/(p*k)); }
|
|
}
|
|
else{ f=1./(t*v*2.); t*=a0/sin(2.*a0*v); s=f-t;
|
|
for(p=1,x*=x,tp=v;;++p){
|
|
t*=x/(p*(v+=1.)); f*= -x/(p*(tp-=1.));
|
|
s+=(y=f-t); if(p>m && fabs(y)<1.e-14) break; }
|
|
}
|
|
}
|
|
else{ double tq,h,w,z,r;
|
|
t=12./pow(x,.333); k=(int)(t*t); y=2.*(x+k);
|
|
m=(int)v; v-=m; tp=v*v-.25; v+=1.; tq=v*v-.25;
|
|
for(s=h=1.,r=f=z=w=0.; k>0 ;--k,y-=2.){
|
|
t=(y*h-(k+1)*z)/(k-1-tp/k); z=h; f+=(h=t);
|
|
t=(y*s-(k+1)*w)/(k-1-tq/k); w=s; r+=(s=t); }
|
|
t=sqrt(a0/x)*exp(-x); s*=t/r; h*=t/f; x/=2.; if(m==0) s=h;
|
|
for(k=1; k<m ;++k){ t=v*s/x+h; h=s; s=t; v+=1.;}
|
|
}
|
|
}
|
|
else{ s=t=sqrt(a0/x); x*=2.;
|
|
for(p=1,y=.5; (tp=fabs(t))>1.e-14 ;++p,y+=1.){
|
|
t*=(v+y)*(v-y)/(p*x); if(y>v && fabs(t)>=tp) break; s+=t; }
|
|
s*=exp(-x/2.);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static double ccmath_jbes(double v,double x)
|
|
{ double y,s,t,tp; int p,m;
|
|
y=x-8.5; if(y>0.) y*=y; tp=v*v/4.+13.69;
|
|
if(y<tp){ x/=2.; m=(int)x;
|
|
if(x>0.) s=t=exp(v*log(x)-ccmath_gaml(v+1.));
|
|
else{ if(v>0.) return 0.; else if(v==0.) return 1.;}
|
|
for(p=1,x*= -x;;++p){ t*=x/(p*(v+=1.)); s+=t;
|
|
if(p>m && fabs(t)<1.e-13) break;
|
|
}
|
|
}
|
|
else{ double u,a0=1.57079632679490;
|
|
s=t=1./sqrt(x*a0); x*=2.; u=0.;
|
|
for(p=1,y=.5; (tp=fabs(t))>1.e-14 ;++p,y+=1.){
|
|
t*=(v+y)*(v-y)/(p*x); if(y>v && fabs(t)>=tp) break;
|
|
if(!(p&1)){ t= -t; s+=t;} else u-=t;
|
|
}
|
|
y=x/2.-(v+.5)*a0; s=cos(y)*s+sin(y)*u;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static double ccmath_nbes(double v,double x)
|
|
{ double y,s,t,tp,u,f,a0=3.14159265358979;
|
|
int p,k,m;
|
|
y=x-8.5; if(y>0.) y*=y; tp=v*v/4.+13.69;
|
|
if(y<tp){ if(x==0.) return HUGE_VAL;
|
|
x/=2.; m=(int)x; u=t=exp(v*log(x)-ccmath_gaml(v+1.));
|
|
if(modf(v,&y)==0.){ k=(int)y; u*=v;
|
|
f=2.*log(x)-ccmath_psi(1)-ccmath_psi(k+1);
|
|
t/=a0; x*= -x; s=f*t;
|
|
for(p=1;;++p){ f-=1./p+1./(v+=1.);
|
|
t*=x/(p*v); s+=(y=t*f); if(p>m && fabs(y)<1.e-13) break; }
|
|
if(k>0){ x= -x; s-=(t=1./(u*a0));
|
|
for(p=1,--k; k>0 ;++p,--k) s-=(t*=x/(p*k)); }
|
|
}
|
|
else{ f=1./(t*v*a0); t/=tan(a0*v); s=t-f;
|
|
for(p=1,x*=x,u=v;;++p){
|
|
t*= -x/(p*(v+=1.)); f*=x/(p*(u-=1.));
|
|
s+=(y=t-f); if(p>m && fabs(y)<1.e-13) break; }
|
|
}
|
|
}
|
|
else{ x*=2.; s=t=2./sqrt(x*a0); u=0.;
|
|
for(p=1,y=.5; (tp=fabs(t))>1.e-14 ;++p,y+=1.){
|
|
t*=(v+y)*(v-y)/(p*x); if(y>v && fabs(t)>tp) break;
|
|
if(!(p&1)){ t= -t; s+=t;} else u+=t;
|
|
}
|
|
y=(x-(v+.5)*a0)/2.; s=sin(y)*s+cos(y)*u;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
/* ---------- end of CCMATH code ---------- */
|
|
|
|
|
|
Value ValueCalc::besseli (Value v, Value x)
|
|
{
|
|
double vv = converter->asFloat (v).asFloat ();
|
|
double xx = converter->asFloat (x).asFloat ();
|
|
return Value (ccmath_ibes (vv, xx));
|
|
}
|
|
|
|
Value ValueCalc::besselj (Value v, Value x)
|
|
{
|
|
double vv = converter->asFloat (v).asFloat ();
|
|
double xx = converter->asFloat (x).asFloat ();
|
|
return Value (ccmath_jbes (vv, xx));
|
|
}
|
|
|
|
Value ValueCalc::besselk (Value v, Value x)
|
|
{
|
|
double vv = converter->asFloat (v).asFloat ();
|
|
double xx = converter->asFloat (x).asFloat ();
|
|
return Value (ccmath_kbes (vv, xx));
|
|
}
|
|
|
|
Value ValueCalc::besseln (Value v, Value x)
|
|
{
|
|
double vv = converter->asFloat (v).asFloat ();
|
|
double xx = converter->asFloat (x).asFloat ();
|
|
return Value (ccmath_nbes (vv, xx));
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
|
|
Value ValueCalc::erf (Value x)
|
|
{
|
|
return Value (::erf (converter->asFloat (x).asFloat()));
|
|
}
|
|
|
|
Value ValueCalc::erfc (Value x)
|
|
{
|
|
return Value (::erfc (converter->asFloat (x).asFloat()));
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
|
|
void ValueCalc::arrayWalk (const Value &range,
|
|
Value &res, arrayWalkFunc func, Value param)
|
|
{
|
|
if (res.isError()) return;
|
|
if (!range.isArray ())
|
|
{
|
|
func (this, res, range, param);
|
|
return;
|
|
}
|
|
|
|
int rows = range.rows ();
|
|
int cols = range.columns ();
|
|
for (int r = 0; r < rows; r++)
|
|
for (int c = 0; c < cols; c++)
|
|
{
|
|
Value v = range.element (c, r);
|
|
if (v.isArray())
|
|
arrayWalk (v, res, func, param);
|
|
else {
|
|
func (this, res, v, param);
|
|
if (res.format() == Value::fmt_None)
|
|
res.setFormat (v.format());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ValueCalc::arrayWalk (QValueVector<Value> &range,
|
|
Value &res, arrayWalkFunc func, Value param)
|
|
{
|
|
if (res.isError()) return;
|
|
for (unsigned int i = 0; i < range.count(); ++i)
|
|
arrayWalk (range[i], res, func, param);
|
|
}
|
|
|
|
void ValueCalc::twoArrayWalk (const Value &a1, const Value &a2,
|
|
Value &res, arrayWalkFunc func)
|
|
{
|
|
if (res.isError()) return;
|
|
if (!a1.isArray ())
|
|
{
|
|
func (this, res, a1, a2);
|
|
return;
|
|
}
|
|
|
|
int rows = a1.rows ();
|
|
int cols = a1.columns ();
|
|
int rows2 = a2.rows ();
|
|
int cols2 = a2.columns ();
|
|
if ((rows != rows2) || (cols != cols2)) {
|
|
res = Value::errorVALUE();
|
|
return;
|
|
}
|
|
for (int r = 0; r < rows; r++)
|
|
for (int c = 0; c < cols; c++)
|
|
{
|
|
Value v1 = a1.element (c, r);
|
|
Value v2 = a2.element (c, r);
|
|
if (v1.isArray() && v2.isArray())
|
|
twoArrayWalk (v1, v2, res, func);
|
|
else {
|
|
func (this, res, v1, v2);
|
|
if (res.format() == Value::fmt_None)
|
|
res.setFormat (format (v1.format(), v2.format()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ValueCalc::twoArrayWalk (QValueVector<Value> &a1,
|
|
QValueVector<Value> &a2, Value &res, arrayWalkFunc func)
|
|
{
|
|
if (res.isError()) return;
|
|
if (a1.count() != a2.count()) {
|
|
res = Value::errorVALUE();
|
|
return;
|
|
}
|
|
for (unsigned int i = 0; i < a1.count(); ++i)
|
|
twoArrayWalk (a1[i], a2[i], res, func);
|
|
}
|
|
|
|
arrayWalkFunc ValueCalc::awFunc (const QString &name)
|
|
{
|
|
if (awFuncs.count(name))
|
|
return awFuncs[name];
|
|
return 0;
|
|
}
|
|
|
|
void ValueCalc::registerAwFunc (const QString &name, arrayWalkFunc func)
|
|
{
|
|
awFuncs[name] = func;
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
|
|
Value ValueCalc::sum (const Value &range, bool full)
|
|
{
|
|
Value res;
|
|
arrayWalk (range, res, awFunc (full ? "suma" : "sum"), 0);
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::sum (QValueVector<Value> range, bool full)
|
|
{
|
|
Value res;
|
|
arrayWalk (range, res, awFunc (full ? "suma" : "sum"), 0);
|
|
return res;
|
|
}
|
|
|
|
// sum of squares
|
|
Value ValueCalc::sumsq (const Value &range, bool full)
|
|
{
|
|
Value res;
|
|
arrayWalk (range, res, awFunc (full ? "sumsqa" : "sumsq"), 0);
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::sumIf (const Value &range,
|
|
const Value &checkRange, const Condition &cond)
|
|
{
|
|
if (!range.isArray())
|
|
{
|
|
if (matches (cond, checkRange.element (0, 0)))
|
|
return converter->asNumeric (range);
|
|
return Value (0.0);
|
|
}
|
|
|
|
//if we are here, we have an array
|
|
Value res;
|
|
|
|
unsigned int rows = range.rows ();
|
|
unsigned int cols = range.columns ();
|
|
for (unsigned int r = 0; r < rows; r++)
|
|
for (unsigned int c = 0; c < cols; c++)
|
|
{
|
|
Value v = range.element (c, r);
|
|
Value newcheck = v;
|
|
if ((c < checkRange.columns()) && (r < checkRange.rows()))
|
|
newcheck = checkRange.element (c, r);
|
|
|
|
if (v.isArray())
|
|
res = add (res, sumIf (v, newcheck, cond));
|
|
else
|
|
if (matches (cond, newcheck))
|
|
res = add (res, v);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int ValueCalc::count (const Value &range, bool full)
|
|
{
|
|
Value res = 0;
|
|
arrayWalk (range, res, awFunc (full ? "counta" : "count"), 0);
|
|
return converter->asInteger (res).asInteger ();
|
|
}
|
|
|
|
int ValueCalc::count (QValueVector<Value> range, bool full)
|
|
{
|
|
Value res = 0;
|
|
arrayWalk (range, res, awFunc (full ? "counta" : "count"), 0);
|
|
return converter->asInteger (res).asInteger ();
|
|
}
|
|
|
|
int ValueCalc::countIf (const Value &range, const Condition &cond)
|
|
{
|
|
if (!range.isArray())
|
|
{
|
|
if (matches (cond, range))
|
|
return range.isEmpty() ? 0 : 1;
|
|
return 0;
|
|
}
|
|
|
|
int res = 0;
|
|
|
|
int cols = range.columns ();
|
|
int rows = range.rows ();
|
|
for (int r = 0; r < rows; r++)
|
|
for (int c = 0; c < cols; c++)
|
|
{
|
|
Value v = range.element (c, r);
|
|
|
|
if (v.isArray())
|
|
res += countIf (v, cond);
|
|
else
|
|
if (matches (cond, v))
|
|
res++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::avg (const Value &range, bool full)
|
|
{
|
|
int cnt = count (range, full);
|
|
if (cnt)
|
|
return div (sum (range, full), cnt);
|
|
return Value (0.0);
|
|
}
|
|
|
|
Value ValueCalc::avg (QValueVector<Value> range, bool full)
|
|
{
|
|
int cnt = count (range, full);
|
|
if (cnt)
|
|
return div (sum (range, full), cnt);
|
|
return Value (0.0);
|
|
}
|
|
|
|
Value ValueCalc::max (const Value &range, bool full)
|
|
{
|
|
Value res;
|
|
arrayWalk (range, res, awFunc (full ? "maxa" : "max"), 0);
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::max (QValueVector<Value> range, bool full)
|
|
{
|
|
Value res;
|
|
arrayWalk (range, res, awFunc (full ? "maxa" : "max"), 0);
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::min (const Value &range, bool full)
|
|
{
|
|
Value res;
|
|
arrayWalk (range, res, awFunc (full ? "mina" : "min"), 0);
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::min (QValueVector<Value> range, bool full)
|
|
{
|
|
Value res;
|
|
arrayWalk (range, res, awFunc (full ? "mina" : "min"), 0);
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::product (const Value &range, Value init,
|
|
bool full)
|
|
{
|
|
Value res = init;
|
|
if (isZero (init)) // special handling of a zero, due to excel-compat
|
|
{
|
|
if (count (range, full) == 0)
|
|
return init;
|
|
res = 1.0;
|
|
}
|
|
arrayWalk (range, res, awFunc (full ? "proda" : "prod"), 0);
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::product (QValueVector<Value> range,
|
|
Value init, bool full)
|
|
{
|
|
Value res = init;
|
|
if (isZero (init)) // special handling of a zero, due to excel-compat
|
|
{
|
|
if (count (range, full) == 0)
|
|
return init;
|
|
res = 1.0;
|
|
}
|
|
arrayWalk (range, res, awFunc (full ? "proda" : "prod"), 0);
|
|
return res;
|
|
}
|
|
|
|
Value ValueCalc::stddev (const Value &range, bool full)
|
|
{
|
|
return stddev (range, avg (range, full), full);
|
|
}
|
|
|
|
Value ValueCalc::stddev (const Value &range, Value avg,
|
|
bool full)
|
|
{
|
|
Value res;
|
|
int cnt = count (range, full);
|
|
arrayWalk (range, res, awFunc (full ? "devsqa" : "devsq"), avg);
|
|
return sqrt (div (res, cnt-1));
|
|
}
|
|
|
|
Value ValueCalc::stddev (QValueVector<Value> range, bool full)
|
|
{
|
|
return stddev (range, avg (range, full), full);
|
|
}
|
|
|
|
Value ValueCalc::stddev (QValueVector<Value> range,
|
|
Value avg, bool full)
|
|
{
|
|
Value res;
|
|
int cnt = count (range, full);
|
|
arrayWalk (range, res, awFunc (full ? "devsqa" : "devsq"), avg);
|
|
return sqrt (div (res, cnt-1));
|
|
}
|
|
|
|
Value ValueCalc::stddevP (const Value &range, bool full)
|
|
{
|
|
return stddevP (range, avg (range, full), full);
|
|
}
|
|
|
|
Value ValueCalc::stddevP (const Value &range, Value avg,
|
|
bool full)
|
|
{
|
|
Value res;
|
|
int cnt = count (range, full);
|
|
arrayWalk (range, res, awFunc (full ? "devsqa" : "devsq"), avg);
|
|
return sqrt (div (res, cnt));
|
|
}
|
|
|
|
Value ValueCalc::stddevP (QValueVector<Value> range, bool full)
|
|
{
|
|
return stddevP (range, avg (range, full), full);
|
|
}
|
|
|
|
Value ValueCalc::stddevP (QValueVector<Value> range,
|
|
Value avg, bool full)
|
|
{
|
|
Value res;
|
|
int cnt = count (range, full);
|
|
arrayWalk (range, res, awFunc (full ? "devsqa" : "devsq"), avg);
|
|
return sqrt (div (res, cnt));
|
|
}
|
|
|
|
Value::Format ValueCalc::format (Value::Format a,
|
|
Value::Format b)
|
|
{
|
|
if ((a == Value::fmt_None) || (a == Value::fmt_Boolean))
|
|
return b;
|
|
return a;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
|
|
void ValueCalc::getCond (Condition &cond, Value val)
|
|
{
|
|
// not a string - we simply take it as a numeric value
|
|
// that also handles floats, logical values, date/time and such
|
|
if (!val.isString()) {
|
|
cond.comp = isEqual;
|
|
cond.type = numeric;
|
|
cond.value = converter->asFloat (val).asFloat();
|
|
return;
|
|
}
|
|
QString text = converter->asString (val).asString();
|
|
cond.comp = isEqual;
|
|
text = text.stripWhiteSpace();
|
|
|
|
if ( text.startsWith( "<=" ) )
|
|
{
|
|
cond.comp = lessEqual;
|
|
text = text.remove( 0, 2 );
|
|
}
|
|
else if ( text.startsWith( ">=" ) )
|
|
{
|
|
cond.comp = greaterEqual;
|
|
text = text.remove( 0, 2 );
|
|
}
|
|
else if ( text.startsWith( "!=" ) || text.startsWith( "<>" ) )
|
|
{
|
|
cond.comp = notEqual;
|
|
text = text.remove( 0, 2 );
|
|
}
|
|
else if ( text.startsWith( "==" ) )
|
|
{
|
|
cond.comp = isEqual;
|
|
text = text.remove( 0, 2 );
|
|
}
|
|
else if ( text.startsWith( "<" ) )
|
|
{
|
|
cond.comp = isLess;
|
|
text = text.remove( 0, 1 );
|
|
}
|
|
else if ( text.startsWith( ">" ) )
|
|
{
|
|
cond.comp = isGreater;
|
|
text = text.remove( 0, 1 );
|
|
}
|
|
else if ( text.startsWith( "=" ) )
|
|
{
|
|
cond.comp = isEqual;
|
|
text = text.remove( 0, 1 );
|
|
}
|
|
|
|
text = text.stripWhiteSpace();
|
|
|
|
bool ok = false;
|
|
double d = text.toDouble( &ok );
|
|
if ( ok )
|
|
{
|
|
cond.type = numeric;
|
|
cond.value = d;
|
|
}
|
|
else
|
|
{
|
|
cond.type = string;
|
|
cond.stringValue = text;
|
|
}
|
|
}
|
|
|
|
bool ValueCalc::matches (const Condition &cond, Value val)
|
|
{
|
|
if (val.isEmpty())
|
|
return false;
|
|
if (cond.type == numeric) {
|
|
double d = converter->asFloat (val).asFloat();
|
|
switch ( cond.comp )
|
|
{
|
|
case isEqual:
|
|
if (approxEqual (d, cond.value)) return true;
|
|
break;
|
|
|
|
case isLess:
|
|
if (d < cond.value) return true;
|
|
break;
|
|
|
|
case isGreater:
|
|
if (d > cond.value) return true;
|
|
break;
|
|
|
|
case lessEqual:
|
|
if (d <= cond.value) return true;
|
|
break;
|
|
|
|
case greaterEqual:
|
|
if (d >= cond.value) return true;
|
|
break;
|
|
|
|
case notEqual:
|
|
if (d != cond.value) return true;
|
|
break;
|
|
}
|
|
} else {
|
|
QString d = converter->asString (val).asString();
|
|
switch ( cond.comp )
|
|
{
|
|
case isEqual:
|
|
if (d == cond.stringValue) return true;
|
|
break;
|
|
|
|
case isLess:
|
|
if (d < cond.stringValue) return true;
|
|
break;
|
|
|
|
case isGreater:
|
|
if (d > cond.stringValue) return true;
|
|
break;
|
|
|
|
case lessEqual:
|
|
if (d <= cond.stringValue) return true;
|
|
break;
|
|
|
|
case greaterEqual:
|
|
if (d >= cond.stringValue) return true;
|
|
break;
|
|
|
|
case notEqual:
|
|
if (d != cond.stringValue) return true;
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|