/* This file is part of the KDE project
   Copyright (C) 1998-2002 The KSpread Team
                           www.koffice.org/kspread
   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.

   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.
*/

// built-in engineering functions

#include "functions.h"
#include "valuecalc.h"
#include "valueconverter.h"

// used by the CONVERT function
#include <qmap.h>

// these are needed for complex functions, while we handle them in the old way
#include <kglobal.h>
#include <klocale.h>
#include <math.h>

using namespace KSpread;

// prototypes (sort alphabetically)
Value func_base (valVector args, ValueCalc *calc, FuncExtra *);
Value func_besseli (valVector args, ValueCalc *calc, FuncExtra *);
Value func_besselj (valVector args, ValueCalc *calc, FuncExtra *);
Value func_besselk (valVector args, ValueCalc *calc, FuncExtra *);
Value func_bessely (valVector args, ValueCalc *calc, FuncExtra *);
Value func_bin2dec (valVector args, ValueCalc *calc, FuncExtra *);
Value func_bin2oct (valVector args, ValueCalc *calc, FuncExtra *);
Value func_bin2hex (valVector args, ValueCalc *calc, FuncExtra *);
Value func_complex (valVector args, ValueCalc *calc, FuncExtra *);
Value func_complex_imag (valVector args, ValueCalc *calc, FuncExtra *);
Value func_complex_real (valVector args, ValueCalc *calc, FuncExtra *);
Value func_convert (valVector args, ValueCalc *calc, FuncExtra *);
Value func_dec2hex (valVector args, ValueCalc *calc, FuncExtra *);
Value func_dec2oct (valVector args, ValueCalc *calc, FuncExtra *);
Value func_dec2bin (valVector args, ValueCalc *calc, FuncExtra *);
Value func_delta (valVector args, ValueCalc *calc, FuncExtra *);
Value func_erf (valVector args, ValueCalc *calc, FuncExtra *);
Value func_erfc (valVector args, ValueCalc *calc, FuncExtra *);
Value func_gestep (valVector args, ValueCalc *calc, FuncExtra *);
Value func_hex2dec (valVector args, ValueCalc *calc, FuncExtra *);
Value func_hex2bin (valVector args, ValueCalc *calc, FuncExtra *);
Value func_hex2oct (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imabs (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imargument (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imconjugate (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imcos (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imdiv (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imexp (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imln (valVector args, ValueCalc *calc, FuncExtra *);
Value func_impower (valVector args, ValueCalc *calc, FuncExtra *);
Value func_improduct (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imsin (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imsqrt (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imsub (valVector args, ValueCalc *calc, FuncExtra *);
Value func_imsum (valVector args, ValueCalc *calc, FuncExtra *);
Value func_oct2dec (valVector args, ValueCalc *calc, FuncExtra *);
Value func_oct2bin (valVector args, ValueCalc *calc, FuncExtra *);
Value func_oct2hex (valVector args, ValueCalc *calc, FuncExtra *);

// registers all engineering functions
void RegisterEngineeringFunctions()
{
  FunctionRepository* repo = FunctionRepository::self();
  Function *f;

  f = new Function ("BASE",        func_base);    // KSpread-specific, like in Quattro-Pro
  f->setParamCount (1, 3);
  repo->add (f);
  f = new Function ("BESSELI",     func_besseli);
  f->setParamCount (2);
  repo->add (f);
  f = new Function ("BESSELJ",     func_besselj);
  f->setParamCount (2);
  repo->add (f);
  f = new Function ("BESSELK",     func_besselk);
  f->setParamCount (2);
  repo->add (f);
  f = new Function ("BESSELY",     func_bessely);
  f->setParamCount (2);
  repo->add (f);
  f = new Function ("BIN2DEC",     func_bin2dec);
  repo->add (f);
  f = new Function ("BIN2OCT",     func_bin2oct);
  repo->add (f);
  f = new Function ("BIN2HEX",     func_bin2hex);
  repo->add (f);
  f = new Function ("COMPLEX",     func_complex);
  f->setParamCount (2);
  repo->add (f);
  f = new Function ("CONVERT",     func_convert);
  f->setParamCount (3);
  repo->add (f);
  f = new Function ("DEC2HEX",     func_dec2hex);
  repo->add (f);
  f = new Function ("DEC2BIN",     func_dec2bin);
  repo->add (f);
  f = new Function ("DEC2OCT",     func_dec2oct);
  repo->add (f);
  f = new Function ("DELTA",       func_delta);
  f->setParamCount (1, 2);
  repo->add (f);
  f = new Function ("ERF",         func_erf);
  f->setParamCount (2);
  repo->add (f);
  f = new Function ("ERFC",        func_erfc);
  f->setParamCount (1, 2);
  repo->add (f);
  f = new Function ("GESTEP",      func_gestep);
  f->setParamCount (1, 2);
  repo->add (f);
  f = new Function ("HEX2BIN",     func_hex2bin);
  repo->add (f);
  f = new Function ("HEX2DEC",     func_hex2dec);
  repo->add (f);
  f = new Function ("HEX2OCT",     func_hex2oct);
  repo->add (f);
  f = new Function ("IMABS",       func_imabs);
  repo->add (f);
  f = new Function ("IMAGINARY",   func_complex_imag);
  repo->add (f);
  f = new Function ("IMARGUMENT",  func_imargument);
  repo->add (f);
  f = new Function ("IMCONJUGATE", func_imconjugate);
  repo->add (f);
  f = new Function ("IMCOS",       func_imcos);
  repo->add (f);
  f = new Function ("IMDIV",       func_imdiv);
  f->setParamCount (1, -1);
  f->setAcceptArray ();
  repo->add (f);
  f = new Function ("IMEXP",       func_imexp);
  repo->add (f);
  f = new Function ("IMLN",        func_imln);
  repo->add (f);
  f = new Function ("IMPOWER",     func_impower);
  f->setParamCount (2);
  repo->add (f);
  f = new Function ("IMPRODUCT",   func_improduct);
  f->setParamCount (1, -1);
  f->setAcceptArray ();
  repo->add (f);
  f = new Function ("IMREAL",      func_complex_real);
  repo->add (f);
  f = new Function ("IMSIN",       func_imsin);
  repo->add (f);
  f = new Function ("IMSQRT",      func_imsqrt);
  repo->add (f);
  f = new Function ("IMSUB",       func_imsub);
  f->setParamCount (1, -1);
  f->setAcceptArray ();
  repo->add (f);
  f = new Function ("IMSUM",       func_imsum);
  f->setParamCount (1, -1);
  f->setAcceptArray ();
  repo->add (f);
  f = new Function ("OCT2BIN",     func_oct2bin);
  repo->add (f);
  f = new Function ("OCT2DEC",     func_oct2dec);
  repo->add (f);
  f = new Function ("OCT2HEX",     func_oct2hex);
  repo->add (f);
}

// Function: BASE
Value func_base (valVector args, ValueCalc *calc, FuncExtra *)
{
  int base = 10;
  int prec = 0;
  if (args.count() > 1)
    base = calc->conv()->asInteger (args[1]).asInteger();
  if (args.count() == 3)
    prec = calc->conv()->asInteger (args[2]).asInteger();
    
  if ((base < 2) || (base > 36))
    return Value::errorVALUE();
  if (prec < 0) prec = 2;

  return calc->base (args[0], base, prec);
}

// Function: BESSELI
Value func_besseli (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value x = args[0];
  Value y = args[1];
  return calc->besseli (y, x);
}

// Function: BESSELJ
Value func_besselj (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value x = args[0];
  Value y = args[1];
  return calc->besselj (y, x);
}

// Function: BESSELK
Value func_besselk (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value x = args[0];
  Value y = args[1];
  return calc->besselk (y, x);
}

// Function: BESSELY
Value func_bessely (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value x = args[0];
  Value y = args[1];
  return calc->besseln (y, x);
}

// Function: DEC2HEX
Value func_dec2hex (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (args[0], 16);
}

// Function: DEC2OCT
Value func_dec2oct (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (args[0], 8);
}

// Function: DEC2BIN
Value func_dec2bin (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (args[0], 2);
}

// Function: BIN2DEC
Value func_bin2dec (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->fromBase (args[0], 2);
}

// Function: BIN2OCT
Value func_bin2oct (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (calc->fromBase (args[0], 2), 8);
}

// Function: BIN2HEX
Value func_bin2hex (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (calc->fromBase (args[0], 2), 16);
}

// Function: OCT2DEC
Value func_oct2dec (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->fromBase (args[0], 8);
}

// Function: OCT2BIN
Value func_oct2bin (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (calc->fromBase (args[0], 8), 2);
}

// Function: OCT2HEX
Value func_oct2hex (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (calc->fromBase (args[0], 8), 16);
}

// Function: HEX2DEC
Value func_hex2dec (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->fromBase (args[0], 16);
}

// Function: HEX2BIN
Value func_hex2bin (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (calc->fromBase (args[0], 16), 2);
}

// Function: HEX2OCT
Value func_hex2oct (valVector args, ValueCalc *calc, FuncExtra *)
{
  return calc->base (calc->fromBase (args[0], 16), 8);
}


// check if unit may contain prefix, for example "kPa" is "Pa" with
// return prefix factor found in unit, or 1.0 for no prefix
// also modify the unit, i.e stripping the prefix from it
// example: "kPa" will return 1e3 and change unit into "Pa"
static double kspread_convert_prefix( QMap<QString,double> map, QString& unit )
{
  if( map.contains( unit ) )
    return 1.0;

  // initialize prefix mapping if necessary
  static QMap<char,double> prefixMap;
  if( prefixMap.isEmpty() )
  {
     prefixMap[ 'E' ] = 1e18;  //  exa
     prefixMap[ 'P' ] = 1e15;  //  peta
     prefixMap[ 'T' ] = 1e12;  // tera
     prefixMap[ 'G' ] = 1e9;   // giga
     prefixMap[ 'M' ] = 1e6;   // mega
     prefixMap[ 'k' ] = 1e3;   // kilo
     prefixMap[ 'h' ] = 1e2;   // hecto
     prefixMap[ 'e' ] = 1e1;   // dekao
     prefixMap[ 'd' ] = 1e1;   // deci
     prefixMap[ 'c' ] = 1e2;   // centi
     prefixMap[ 'm' ] = 1e3;   // milli
     prefixMap[ 'u' ] = 1e6;   // micro
     prefixMap[ 'n' ] = 1e9;   // nano
     prefixMap[ 'p' ] = 1e12;  // pico
     prefixMap[ 'f' ] = 1e15;  // femto
     prefixMap[ 'a' ] = 1e18;  // atto
  }

  // check for possible prefix
  char prefix = unit[0].latin1();
  if( prefixMap.contains( prefix ) )
  {
    unit.remove( 0, 1 );
    return prefixMap[ prefix ];
  }

  // fail miserably
  return 0.0;
}

static bool kspread_convert_mass( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> massMap;

  // first-time initialization
  if( massMap.isEmpty() )
  {
    massMap[ "g" ]        = 1.0; // Gram (the reference )
    massMap[ "sg" ]       = 6.8522050005347800E-05; // Pieces
    massMap[ "lbm" ]      = 2.2046229146913400E-03; // Pound
    massMap[ "u" ]        = 6.0221370000000000E23; // U (atomic mass)
    massMap[ "ozm" ]      = 3.5273971800362700E-02; // Ounce
    massMap[ "stone" ]    = 1.574730e-04; // Stone
    massMap[ "ton" ]      = 1.102311e-06; // Ton
    massMap[ "grain" ]    = 1.543236E01;  // Grain
    massMap[ "pweight" ]  = 7.054792E-01; // Pennyweight
    massMap[ "hweight" ]  = 1.968413E-05; // Hundredweight
    massMap[ "shweight" ] = 2.204623E-05; // Shorthundredweight
    massMap[ "brton" ]    = 9.842065E-07; // Gross Registered Ton
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( massMap, fromU );
  double toPrefix = kspread_convert_prefix( massMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !massMap.contains( fromU ) ) return false;
  if( !massMap.contains( toU ) ) return false;

  result = value * fromPrefix * massMap[toU] / (massMap[fromU] * toPrefix);

  return true;
}


static bool kspread_convert_distance( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> distanceMap;

  // first-time initialization
  if( distanceMap.isEmpty() )
  {
    distanceMap[ "m" ]         = 1.0;  // meter (the reference)
    distanceMap[ "in" ]        = 1.0 / 0.0254; // inch
    distanceMap[ "ft" ]        = 1.0 / (12.0 * 0.0254); // feet
    distanceMap[ "yd" ]        = 1.0 / (3.0 * 12.0 * 0.0254); // yar
    distanceMap[ "mi" ]        = 6.2137119223733397e-4; // mile
    distanceMap[ "Nmi" ]       = 5.3995680345572354e-04; // nautical mile
    distanceMap[ "ang" ]       = 1e10; // Angstrom
    distanceMap[ "parsec" ]    = 3.240779e-17; // Parsec
    distanceMap[ "lightyear" ] = 1.057023455773293e-16; // lightyear
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( distanceMap, fromU );
  double toPrefix = kspread_convert_prefix( distanceMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !distanceMap.contains( fromU ) ) return false;
  if( !distanceMap.contains( toU ) ) return false;

  result = value * fromPrefix * distanceMap[toU] / (distanceMap[fromU] * toPrefix);

  return true;
}

static bool kspread_convert_pressure( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> pressureMap;

  // first-time initialization
  if( pressureMap.isEmpty() )
  {
    pressureMap[ "Pa" ] = 1.0;
    pressureMap[ "atm" ] = 0.9869233e-5;
    pressureMap[ "mmHg" ] = 0.00750061708;
    pressureMap[ "psi" ] = 1 / 6894.754;
    pressureMap[ "Torr" ] = 1 / 133.32237;
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( pressureMap, fromU );
  double toPrefix = kspread_convert_prefix( pressureMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !pressureMap.contains( fromU ) ) return false;
  if( !pressureMap.contains( toU ) ) return false;

  result = value * fromPrefix * pressureMap[toU] / (pressureMap[fromU] * toPrefix);

  return true;
}

static bool kspread_convert_force( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> forceMap;

  // first-time initialization
  if( forceMap.isEmpty() )
  {
    forceMap[ "N" ]      = 1.0; // Newton (reference)
    forceMap[ "dyn" ]    = 1.0e5; // dyn
    forceMap[ "pond" ]   = 1.019716e2; // pond
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( forceMap, fromU );
  double toPrefix = kspread_convert_prefix( forceMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !forceMap.contains( fromU ) ) return false;
  if( !forceMap.contains( toU ) ) return false;

  result = value * fromPrefix * forceMap[toU] / (forceMap[fromU] * toPrefix);

  return true;
}

static bool kspread_convert_energy( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> energyMap;

  // first-time initialization
  if( energyMap.isEmpty() )
  {
    energyMap[ "J" ]   = 1.0; // Joule (the reference)
    energyMap[ "e" ]   = 1.0e7; //erg
    energyMap[ "c" ]   = 0.239006249473467; // thermodynamical calorie
    energyMap[ "cal" ] = 0.238846190642017; // calorie
    energyMap[ "eV" ]  = 6.241457e+18; // electronvolt
    energyMap[ "HPh" ] = 3.72506111e-7; // horsepower-hour
    energyMap[ "Wh" ]  = 0.000277778; // watt-hour
    energyMap[ "flb" ] = 23.73042222;
    energyMap[ "BTU" ] = 9.47815067349015e-4; // British Thermal Unit
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( energyMap, fromU );
  double toPrefix = kspread_convert_prefix( energyMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !energyMap.contains( fromU ) ) return false;
  if( !energyMap.contains( toU ) ) return false;

  result = value * fromPrefix * energyMap[toU] / (energyMap[fromU] * toPrefix);

  return true;
}

static bool kspread_convert_power( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> powerMap;

  // first-time initialization
  if( powerMap.isEmpty() )
  {
    powerMap[ "W" ]   = 1.0; // Watt (the reference)
    powerMap[ "HP" ]  = 1.341022e-3; // Horsepower
    powerMap[ "PS" ]  = 1.359622e-3; // Pferdestärke (German)
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( powerMap, fromU );
  double toPrefix = kspread_convert_prefix( powerMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !powerMap.contains( fromU ) ) return false;
  if( !powerMap.contains( toU ) ) return false;

  result = value * fromPrefix * powerMap[toU] / (powerMap[fromU] * toPrefix);

  return true;
}

static bool kspread_convert_magnetism( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> magnetismMap;

  // first-time initialization
  if( magnetismMap.isEmpty() )
  {
    magnetismMap[ "T" ]   = 1.0;    // Tesla (the reference)
    magnetismMap[ "ga" ]   = 1.0e4; // Gauss
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( magnetismMap, fromU );
  double toPrefix = kspread_convert_prefix( magnetismMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !magnetismMap.contains( fromU ) ) return false;
  if( !magnetismMap.contains( toU ) ) return false;

  result = value * fromPrefix * magnetismMap[toU] / (magnetismMap[fromU] * toPrefix);

  return true;
}

static bool kspread_convert_temperature( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> tempFactorMap;
  static QMap<QString, double> tempOffsetMap;

  // first-time initialization
  if( tempFactorMap.isEmpty() || tempOffsetMap.isEmpty() )
  {
    tempFactorMap[ "C" ] = 1.0; tempOffsetMap[ "C" ] = 0.0;
    tempFactorMap[ "F" ] = 5.0/9.0; tempOffsetMap[ "F" ] = -32.0;
    tempFactorMap[ "K" ] = 1.0; tempOffsetMap[ "K" ] = -273.15;
  }

  if( !tempFactorMap.contains( fromUnit ) ) return false;
  if( !tempOffsetMap.contains( fromUnit ) ) return false;
  if( !tempFactorMap.contains( toUnit ) ) return false;
  if( !tempOffsetMap.contains( toUnit ) ) return false;

  result = ( value + tempOffsetMap[ fromUnit ] )* tempFactorMap[ fromUnit ];
  result = ( result / tempFactorMap[ toUnit ] ) - tempOffsetMap[ toUnit ];

  return true;
}

static bool kspread_convert_volume( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> volumeMap;

  // first-time initialization
  if( volumeMap.isEmpty() )
  {
    volumeMap[ "l" ]      = 1.0; // Liter (the reference)
    volumeMap[ "tsp" ]    = 202.84; // teaspoon
    volumeMap[ "tbs" ]    = 67.6133333333333; // sheetspoon
    volumeMap[ "oz" ]     = 33.8066666666667; // ounce liquid
    volumeMap[ "cup" ]    = 4.22583333333333; // cup
    volumeMap[ "pt" ]     = 2.11291666666667; // pint
    volumeMap[ "qt" ]     = 1.05645833333333; // quart
    volumeMap[ "gal" ]    = 0.26411458333333; // gallone
    volumeMap[ "m3" ]     = 1.0e-3; // cubic meter
    volumeMap[ "mi3" ]    = 2.3991275857892772e-13; // cubic mile
    volumeMap[ "Nmi3" ]   = 1.5742621468581148e-13; // cubic Nautical mile
    volumeMap[ "in3" ]    = 6.1023744094732284e1; // cubic inch
    volumeMap[ "ft3" ]    = 3.5314666721488590e-2; // cubic foot
    volumeMap[ "yd3" ]    = 1.3079506193143922; // cubic yard
    volumeMap[ "barrel" ] = 6.289811E-03; // barrel
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( volumeMap, fromU );
  double toPrefix = kspread_convert_prefix( volumeMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !volumeMap.contains( fromU ) ) return false;
  if( !volumeMap.contains( toU ) ) return false;

  result = value * fromPrefix * volumeMap[toU] / (volumeMap[fromU] * toPrefix);

  return true;
}

static bool kspread_convert_area( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> areaMap;

  // first-time initialization
  if( areaMap.isEmpty() )
  {
    areaMap[ "m2" ]   = 1.0; // square meter (the reference)
    areaMap[ "mi2" ]  = 3.8610215854244585e-7; // square mile
    areaMap[ "Nmi2" ] = 2.9155334959812286e-7; // square Nautical mile
    areaMap[ "in2" ]  = 1.5500031000062000e3; // square inch
    areaMap[ "ft2" ]  = 1.0763910416709722e1; // square foot
    areaMap[ "yd2" ]  = 1.0936132983377078; // square yard
    areaMap[ "acre" ] = 4.046856e3; // acre
    areaMap[ "ha" ]   = 1.0e4; // hectare
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( areaMap, fromU );
  double toPrefix = kspread_convert_prefix( areaMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !areaMap.contains( fromU ) ) return false;
  if( !areaMap.contains( toU ) ) return false;

  result = value * fromPrefix * areaMap[toU] / (areaMap[fromU] * toPrefix);

  return true;
}

static bool kspread_convert_speed( const QString& fromUnit,
  const QString& toUnit, double value, double& result )
{
  static QMap<QString, double> speedMap;

  // first-time initialization
  if( speedMap.isEmpty() )
  {
    speedMap[ "m/s" ] = 1.0; // meters per second (the reference)
    speedMap[ "m/h" ] = 3.6e3; // meters per hour
    speedMap[ "mph" ] = 2.2369362920544023; // miles per hour
    speedMap[ "kn" ]  = 1.9438444924406048; // knot
  }

  QString fromU = fromUnit;
  QString toU = toUnit;
  double fromPrefix = kspread_convert_prefix( speedMap, fromU );
  double toPrefix = kspread_convert_prefix( speedMap, toU );
  if( fromPrefix == 0.0 ) return false;
  if( toPrefix == 0.0 ) return false;
  if( !speedMap.contains( fromU ) ) return false;
  if( !speedMap.contains( toU ) ) return false;

  result = value * fromPrefix * speedMap[toU] / (speedMap[fromU] * toPrefix);

  return true;
}


// Function: CONVERT
Value func_convert (valVector args, ValueCalc *calc, FuncExtra *)
{
  // This function won't support arbitrary precision.

  double value = calc->conv()->asFloat (args[0]).asFloat ();
  QString fromUnit = calc->conv()->asString (args[1]).asString();
  QString toUnit = calc->conv()->asString (args[2]).asString();

  double result = value;

  if( !kspread_convert_mass( fromUnit, toUnit, value, result ) )
    if( !kspread_convert_distance( fromUnit, toUnit, value, result ) )
      if( !kspread_convert_pressure( fromUnit, toUnit, value, result ) )
        if( !kspread_convert_force( fromUnit, toUnit, value, result ) )
          if( !kspread_convert_energy( fromUnit, toUnit, value, result ) )
            if( !kspread_convert_power( fromUnit, toUnit, value, result ) )
              if( !kspread_convert_magnetism( fromUnit, toUnit, value, result ) )
                if( !kspread_convert_temperature( fromUnit, toUnit, value, result ) )
                  if( !kspread_convert_volume( fromUnit, toUnit, value, result ) )
                    if( !kspread_convert_area( fromUnit, toUnit, value, result ) )
                      if( !kspread_convert_speed( fromUnit, toUnit, value, result ) )
                        return Value::errorNA();

  return Value (result);
}


// functions operating over complex numbers ...
// these may eventually end up being merged into ValueCalc and friends
// then complex numbers will be handled transparently in most functions

static QString func_create_complex( double real,double imag )
{
  QString tmp,tmp2;
  if(imag ==0)
        {
        return KGlobal::locale()->formatNumber( real);
        }
  if(real!=0)
        tmp=KGlobal::locale()->formatNumber(real);
  else
    return KGlobal::locale()->formatNumber(imag)+"i";
  if (imag >0)
        tmp=tmp+"+"+KGlobal::locale()->formatNumber(imag)+"i";
  else
        tmp=tmp+KGlobal::locale()->formatNumber(imag)+"i";
  return tmp;

}

// Function: COMPLEX
Value func_complex (valVector args, ValueCalc *calc, FuncExtra *)
{
  if (calc->isZero (args[1]))
    return args[0];
  double re = calc->conv()->asFloat (args[0]).asFloat ();
  double im = calc->conv()->asFloat (args[1]).asFloat ();
  QString tmp=func_create_complex (re, im);
  bool ok;
  double result = KGlobal::locale()->readNumber(tmp, &ok);
  if (ok)
    return Value (result);
  return Value (tmp);
}


static double imag_complexe(QString str, bool &ok)
{
QString tmp=str;
if(tmp.find('i')==-1)
        {  //not a complex
        ok=true;
        return 0;
        }
else if( tmp.length()==1)
        {
        // i
        ok=true;
        return 1;
        }
else  if( tmp.length()==2 )
        {
        //-i,+i,
        int pos1;
        if((pos1=tmp.find('+'))!=-1&& pos1==0)
                {
                ok=true;
                return 1;
                }
        else if( (pos1=tmp.find('-'))!=-1 && pos1==0 )
                {
                ok=true;
                return -1;
                }
        else if(tmp[0].isDigit())
                { //5i
                ok=true;
                return KGlobal::locale()->readNumber(tmp.left(1));
                }
        else
                {
                ok=false;
                return 0;
                }
        }
else
        {//12+12i
        int pos1,pos2;
        if((pos1=tmp.find('i'))!=-1)
                {
                double val;
                QString tmpStr;

                if((pos2=tmp.findRev('+'))!=-1 && pos2!=0)
                        {
                        if((pos1-pos2)==1)
                                {
                                 ok=true;
                                 return 1;
                                }
                        else
                                {
                                tmpStr=tmp.mid(pos2,(pos1-pos2));
                                val=KGlobal::locale()->readNumber(tmpStr, &ok);
                                if(!ok)
                                        val=0;
                                return val;
                                }
                        }
                else if( (pos2=tmp.findRev('-'))!=-1&& pos2!=0)
                        {
                        if((pos1-pos2)==1)
                                {
                                 ok=true;
                                 return -1;
                                }
                        else
                                {
                                tmpStr=tmp.mid(pos2,(pos1-pos2));
                                val=KGlobal::locale()->readNumber(tmpStr, &ok);
                                if(!ok)
                                        val=0;
                                return val;
                                }
                        }
                else
                        {//15.55i
                        tmpStr=tmp.left(pos1);
                        val=KGlobal::locale()->readNumber(tmpStr, &ok);
                        if(!ok)
                                val=0;
                        return val;
                        }
                }
        }
ok=false;
return 0;
}

// Function: IMAGINARY
Value func_complex_imag (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString ();
  bool good;
  double result=imag_complexe(tmp, good);
  if (good)
    return Value (result);
  return Value::errorVALUE();
}


static double real_complexe(QString str, bool &ok)
{
double val;
int pos1,pos2;
QString tmp=str;
QString tmpStr;
if((pos1=tmp.find('i'))==-1)
        { //12.5
        val=KGlobal::locale()->readNumber(tmp, &ok);
        if(!ok)
                val=0;
        return val;
        }
else
        { //15-xi
        if((pos2=tmp.findRev('-'))!=-1 && pos2!=0)
                {
                tmpStr=tmp.left(pos2);
                val=KGlobal::locale()->readNumber(tmpStr, &ok);
                if(!ok)
                        val=0;
                return val;
                } //15+xi
        else if((pos2=tmp.findRev('+'))!=-1)
                {
                tmpStr=tmp.left(pos2);
                val=KGlobal::locale()->readNumber(tmpStr, &ok);
                if(!ok)
                        val=0;
                return val;
                }
        else
                {
                ok=true;
                return 0;
                }
        }

ok=false;
return 0;
}

// Function: IMREAL
Value func_complex_real (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString ();
  bool good;
  double result=real_complexe(tmp, good);
  if (good)
    return Value (result);
  return Value::errorVALUE();
}

void ImHelper (ValueCalc *c, Value res, Value val,
    double &imag, double &real, double &imag1, double &real1)
{
  bool ok;
  imag=imag_complexe(res.asString(), ok);
  real=real_complexe(res.asString(), ok);
  if (val.isString())
  {
    imag1 = imag_complexe (val.asString(), ok);
    real1 = real_complexe (val.asString(), ok);
  } else {
    imag1=0;
    real1=c->conv()->asFloat (val).asFloat();
  }
}

void awImSum (ValueCalc *c, Value &res, Value val, Value)
{
  double imag,real,imag1,real1;
  ImHelper (c, res, val, imag, real, imag1, real1);
  res=func_create_complex(real+real1,imag+imag1);
}

void awImSub (ValueCalc *c, Value &res, Value val, Value)
{
  double imag,real,imag1,real1;
  ImHelper (c, res, val, imag, real, imag1, real1);
  res=func_create_complex(real-real1,imag-imag1);
}

void awImMul (ValueCalc *c, Value &res, Value val, Value)
{
  double imag,real,imag1,real1;
  ImHelper (c, res, val, imag, real, imag1, real1);
  res=func_create_complex(real*real1+(imag*imag1)*-1,real*imag1+real1*imag);
}

void awImDiv (ValueCalc *c, Value &res, Value val, Value)
{
  double imag,real,imag1,real1;
  ImHelper (c, res, val, imag, real, imag1, real1);
  res=func_create_complex((real*real1+imag*imag1)/(real1*real1+imag1*imag1),
      (real1*imag-real*imag1)/(real1*real1+imag1*imag1));
}

// Function: IMSUM
Value func_imsum (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value result;
  calc->arrayWalk (args, result, awImSum, 0);
  
  bool ok;
  QString res = calc->conv()->asString (result).asString();
  double val=KGlobal::locale()->readNumber(res, &ok);
  if (ok)
    return Value (val);
  return Value (result);
}

// Function: IMSUB
Value func_imsub (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value result;
  calc->arrayWalk (args, result, awImSub, 0);
  
  bool ok;
  QString res = calc->conv()->asString (result).asString();
  double val=KGlobal::locale()->readNumber(res, &ok);
  if (ok)
    return Value (val);
  return Value (result);
}

// Function: IMPRODUCT
Value func_improduct (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value result;
  calc->arrayWalk (args, result, awImMul, 0);
  
  bool ok;
  QString res = calc->conv()->asString (result).asString();
  double val=KGlobal::locale()->readNumber(res, &ok);
  if (ok)
    return Value (val);
  return Value (result);
}

// Function: IMDIV
Value func_imdiv (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value result;
  calc->arrayWalk (args, result, awImDiv, 0);
  
  bool ok;
  QString res = calc->conv()->asString (result).asString();
  double val=KGlobal::locale()->readNumber(res, &ok);
  if (ok)
    return Value (val);
  return Value (result);
}

// Function: IMCONJUGATE
Value func_imconjugate (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  bool ok;
  double real=real_complexe(tmp,ok);
  if (!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();

  tmp=func_create_complex(real,-imag);

  double result=KGlobal::locale()->readNumber(tmp, &ok);
  if(ok)
    return Value (result);
  
  return Value (tmp);
}

// Function: IMARGUMENT
Value func_imargument (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  bool ok;
  double real=real_complexe(tmp,ok);
  if (!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if (!ok)
    return Value::errorVALUE();
  if(imag==0)
    return Value::errorDIV0();
  double arg=atan2(imag,real);

  return Value (arg);
}

// Function: IMABS
Value func_imabs (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  bool ok;
  double real=real_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double arg=sqrt(pow(imag,2)+pow(real,2));

  return Value (arg);
}

// Function: IMCOS
Value func_imcos (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  bool ok;
  double real=real_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag_res=sin(real)*sinh(imag);
  double real_res=cos(real)*cosh(imag);


  tmp=func_create_complex(real_res,-imag_res);

  double result=KGlobal::locale()->readNumber(tmp, &ok);
  if(ok)
    return Value (result);
  
  return Value (tmp);
}

// Function: IMSIN
Value func_imsin (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  bool ok;
  double real=real_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag_res=cos(real)*sinh(imag);
  double real_res=sin(real)*cosh(imag);


  tmp=func_create_complex(real_res,imag_res);

  double result=KGlobal::locale()->readNumber(tmp, &ok);
  if(ok)
    return Value (result);
  
  return Value (tmp);
}

// Function: IMLN
Value func_imln (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  bool ok;
  double real=real_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();

  double arg=sqrt(pow(imag,2)+pow(real,2));
  double real_res=log(arg);
  double imag_res=atan(imag/real);
  tmp=func_create_complex(real_res,imag_res);

  double result=KGlobal::locale()->readNumber(tmp, &ok);
  if(ok)
    return Value (result);
  
  return Value (tmp);
}

// Function: IMEXP
Value func_imexp (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  bool ok;
  double real=real_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag_res=exp(real)*sin(imag);
  double real_res=exp(real)*cos(imag);


  tmp=func_create_complex(real_res,imag_res);

  double result=KGlobal::locale()->readNumber(tmp, &ok);
  if(ok)
    return Value (result);
  
  return Value (tmp);
}

// Function: IMSQRT
Value func_imsqrt (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  bool ok;
  double real=real_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double arg=sqrt(sqrt(pow(imag,2)+pow(real,2)));
  double angle=atan(imag/real);

  double real_res=arg*cos((angle/2));
  double imag_res=arg*sin((angle/2));

  tmp=func_create_complex(real_res,imag_res);

  double result=KGlobal::locale()->readNumber(tmp, &ok);
  if(ok)
    return Value (result);
  
  return Value (tmp);
}

// Function: IMPOWER
Value func_impower (valVector args, ValueCalc *calc, FuncExtra *)
{
  QString tmp = calc->conv()->asString (args[0]).asString();
  double val2 = calc->conv()->asFloat (args[1]).asFloat();
  bool ok;
  double real=real_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();
  double imag=imag_complexe(tmp,ok);
  if(!ok)
    return Value::errorVALUE();

  double arg=::pow(sqrt(pow(imag,2)+pow(real,2)),val2);
  double angle=atan(imag/real);

  double real_res=arg*cos(angle*val2);
  double imag_res=arg*sin(angle*val2);

  tmp=func_create_complex(real_res,imag_res);

  double result=KGlobal::locale()->readNumber(tmp, &ok);
  if(ok)
    return Value (result);
  
  return Value (tmp);
}

// Function: DELTA
Value func_delta (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value val1 = args[0];
  Value val2 = 0.0;
  if (args.count() == 2)
    val2 = args[1];
  
  return Value (calc->approxEqual (val1, val2) ? 1 : 0);
}

// Function: ERF
Value func_erf (valVector args, ValueCalc *calc, FuncExtra *)
{
  if (args.count() == 2)
    return calc->sub (calc->erf (args[1]), calc->erf (args[0]));
  return calc->erf (args[0]);
}

// Function: ERFC
Value func_erfc (valVector args, ValueCalc *calc, FuncExtra *)
{
  if (args.count() == 2)
    return calc->sub (calc->erfc (args[1]), calc->erfc (args[0]));
  return calc->erfc (args[0]);
}

// Function: GESTEP
Value func_gestep (valVector args, ValueCalc *calc, FuncExtra *)
{
  Value x = args[0];
  Value y = 0.0;
  if (args.count() == 2)
    y = args[1];
  
  int result = 0;
  if (calc->greater (x, y) || calc->approxEqual (x, y))
    result = 1;

  return Value (result);
}

