summaryrefslogtreecommitdiffstats
path: root/kspread/valuecalc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kspread/valuecalc.cpp')
-rw-r--r--kspread/valuecalc.cpp1992
1 files changed, 1992 insertions, 0 deletions
diff --git a/kspread/valuecalc.cpp b/kspread/valuecalc.cpp
new file mode 100644
index 00000000..3a1182e5
--- /dev/null
+++ b/kspread/valuecalc.cpp
@@ -0,0 +1,1992 @@
+/* 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();
+ TQString result = TQString::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)
+{
+ TQString 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 (TQValueVector<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 (TQValueVector<Value> &a1,
+ TQValueVector<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 TQString &name)
+{
+ if (awFuncs.count(name))
+ return awFuncs[name];
+ return 0;
+}
+
+void ValueCalc::registerAwFunc (const TQString &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 (TQValueVector<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 (TQValueVector<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 (TQValueVector<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 (TQValueVector<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 (TQValueVector<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 (TQValueVector<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 (TQValueVector<Value> range, bool full)
+{
+ return stddev (range, avg (range, full), full);
+}
+
+Value ValueCalc::stddev (TQValueVector<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 (TQValueVector<Value> range, bool full)
+{
+ return stddevP (range, avg (range, full), full);
+}
+
+Value ValueCalc::stddevP (TQValueVector<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;
+ }
+ TQString 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 {
+ TQString 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;
+}
+