//#if WIN
//#define _TIMEVAL_DEFINED
//#endif
#include <time.h>
#include <stdarg.h>
#include <limits.h>
#include <cmath>

#include "dpuser.yacchelper.h"
#include "fitting.h"
#ifdef HAS_PGPLOT
#include "cpgplot.h"
#endif /* HAS_PGPLOT */
#include "functions/astrolib/astrolib.h"
#include "platform.h"
#include "dpuser_funcs.h"
#include "dpuser_utils.h"
#include "JulianDay.h"
#include "fits.h"
#include "cube.h"
#include "gsl/gsl_sf_erf.h"
#include "gsl/gsl_sf_gamma.h"
#include "gsl/gsl_sf_bessel.h"
#include "gsl/gsl_rng.h"
#include "gsl/gsl_randist.h"
#include "functions.h"
#include "procedures.h"
#include "utils.h"
#include "dpuser_utils.h"
#include "dpstring.h"
#include "dpstringlist.h"

#ifdef WIN
#pragma warning (disable: 4018) // disable warning for comparing signed and unsigned
#pragma warning (disable: 4800) // disable warning for setting boolean value
#pragma warning (disable: 4996) // disable warning for deprecated POSIX name
#endif /* WIN */

#ifdef _WINDOWS
#include <float.h>
/*returns 1 if x is positive infinity, and -1 if x is negative infinity.*/
int my_finite(double x)
{
    if (_fpclass(x) == _FPCLASS_PINF) {
        return 1;
    } else if (_fpclass(x) == _FPCLASS_NINF) {
        return -1;
    }

    return 0;
}

#define _ISINF my_finite
#else
#define _ISINF std::isinf
#endif

std::vector<compiled_function> compiledfunctions;

extern gsl_rng *gsl_rng_r;  /* global generator */

int nalternate = 4;

/* alternate names for functions - mainly for IDL compatibility */
char *alternateFunctionNames[][4] = {
    { "fltarr", "floatarray" },
    { "intarr", "intarray" },
    { "dblarr", "doublearray" },
    { "double", "float" }
};

char *alternateFunctionName(char *s) {
    int i;

    for (i = 0; i < nalternate; i++) {
        if (strcmp(s, alternateFunctionNames[i][0]) == 0) return alternateFunctionNames[i][1];
    }
    return s;
}

int nfunctions;

int lookupUserFunction(char *s) {
    dpint64 i;

    for (i = 0; i < userfncnames.count(); i++) {
        if (userfncnames[i] == s) return i;
    }
    return -1;
}

// resolve function-names
int funcWord(char *s) {
    int i;
    char *name = alternateFunctionName(s);

   // built-in
    for (i = 0; i < nfunctions; i++) {
        if (funcs[i].name == name) return i + 1;
    }
   // compiled
    for (i = 0; i < compiledfunctions.size(); i++) {
        if (compiledfunctions[i].name == name) return nfunctions + i + 1;
    }
   // stored
    if ((i = lookupUserFunction(s)) != -1) return -i-1;

    return 0;
}

// resolve function-names for compile command
// return value of
//     zero means that function doesn't exist
//     -1 means that function is already builtin
//     -2 means that function is already stored
int funcWord_for_compile(const dpString &s) {
    int i;
   char *n = (char *)(s.c_str());
    char *name = alternateFunctionName(n);

    // built-in function
    for (i = 0; i < nfunctions; i++) {
        if (funcs[i].name == name) return -1;
    }
    // stored function
    if ((i = lookupUserFunction(n)) != -1) return -2;

    // compiled procedure
    for (i = 0; i < compiledprocedures.size(); i++) {
        if (compiledprocedures[i].name == s) return -3;
    }

    return 0;
}

// resolve function-names for store-function command
// return value of
//     zero means that function doesn't exist
//     -1 means that function is already builtin
//     -2 means that function is already copmpiled
int funcWord_for_store(const dpString &s)
{
    int i;
   char *n = (char *)(s.c_str());
    char *name = alternateFunctionName(n);

   // built-in
    for (i = 0; i < nfunctions; i++) {
        if (funcs[i].name == name) return -1;
    }
   // compiled
    for (i = 0; i < compiledfunctions.size(); i++) {
        if (compiledfunctions[i].name == name) return -2;
    }

    return 0;
}

/*!
sin
Returns the trigonometric sine of its argument. The returned value will be real, complex, or a matrix, depending on the argument. By default, the argument is in radians. This can be changed by specifying the switch /deg.
Syntax
result = sin(angle [, /deg])
Arguments
angle: a number (integer, real, complex) or a matrix, specifying the angle for which the sine is to be calculated. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The argument is in degrees
Examples
To find the sine of the angle 90 degrees, type:
<code>print sin(90, /deg)
See also
function_cos
function_tan
category_trigonometry
*/

dpuserType dpuserFunction_sin(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = sin((double)arg.lvalue * M_PI / 180.);
            else rv.dvalue = sin((double)arg.lvalue);
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = sin(arg.dvalue * M_PI / 180.);
            else rv.dvalue = sin(arg.dvalue);
            break;
        case typeCom:
            rv.type = typeCom;
            if (option == "deg") rv.cvalue = CreateComplex(complex_sin(*arg.cvalue * M_PI / 180.));
            else rv.cvalue = CreateComplex(complex_sin(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Sin(option != "deg");
            break;
        default:
            throw dpuserTypeException("sin: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
cos
Returns the trigonometric cosine of its argument. The returned value will be real, complex, or a matrix, depending on the argument. By default, the argument is in radians. This can be changed by specifying the switch /deg.
Syntax
result = cos(angle [, /deg])
Arguments
angle: a number (integer, real, complex) or a matrix, specifying the angle for which the cosine is to be calculated. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The argument is in degrees
Examples
To find the cosine of the angle 90 degrees, type:
<code>print cos(90, /deg)
See also
function_sin
function_tan
category_trigonometry
*/

dpuserType dpuserFunction_cos(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = cos((double)arg.lvalue * M_PI / 180.);
            else rv.dvalue = cos((double)arg.lvalue);
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = cos(arg.dvalue * M_PI / 180.);
            else rv.dvalue = cos(arg.dvalue);
            break;
        case typeCom:
            rv.type = typeCom;
            if (option == "deg") rv.cvalue = CreateComplex(complex_cos(*arg.cvalue * M_PI / 180.));
            else rv.cvalue = CreateComplex(complex_cos(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Cos(option != "deg");
            break;
        default:
            throw dpuserTypeException("cos: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
tan
Returns the trigonometric tangent of its argument. The returned value will be real, complex, or a matrix, depending on the argument. By default, the argument is in radians. This can be changed by specifying the switch /deg.
Syntax
result = tan(angle [, /deg])
Arguments
angle: a number (integer, real, complex) or a matrix, specifying the angle for which the tangent is to be calculated. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The argument is in degrees
Examples
To find the tangent of the angle 45 degrees, type:
<code>print tan(45, /deg)
See also
function_sin
function_cos
category_trigonometry
*/

dpuserType dpuserFunction_tan(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = tan((double)arg.lvalue * M_PI / 180.);
            else rv.dvalue = tan((double)arg.lvalue);
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = tan(arg.dvalue * M_PI / 180.);
            else rv.dvalue = tan(arg.dvalue);
            break;
        case typeCom:
            rv.type = typeCom;
            if (option == "deg") rv.cvalue = CreateComplex(complex_tan(*arg.cvalue * M_PI / 180.));
            else rv.cvalue = CreateComplex(complex_tan(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Tan(option != "deg");
            break;
        default:
            throw dpuserTypeException("tan: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
sinh
Returns the hyperbolic sine of its argument. The returned value will be real, complex, or a matrix, depending on the argument. By default, the argument is in radians. This can be changed by specifying the switch /deg.
Syntax
result = sinh(angle [, /deg])
Arguments
angle: a number (integer, real, complex) or a matrix, specifying the angle for which the hyperbolic sine is to be calculated. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The argument is in degrees
Examples
To find the hyperbolic sine of the angle 90 degrees, type:
<code>print sinh(90, /deg)
See also
function_cosh
function_tanh
category_trigonometry
*/

dpuserType dpuserFunction_sinh(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = sinh((double)arg.lvalue * M_PI / 180.);
            else rv.dvalue = sinh((double)arg.lvalue);
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = sinh(arg.dvalue * M_PI / 180.);
            else rv.dvalue = sinh(arg.dvalue);
            break;
        case typeCom:
            rv.type = typeCom;
            if (option == "deg") rv.cvalue = CreateComplex(complex_sinh(*arg.cvalue * M_PI / 180.));
            else rv.cvalue = CreateComplex(complex_sinh(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Sinh(option != "deg");
            break;
        default:
            throw dpuserTypeException("sinh: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
cosh
Returns the hyperbolic cosine of its argument. The returned value will be real, complex, or a matrix, depending on the argument. By default, the argument is in radians. This can be changed by specifying the switch /deg.
Syntax
result = cosh(angle [, /deg])
Arguments
angle: a number (integer, real, complex) or a matrix, specifying the angle for which the hyperbolic cosine is to be calculated. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The argument is in degrees
Examples
To find the hyperbolic cosine of the angle 90 degrees, type:
<code>print cosh(90, /deg)
See also
function_sinh
function_tanh
category_trigonometry
*/

dpuserType dpuserFunction_cosh(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = cosh((double)arg.lvalue * M_PI / 180.);
            else rv.dvalue = cosh((double)arg.lvalue);
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = cosh(arg.dvalue * M_PI / 180.);
            else rv.dvalue = cosh(arg.dvalue);
            break;
        case typeCom:
            rv.type = typeCom;
            if (option == "deg") rv.cvalue = CreateComplex(complex_cosh(*arg.cvalue * M_PI / 180.));
            else rv.cvalue = CreateComplex(complex_cosh(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Cosh(option != "deg");
            break;
        default:
            throw dpuserTypeException("cosh: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
tanh
Returns the hyperbolic tangent of its argument. The returned value will be real, complex, or a matrix, depending on the argument. By default, the argument is in radians. This can be changed by specifying the switch /deg.
Syntax
result = tanh(angle [, /deg])
Arguments
angle:a number (integer, real, complex) or a matrix, specifying the angle for which the hyperbolic tangent is to be calculated. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The argument is in degrees
Examples
To find the hyperbolic tangent of the angle 45 degrees, type:
<code>print tanh(45, /deg)
See also
function_sinh
function_cosh
category_trigonometry
*/

dpuserType dpuserFunction_tanh(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = tanh((double)arg.lvalue * M_PI / 180.);
            else rv.dvalue = tanh((double)arg.lvalue);
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (option == "deg") rv.dvalue = tanh(arg.dvalue * M_PI / 180.);
            else rv.dvalue = tanh(arg.dvalue);
            break;
        case typeCom:
            rv.type = typeCom;
            if (option == "deg") rv.cvalue = CreateComplex(complex_tanh(*arg.cvalue * M_PI / 180.));
            else rv.cvalue = CreateComplex(complex_tanh(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Tanh(option != "deg");
            break;
        default:
            throw dpuserTypeException("tanh: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
asin
Returns the arcus sine of its argument, which can be either real, complex or a matrix. The returned value's type is changed to complex if necessary (when the absolute value of the argument is greater than 1). By default, the returned value is in radians. This can be changed by specifying the switch /deg.
Syntax
result = asin(X [, /deg])
Arguments
X: a number (integer, real, complex) or a matrix, specifying the sine of the angle to be returned. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The result will be in degrees
Examples
To find the angle (in degrees) whose sine is 0.5, type:
<code>print asin(0.5, /deg)
See also
function_acos
function_atan
category_trigonometry
*/

dpuserType dpuserFunction_asin(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (abs(arg.lvalue) <= 1) {
                rv.dvalue = asin((double)arg.lvalue);
                if (option == "deg") rv.dvalue *= 180. / M_PI;
            } else {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_asin(dpComplex((double)arg.lvalue, 0.0)));
                if (option == "deg") *rv.cvalue *= 180. / M_PI;
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (fabs(arg.dvalue) <= 1.0) {
                rv.dvalue = asin(arg.dvalue);
                if (option == "deg") rv.dvalue *= 180. / M_PI;
            } else {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_asin(dpComplex(arg.dvalue, 0.0)));
                if (option == "deg") *rv.cvalue *= 180. / M_PI;
            }
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_asin(*arg.cvalue));
            if (option == "deg") *rv.cvalue *= 180. / M_PI;
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Asin(option != "deg");
            break;
        default:
            throw dpuserTypeException("asin: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
acos
Returns the arcus cosine of its argument, which can be either real, complex or a matrix. The returned value's type is changed to complex if necessary (when the absolute value of the argument is greater than 1). By default, the returned value is in radians. This can be changed by specifying the switch /deg.
Syntax
result = acos(X [, /deg])
Arguments
X: a number (integer, real, complex) or a matrix, specifying the cosine of the angle to be returned. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The result will be in degrees
Examples
To find the angle (in degrees) whose cosine is 0.5, type:
<code>print acos(0.5, /deg)
See also
function_asin
function_atan
category_trigonometry
*/

dpuserType dpuserFunction_acos(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (abs(arg.lvalue) <= 1) {
                rv.dvalue = acos((double)arg.lvalue);
                if (option == "deg") rv.dvalue *= 180. / M_PI;
            } else {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_acos(dpComplex((double)arg.lvalue, 0.0)));
                if (option == "deg") *rv.cvalue *= 180. / M_PI;
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (fabs(arg.dvalue) <= 1.0) {
                rv.dvalue = acos(arg.dvalue);
                if (option == "deg") rv.dvalue *= 180. / M_PI;
            } else {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_acos(dpComplex(arg.dvalue, 0.0)));
                if (option == "deg") *rv.cvalue *= 180. / M_PI;
            }
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_acos(*arg.cvalue));
            if (option == "deg") *rv.cvalue *= 180. / M_PI;
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Acos(option != "deg");
            break;
        default:
            throw dpuserTypeException("acos: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
atan
Returns the arcus tangent of its argument, which can be either real, complex
or a matrix. The returned value has the same type as the argument. By default, the returned value is in radians. This can be changed by specifying the switch /deg.
Syntax
result = atan(X [, Y] [, /deg])
Arguments
X: a number (integer, real, complex) or a matrix, specifying the tangent of the angle to be returned. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Y: a real number. If given, returns atan(X / Y) for real numbers, quadrant correct.
Switches
/deg: The result will be in degrees
Examples
To find the angle (in degrees) whose tangent is 0.5, type:
<code>print atan(0.5, /deg)
See also
function_asin
function_acos
category_trigonometry
*/

dpuserType dpuserFunction_atan(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            rv.dvalue = atan((double)arg.lvalue);
            if (option == "deg") rv.dvalue *= 180. / M_PI;
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = atan(arg.dvalue);
            if (option == "deg") rv.dvalue *= 180. / M_PI;
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_atan(*arg.cvalue));
            if (option == "deg") *rv.cvalue *= 180. / M_PI;
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Atan(option != "deg");
            break;
        default:
            throw dpuserTypeException("atan: Wrong argument type.");
            break;
    }
    return rv;
}

dpuserType dpuserFunction_atan(dpuserType arg, dpuserType arg2, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (arg2.type == typeCon)
                rv.dvalue = atan2((double)arg.lvalue, (double)arg2.lvalue);
            else if (arg2.type == typeDbl)
                rv.dvalue = atan2((double)arg.lvalue, arg2.dvalue);
            else
                throw dpuserTypeException("atan: Wrong argument type.");
            if (option == "deg") rv.dvalue *= 180. / M_PI;
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (arg2.type == typeCon)
                rv.dvalue = atan2(arg.dvalue, (double)arg2.lvalue);
            else if (arg2.type == typeDbl)
                rv.dvalue = atan2(arg.dvalue, arg2.dvalue);
            else
                throw dpuserTypeException("atan: Wrong argument type.");
            if (option == "deg") rv.dvalue *= 180. / M_PI;
            break;
        case typeFits:
            if (arg2.type == typeFits) {
                rv.type = typeFits;
                rv.clone(arg);
                rv.fvalue->Atan2(*arg2.fvalue, option != "deg");
            } else
                throw dpuserTypeException("atan: Wrong argument type.");
            break;
        default:
            throw dpuserTypeException("atan: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
asinh
Returns the hyperbolic arcus sine of its argument, which can be either real, complex or a matrix. By default, the returned value is in radians. This can be changed by specifying the switch /deg.
Syntax
result = asinh(X [, /deg])
Arguments
X: a number (integer, real, complex) or a matrix, specifying the hyperbolic sine of the angle to be returned. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The result will be in degrees
Examples
To find the angle (in degrees) whose hyperbolic sine is 0.5, type:
<code>print asinh(0.5, /deg)
See also
function_acosh
function_atanh
category_trigonometry
*/

dpuserType dpuserFunction_asinh(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            rv.dvalue = asinh((double)arg.lvalue);
            if (option == "deg") rv.dvalue *= 180. / M_PI;
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = asinh(arg.dvalue);
            if (option == "deg") rv.dvalue *= 180. / M_PI;
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_asinh(*arg.cvalue));
            if (option == "deg") *rv.cvalue *= 180. / M_PI;
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Asinh(option != "deg");
            break;
        default:
            throw dpuserTypeException("asinh: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
acosh
Returns the hyperbolic arcus cosine of its argument, which can be either real, complex or a matrix. By default, the returned value is in radians. This can be changed by specifying the switch /deg.
Syntax
result = acosh(X [, /deg])
Arguments
X: a number (integer, real, complex) or a matrix, specifying the hyperbolic cosine of the angle to be returned. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The result will be in degrees
Examples
To find the angle (in degrees) whose hyperbolic cosine is 0.5, type:
<code>print acosh(0.5, /deg)
See also
function_asinh
function_atanh
category_trigonometry
*/

dpuserType dpuserFunction_acosh(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (arg.lvalue >= 1) {
                rv.dvalue = acosh((double)arg.lvalue);
                if (option == "deg") rv.dvalue *= 180. / M_PI;
            } else {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_acosh(dpComplex((double)arg.lvalue, 0.0)));
                if (option == "deg") *rv.cvalue *= 180. / M_PI;
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (arg.lvalue >= 1.0) {
                rv.dvalue = acosh(arg.dvalue);
                if (option == "deg") rv.dvalue *= 180. / M_PI;
            } else {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_acosh(dpComplex(arg.dvalue, 0.0)));
                if (option == "deg") *rv.cvalue *= 180. / M_PI;
            }
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_acosh(*arg.cvalue));
            if (option == "deg") *rv.cvalue *= 180. / M_PI;
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Acosh(option != "deg");
            break;
        default:
            throw dpuserTypeException("acosh: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
atanh
Returns the hyperbolic arcus tangent of X, which can be either real, complex or a matrix. By default, the returned value is in radians. This can be changed by specifying the switch /deg.
Syntax
result = atanh(X [, /deg])
Arguments
X: a number (integer, real, complex) or a matrix, specifying the hyperbolic tangent of the angle to be returned. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
Switches
/deg: The result will be in degrees
Examples
To find the angle (in degrees) whose hyperbolic tangent is 0.5, type:
<code>print atanh(0.5, /deg)
See also
function_acosh
function_asinh
category_trigonometry
*/

dpuserType dpuserFunction_atanh(dpuserType arg, dpString option) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (arg.lvalue < 1) {
                rv.dvalue = atanh((double)arg.lvalue);
                if (option == "deg") rv.dvalue *= 180. / M_PI;
            } else {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_atanh(dpComplex((double)arg.lvalue, 0.0)));
                if (option == "deg") *rv.cvalue *= 180. / M_PI;
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (arg.lvalue < 1.0) {
                rv.dvalue = atanh(arg.dvalue);
                if (option == "deg") rv.dvalue *= 180. / M_PI;
            } else {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_atanh(dpComplex(arg.dvalue, 0.0)));
                if (option == "deg") *rv.cvalue *= 180. / M_PI;
            }
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_atanh(*arg.cvalue));
            if (option == "deg") *rv.cvalue *= 180. / M_PI;
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Atanh(option != "deg");
            break;
        default:
            throw dpuserTypeException("atanh: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
exp
Returns the exponential function of X. The returned value will be real, complex, or a matrix, depending on the argument. 
Syntax
result = exp(X)
Arguments
X: a number (integer, real, complex) or a matrix. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
See also
function_ln
*/

dpuserType dpuserFunction_exp(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            rv.dvalue = exp((double)arg.lvalue);
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = exp(arg.dvalue);
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_exp(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Exp();
            break;
        default:
            throw dpuserTypeException("exp: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
log
Returns the logarithm of X. If no base is supplied, the logarithm to the base 10 is returned.
Syntax
result = log(X [, base])
Arguments
X: The value whose logarithm is to be returned. This can be a number (integer, real, complex) or a matrix. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
base: If given, the logarithm of X to this base will be returned
See also
function_ln
*/

dpuserType dpuserFunction_log(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (arg.lvalue < 0) {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_log(dpComplex((double)arg.lvalue, 0.0) / log(10.0)));
            } else {
                rv.dvalue = log((double)arg.lvalue) / log(10.0);
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (arg.dvalue < 0.0) {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_log(dpComplex(arg.dvalue, 0.0) / log(10.0)));
            } else {
                rv.dvalue = log(arg.dvalue) / log(10.0);
            }
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_log(*arg.cvalue) / complex_log(dpComplex(10.0)));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.fvalue->Log();
            break;
        default:
            throw dpuserTypeException("log: Wrong argument type.");
            break;
    }
    return rv;
}

dpuserType dpuserFunction_log(dpuserType arg, dpuserType base) {
    dpuserType rv;
    double b;

    if (base.type == typeDbl) b = base.dvalue;
    else if (base.type == typeCon) b = (double)base.lvalue;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (arg.lvalue < 0 || b < 0.0) {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_log(dpComplex((double)arg.lvalue, 0.0) / complex_log(dpComplex(b, 0.0))));
            } else {
                rv.dvalue = log((double)arg.lvalue) / log(b);
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (arg.dvalue < 0.0 || b < 0.0) {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_log(dpComplex(arg.dvalue, 0.0) / complex_log(dpComplex(b, 0.0))));
            } else {
                rv.dvalue = log(arg.dvalue) / log(b);
            }
            break;
        case typeCom:
            rv.type = typeCom;
            if (base.type == typeCom) {
                rv.cvalue = CreateComplex(complex_log(*arg.cvalue) / complex_log(*base.cvalue));
            } else {
                rv.cvalue = CreateComplex(complex_log(*arg.cvalue) / complex_log(dpComplex(b)));
            }
            break;
        case typeFits:
            rv.type = typeFits;
            rv.fvalue->Log(b);
            break;
        default:
            throw dpuserTypeException("log: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
ln
Returns the logarithm of X to the base e (the natural logarithm).
Syntax
result = ln(X)
Arguments
X: The value whose logarithm is to be returned. This can be a number (integer, real, complex) or a matrix. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
See also
function_log
*/

dpuserType dpuserFunction_ln(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (arg.lvalue < 0) {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_log(dpComplex((double)arg.lvalue, 0.0)));
            } else {
                rv.dvalue = log((double)arg.lvalue);
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (arg.dvalue < 0.0) {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_log(dpComplex(arg.dvalue, 0.0)));
            } else {
                rv.dvalue = log(arg.dvalue);
            }
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_log(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.fvalue->Ln();
            break;
        default:
            throw dpuserTypeException("ln: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
sqrt
Returns the square root of X.
Syntax
result = sqrt(X)
Arguments
X: The value whose square root is to be returned. This can be a number (integer, real, complex) or a matrix. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
*/

dpuserType dpuserFunction_sqrt(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            if (arg.lvalue < 0) {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_sqrt(dpComplex((double)arg.lvalue, 0.0)));
            } else {
                rv.dvalue = sqrt((double)arg.lvalue);
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            if (arg.dvalue < 0.0) {
                rv.type = typeCom;
                rv.cvalue = CreateComplex(complex_sqrt(dpComplex(arg.dvalue, 0.0)));
            } else {
                rv.dvalue = sqrt(arg.dvalue);
            }
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(complex_sqrt(*arg.cvalue));
            break;
        case typeFits:
            rv.type = typeFits;
            rv.fvalue->Sqrt();
            break;
        default:
            throw dpuserTypeException("ln: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
erf
Returns the error function of X.
Syntax
result = erf(X)
Arguments
X: A real number.
*/

dpuserType dpuserFunction_erf(dpuserType arg) {
    dpuserType rv;
    double b;

    if (arg.type == typeDbl) b = arg.dvalue;
    else if (arg.type == typeCon) b = (double)arg.lvalue;
    else throw dpuserTypeException("erf: Wrong argument type.");

    rv.type = typeDbl;
    rv.dvalue = gsl_sf_erf(b);

    return rv;
}

/*!
bessel
Returns the bessel function of X.
Syntax
result = bessel(X, kind, order)
Arguments
X: A real number.
kind: An integer number (1 or 2), specifying the kind of the bessel function to be computed.
order: An integer number >= 0, specifying the order of the bessel function to be computed.
*/

dpuserType dpuserFunction_bessel(dpuserType arg, dpuserType kind, dpuserType order) {
    dpuserType rv;
    double b;
    int Order, Kind;

    if (!arg.isReal())
        throw dpuserTypeException("bessel: Wrong argument type.");
    else
        b = arg.toDouble();

    if (kind.type != typeCon || order.type != typeCon)
        throw dpuserTypeException("bessel: Wrong argument type.");

    Kind = kind.lvalue;
    Order = order.lvalue;

    if (Order < 0)
        throw dpuserTypeException("bessel: Order must be >= 0.");
    if (Kind < 1 || Kind > 2)
        throw dpuserTypeException("bessel: Kind must be either 1 or 2.");

    rv.type = typeDbl;
    switch (Kind) {
        case 1: switch (Order) {
            case 0: rv.dvalue = gsl_sf_bessel_J0(b); break;
            case 1: rv.dvalue = gsl_sf_bessel_J1(b); break;
            default: rv.dvalue = gsl_sf_bessel_Jn(Order, b); break;
        }
        break;
        case 2: switch (Order) {
            case 0: rv.dvalue = gsl_sf_bessel_Y0(b); break;
            case 1: rv.dvalue = gsl_sf_bessel_Y0(b); break;
            default: rv.dvalue = gsl_sf_bessel_Yn(Order, b); break;
        }
        break;
        default:
            throw dpuserTypeException("bessel: Wrong argument type.");
        break;
    }
    return rv;
}

/*!
int
Returns the integer part of X.
Syntax
result = int(X)
Arguments
X: a real number.
Examples
To print the integer part of the real number -6.7 which is -6, type:
<code>print int(-6.7)
See also
function_round
*/

dpuserType dpuserFunction_int(dpuserType arg) {
    dpuserType rv;

    rv.type = typeCon;
    if (arg.type == typeStr)
        rv.lvalue = (long)arg.svalue->toDouble();
    else if (arg.isReal())
        rv.lvalue = (long)arg.toDouble();
    else
        throw dpuserTypeException("int: Wrong argument type.");

    return rv;
}

/*!
round
Returns X rounded to the nearest integer.
Syntax
result = round(X)
Arguments
X: a real number.
See also
function_int
*/

dpuserType dpuserFunction_round(dpuserType arg) {
    dpuserType rv;

//	printf("round: arg.type = %i\n", arg.type);

    rv.type = typeCon;
    if (arg.isReal())
        rv.lvalue = (long)(nint(arg.toDouble()));
    else
        throw dpuserTypeException("round: Wrong argument type.");

    return rv;
}

/*!
frac
Returns the fractional part of X.
Syntax
result = frax(X)
Arguments
X: a real number.
*/

dpuserType dpuserFunction_frac(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("frac", arg);

    dpuserType rv;
    double b = arg.toDouble();

    rv.type = typeDbl;
    rv.dvalue = fabs(b - (double)(int)b);

    return rv;
}

/*!
abs
Returns the absolute value of X.
Syntax
result = abs(X)
Arguments
X: The value whose absolute value is to be returned. This can be a number (integer, real, complex) or a matrix. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
See also
function_sign
function_arg
function_real
function_imag
*/

dpuserType dpuserFunction_abs(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeCon;
            rv.lvalue = abs(arg.lvalue);
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = fabs(arg.dvalue);
            break;
        case typeCom:
            rv.type = typeDbl;
            rv.dvalue = complex_abs(*arg.cvalue);
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->Abs();
            break;
        default:
            throw dpuserTypeException("abs: Wrong argument type.");
    }

    return rv;
}

/*!
sign
Returns the sign of X.
Syntax
result = sign(X)
Arguments
X: a real number.
See also
function_abs
*/

dpuserType dpuserFunction_sign(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("sign", arg);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = (arg.toDouble() < 0.0 ? -1 : 1);

    return rv;
}

/*!
rad2deg
Returns X in radians converted to degrees. This is equivalent to X*180/pi.
Syntax
result = rad2deg(X)
Arguments
X: The value which is to be converted to degrees. This can be any number (integer, real, complex) or a matrix.
See also
function_deg2rad
category_trigonometry
*/

dpuserType dpuserFunction_rad2deg(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            rv.dvalue = arg.lvalue * 180.0 / M_PI;
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = arg.dvalue * 180.0 / M_PI;
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(*arg.cvalue * 180.0 / M_PI);
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            *rv.fvalue *= 180.0 / M_PI;
            break;
        default:
            throw dpuserTypeException("rad2deg: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
deg2rad
Returns X in degrees converted to radians. This is equivalent to X*pi/180.
Syntax
result = deg2rad(X)
Arguments
X: The value which is to be converted to radians. This can be any number (integer, real, complex) or a matrix.
See also
function_rad2deg
category_trigonometry
*/

dpuserType dpuserFunction_deg2rad(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeDbl;
            rv.dvalue = arg.lvalue * M_PI / 180.0;
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = arg.dvalue * M_PI / 180.0;
            break;
        case typeCom:
            rv.type = typeCom;
            rv.cvalue = CreateComplex(*arg.cvalue * M_PI / 180.0);
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            *rv.fvalue *= M_PI / 180.0;
            break;
        default:
            throw dpuserTypeException("deg2rad: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
float
Returns the string X converted to a real number.
Syntax
result = float(X)
Arguments
X: A string.
*/

dpuserType dpuserFunction_float(dpuserType arg) {
    dpuserType rv;

    rv.type = typeDbl;
    switch (arg.type) {
        case typeCon:
            rv.dvalue = (double)arg.lvalue;
            break;
        case typeDbl:
            rv.dvalue = arg.dvalue;
            break;
        case typeCom:
            rv.dvalue = real(*arg.cvalue);
            break;
        case typeStr:
            rv.dvalue = atof(arg.svalue->c_str());
            break;
        default:
            throw dpuserTypeException("float: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
strlen
Returns the length of the string X.
Syntax
result = strlen(X)
Arguments
X: A string.
See also
function_nelements
*/

dpuserType dpuserFunction_strlen(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("strlen", arg);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = arg.svalue->length();

    return rv;
}

/*!
min
Returns the minimum value of an array or the minimum of two real numbers. Undefined values (INF and NaN) are ignored.
Syntax
result = min(X [, ignore] [,/x | /y | /z | /xy | /xz | /yz])<br>
result = min(a, b)
Arguments
X: A matrix.
ignore: An optional value which should be ignored.
a: A real number.
b: A real number
Switches
/x: Calculate the minimum along the first axis
/y: Calculate the minimum along the second axis
/z: Calculate the minimum along the third axis
/xy: Calculate the minimum along the first and second axes
/xz: Calculate the minimum along the first and third axes
/yz: Calculate the minimum along the second and third axes
Examples
To get a 1D vector of the minima of all slices along a 3D cube:
<code>result = min(cube, /xy)
See also
function_max
function_avg
function_total
function_median
function_stddev
function_meddev
function_variance
*/

dpuserType dpuserFunction_min(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeCon;
            rv.lvalue = arg.lvalue;
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = arg.dvalue;
            break;
        case typeFits:
            rv.type = typeDbl;
            rv.dvalue = dp_min(*arg.fvalue);
            break;
        default:
            throw dpuserTypeException("min: Wrong argument type.");
            break;
    }
    return rv;
}

dpuserType dpuserFunction_min(dpuserType arg, dpuserType arg1) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            if (arg.type == typeCon) {
                rv.type = typeCon;
                rv.lvalue = arg.lvalue < arg1.lvalue ? arg.lvalue : arg1.lvalue;
            } else {
                rv.type = typeDbl;
                rv.dvalue = arg.lvalue < arg1.dvalue ? (double)arg.lvalue : arg1.dvalue;
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = arg.dvalue < arg1.toDouble() ? arg.dvalue : arg1.toDouble();
            break;
        case typeFits:
            rv.type = typeDbl;
            rv.dvalue = dp_min_i(*arg.fvalue, arg1.toDouble());
            break;
        default:
            throw dpuserTypeException("min: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
max
Returns the maximum value of an array or the maximum of two real numbers. Undefined values (INF and NaN) are ignored.
Syntax
result = max(X [, ignore] [,/x | /y | /z | /xy | /xz | /yz])<br>
result = max(a, b)
Arguments
X: A matrix.
ignore: An optional value which should be ignored.
a: A real number.
b: A real number.
Switches
/x: Calculate the maximum along the first axis
/y: Calculate the maximum along the second axis
/z: Calculate the maximum along the third axis
/xy: Calculate the maximum along the first and second axes
/xz: Calculate the maximum along the first and third axes
/yz: Calculate the maximum along the second and third axes
Examples
To get a 1D vector of the maxima of all slices along a 3D cube:
<code>result = max(cube, /xy)
See also
function_min
function_avg
function_total
function_median
function_stddev
function_meddev
function_variance
*/

dpuserType dpuserFunction_max(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeCon;
            rv.lvalue = arg.lvalue;
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = arg.dvalue;
            break;
        case typeFits:
            rv.type = typeDbl;
            rv.dvalue = dp_max(*arg.fvalue);
            break;
        default:
            throw dpuserTypeException("max: Wrong argument type.");
            break;
    }
    return rv;
}

dpuserType dpuserFunction_max(dpuserType arg, dpuserType arg1) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            if (arg.type == typeCon) {
                rv.type = typeCon;
                rv.lvalue = arg.lvalue > arg1.lvalue ? arg.lvalue : arg1.lvalue;
            } else {
                rv.type = typeDbl;
                rv.dvalue = arg.lvalue > arg1.dvalue ? (double)arg.lvalue : arg1.dvalue;
            }
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = arg.dvalue > arg1.toDouble() ? arg.dvalue : arg1.toDouble();
            break;
        case typeFits:
            rv.type = typeDbl;
            rv.dvalue = dp_max_i(*arg.fvalue, arg1.toDouble());
            break;
        default:
            throw dpuserTypeException("max: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
avg
Returns the average value of X, optionally omitting a value. Undefined values (INF and NaN) are ignored.
Syntax
result = avg(X [, omit [,/x | /y | /z | /xy | /xz | /yz]])
Arguments
X: A matrix.
omit: A value to be omitted when taking the average.
Switches
/x: Calculate the average along the first axis
/y: Calculate the average along the second axis
/z: Calculate the average along the third axis
/xy: Calculate the average along the first and second axes
/xz: Calculate the average along the first and third axes
/yz: Calculate the average along the second and third axes
Examples
Find the average of the values 0,1,2,3,4 (which is 2):
<code>print avg([0:4])<br>
Calculate an average image of a 3D cube:
<code>result = avg(cube, /z)<br>
This is the same as:
<code>result = cubeavg(cube)
See also
function_max
function_min
function_total
function_median
function_stddev
function_meddev
function_cubeavg
function_variance
*/

dpuserType dpuserFunction_avg(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("avg", arg);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = dp_avg(*arg.fvalue);

    return rv;
}

dpuserType dpuserFunction_avg(dpuserType arg, dpuserType arg1) {
    Dpuser2cUtils::checkFuncArgs("avg", arg, arg1);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = dp_avg_i(*arg.fvalue, arg1.toDouble());

    return rv;
}

/*!
total
Returns the sum of all values in X. Undefined values (INF and NaN) are ignored.
Syntax
result = total(X [, ignore] [,/x | /y | /z | /xy | /xz | /yz])
Arguments
X: A matrix.
ignore: An optional value which should be ignored.
Switches
/x: Calculate the sum along the first axis
/y: Calculate the sum along the second axis
/z: Calculate the sum along the third axis
/xy: Calculate the sum along the first and second axes
/xz: Calculate the sum along the first and third axes
/yz: Calculate the sum along the second and third axes
Examples
Calculate a sum image of a 3D cube:
<code>result = total(cube, /z)
See also
function_max
function_min
function_avg
function_median
function_stddev
function_meddev
*/

dpuserType dpuserFunction_total(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("total", arg);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = dp_total(*arg.fvalue);

    return rv;
}

dpuserType dpuserFunction_total(dpuserType arg, dpuserType arg1) {
    Dpuser2cUtils::checkFuncArgs("total", arg, arg1);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = dp_total_i(*arg.fvalue, arg1.toDouble());

    return rv;
}

/*!
median
Returns the median value of X, optionally neglecting a value.
Syntax
result = median(X [, omit] [,/x | /y | /z | /xy | /xz | /yz])
Arguments
X: A matrix.
omit: A value which should be omitted when taking the median.
Switches
/x: Calculate the sum along the first axis
/y: Calculate the sum along the second axis
/z: Calculate the sum along the third axis
/xy: Calculate the sum along the first and second axes
/xz: Calculate the sum along the first and third axes
/yz: Calculate the sum along the second and third axes
Examples
Calculate a median image of a 3D cube:
<code>result = median(cube, /z)<br>
This is the same as:
<code>result = cubemedian(cube)
See also
function_max
function_min
function_total
function_avg
function_stddev
function_meddev
function_cubemedian
*/

dpuserType dpuserFunction_median(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("median", arg);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = arg.fvalue->get_median();

    return rv;
}

dpuserType dpuserFunction_median(dpuserType arg, dpuserType arg1) {
    Dpuser2cUtils::checkFuncArgs("median", arg, arg1);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = arg.fvalue->get_median(arg1.toDouble());

    return rv;
}

/*!
naxis
Returns the number of axes in X.
Syntax
result = naxis(X [, extension])
Arguments
X: A matrix or a file name
extension: Integer number specifying which extension to read.
See also
function_nelements
function_naxis1
function_naxis2
function_naxis3
*/

dpuserType dpuserFunction_naxis(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("naxis", arg);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = arg.fvalue->Naxis(0);

    return rv;
}

/*!
naxis1
Returns the length of axis #1 in X.
Syntax
result = naxis1(X [, extension])
Arguments
X: A matrix or a file name
extension: Integer number specifying which extension to read.
See also
function_nelements
function_naxis
function_naxis2
function_naxis3
*/

dpuserType dpuserFunction_naxis1(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("naxis1", arg);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = arg.fvalue->Naxis(1);

    return rv;
}

/*!
naxis2
Returns the length of axis #2 in X.
Syntax
result = naxis2(X [, extension])
Arguments
X: A matrix or a file name
extension: Integer number specifying which extension to read.
See also
function_nelements
function_naxis
function_naxis1
function_naxis3
*/

dpuserType dpuserFunction_naxis2(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("naxis2", arg);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = arg.fvalue->Naxis(2);

    return rv;
}

/*!
naxis3
Returns the length of axis #3 in X.
Syntax
result = naxis3(X [, extension])
Arguments
X: A matrix or a file name
extension: Integer number specifying which extension to read.
See also
function_nelements
function_naxis
function_naxis1
function_naxis2
*/

dpuserType dpuserFunction_naxis3(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("naxis3", arg);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = arg.fvalue->Naxis(3);

    return rv;
}

/*!
xmax
Returns the x coordinate of the maximum in X.
Syntax
result = xmax(X)
Arguments
X: A matrix.
See also
function_ymax
function_xmin
function_ymin
function_xcen
function_ycen
function_maxima
function_centroids
procedure_centroid
*/

dpuserType dpuserFunction_xmax(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("xmax", arg);

    dpuserType rv;
    int x, y;

    rv.type = typeCon;
    arg.fvalue->max_pos(&x, &y);
    rv.lvalue = x;

    return rv;
}

/*!
ymax
Returns the y coordinate of the maximum in X.
Syntax
result = ymax(X)
Arguments
X: A matrix.
See also
function_xmax
function_xmin
function_ymin
function_xcen
function_ycen
function_maxima
function_centroids
procedure_centroid
*/

dpuserType dpuserFunction_ymax(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("ymax", arg);

    dpuserType rv;
    int x, y;

    rv.type = typeCon;
    arg.fvalue->max_pos(&x, &y);
    rv.lvalue = y;

    return rv;
}

/*!
xmin
Returns the x coordinate of the minimum in X.
Syntax
result = xmin(X)
Arguments
X: A matrix.
See also
function_ymin
function_xmax
function_ymax
function_xcen
function_ycen
function_maxima
function_centroids
procedure_centroid
*/

dpuserType dpuserFunction_xmin(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("xmin", arg);

    dpuserType rv;
    int x, y;

    rv.type = typeCon;
    arg.fvalue->min_pos(&x, &y);
    rv.lvalue = x;

    return rv;
}

/*!
ymin
Returns the y coordinate of the minimum in X.
Syntax
result = ymin(X)
Arguments
X: A matrix.
See also
function_xmin
function_xmax
function_ymax
function_xcen
function_ycen
function_maxima
function_centroids
procedure_centroid
*/

dpuserType dpuserFunction_ymin(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("ymin", arg);

    dpuserType rv;
    int x, y;

    rv.type = typeCon;
    arg.fvalue->min_pos(&x, &y);
    rv.lvalue = y;

    return rv;
}

/*!
xcen
Returns the x coordinate of the centroid in X.
Syntax
result = xcen(X)
Arguments
X: A matrix.
See also
function_xmax
function_ymax
function_xmin
function_ymin
function_ycen
function_maxima
function_centroids
procedure_centroid
*/

dpuserType dpuserFunction_xcen(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("xcen", arg);

    dpuserType rv;
    double x, y;

    rv.type = typeDbl;
    arg.fvalue->centroid(&x, &y);
    rv.dvalue = x;

    return rv;
}

/*!
ycen
Returns the y coordinate of the centroid in X.
Syntax
result = ycen(X)
Arguments
X: A matrix.
See also
function_xmax
function_ymax
function_xmin
function_ymin
function_xcen
function_maxima
function_centroids
procedure_centroid
*/

dpuserType dpuserFunction_ycen(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("ycen", arg);

    dpuserType rv;
    double x, y;

    rv.type = typeDbl;
    arg.fvalue->centroid(&x, &y);
    rv.dvalue = y;

    return rv;
}

/*!
real
Returns the real part of the complex number X.
Syntax
result = real(X)
Arguments
X: The value whose real part is to be returned. This can be a number (integer, real, complex) or a matrix. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
See also
function_imag
function_arg
function_abs
function_complex
function_conj
*/

dpuserType dpuserFunction_real(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.clone(arg);
            break;
        case typeDbl:
            rv.clone(arg);
            break;
        case typeCom:
            rv.type = typeDbl;
            rv.dvalue = arg.cvalue->real();
            break;
        case typeFits:
            rv.clone(arg);
            rv.fvalue->ri_r();
            break;
        default:
            throw dpuserTypeException("real: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
imag
Returns the imaginary part of the complex number X. If X is not a complex number, 0 is returned.
Syntax
result = imag(X)
Arguments
X: The value whose imaginary part is to be returned. This can be a number (integer, real, complex) or a matrix. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
See also
function_real
function_arg
function_abs
function_complex
function_conj
*/

dpuserType dpuserFunction_imag(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
            rv.type = typeCon;
            rv.lvalue = 0;
            break;
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = 0.0;
            break;
        case typeCom:
            rv.type = typeDbl;
            rv.dvalue = arg.cvalue->imag();
            break;
        case typeFits:
            rv.clone(arg);
            rv.fvalue->ri_i();
            break;
        default:
            throw dpuserTypeException("imag: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
arg
Returns the argument of the complex number X (the angle of the polar representation of a complex number).
Syntax
result = arg(X)
Arguments
X: The value whose imaginary part is to be returned. This can be a number (integer, real, complex) or a matrix. If X is a matrix, a matrix of the same size is returned where each element represents the result of the operation.
See also
function_real
function_imag
function_abs
function_complex
function_conj
*/

dpuserType dpuserFunction_arg(dpuserType arg) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon:
        case typeDbl:
            rv.type = typeDbl;
            rv.dvalue = complex_arg(dpComplex(arg.toDouble()));
            break;
        case typeCom:
            rv.type = typeDbl;
            rv.dvalue = complex_arg(*arg.cvalue);
            break;
        case typeFits:
            rv.clone(arg);
            rv.fvalue->ri_arg();
            break;
        default:
            throw dpuserTypeException("arg: Wrong argument type.");
            break;
    }
    return rv;
}

/*!
jd
Returns the Julian Date of given date. Hour, minute, and second default to 0.
Syntax
result = jd(day, month, year [, hour [, minute [,second]]])
Arguments
day: An integer number.
month: An integer number. 
year: An integer number.
hour: An integer number.
minute: An integer number.
second: A real number.
See also
function_jdnumber
function_jdfraction
function_calday
function_now
*/

dpuserType dpuserFunction_jd(dpuserType day, dpuserType month, dpuserType year) {
    Dpuser2cUtils::checkFuncArgs("jd", day, month, year);

    dpuserType rv;
    rv.type = typeDbl;
    CJulianDay jd;
    jd.SetJD(day.lvalue, month.lvalue, year.lvalue, 0, 0, 0.0);
    rv.dvalue = (double)jd;

    return rv;
}

dpuserType dpuserFunction_jd(dpuserType day, dpuserType month, dpuserType year, dpuserType hour) {
    Dpuser2cUtils::checkFuncArgs("jd", day, month, year, hour);

    dpuserType rv;
    rv.type = typeDbl;
    CJulianDay jd;
    jd.SetJD(day.lvalue, month.lvalue, year.lvalue, hour.lvalue, 0, 0.0);
    rv.dvalue = (double)jd;

    return rv;
}

dpuserType dpuserFunction_jd(dpuserType day, dpuserType month, dpuserType year, dpuserType hour, dpuserType minute) {
    Dpuser2cUtils::checkFuncArgs("jd", day, month, year, hour, minute);

    dpuserType rv;
    rv.type = typeDbl;
    CJulianDay jd;
    jd.SetJD(day.lvalue, month.lvalue, year.lvalue, hour.lvalue, minute.lvalue, 0.0);
    rv.dvalue = (double)jd;

    return rv;
}

dpuserType dpuserFunction_jd(dpuserType day, dpuserType month, dpuserType year, dpuserType hour, dpuserType minute, dpuserType second) {
    Dpuser2cUtils::checkFuncArgs("jd", day, month, year, hour, minute, second);

    dpuserType rv;
    rv.type = typeDbl;
    CJulianDay jd;
    jd.SetJD(day.lvalue, month.lvalue, year.lvalue, hour.lvalue, minute.lvalue, second.toDouble());
    rv.dvalue = (double)jd;

    return rv;
}

//Alex
/*!
jdnumber
Returns the integer part of the Julian Date of given date. Hour, minute, and second default to 0.
Syntax
result = jdnumber(day, month, year [, hour [, minute [,second]]])
Arguments
day: An integer number.
month: An integer number. 
year: An integer number.
hour: An integer number.
minute: An integer number.
second: A real number.
See also
function_jd
function_jdfraction
function_calday
function_now
*/

dpuserType dpuserFunction_jdnumber(dpuserType day, dpuserType month, dpuserType year) {
    dpuserType hour;
    hour.type = typeCon;
    hour.lvalue = 0;

    return dpuserFunction_jdnumber(day, month, year, hour);
}

//Alex

dpuserType dpuserFunction_jdnumber(dpuserType day, dpuserType month, dpuserType year, dpuserType hour) {
    dpuserType min;
    min.type = typeCon;
    min.lvalue = 0;

    return dpuserFunction_jdnumber(day, month, year, hour, min);
}

//Alex

dpuserType dpuserFunction_jdnumber(dpuserType day, dpuserType month, dpuserType year, dpuserType hour, dpuserType minute) {
    dpuserType sec;
    sec.type = typeDbl;
    sec.dvalue = 0.0;

    return dpuserFunction_jdnumber(day, month, year, hour, minute, sec);
}

//Alex

dpuserType dpuserFunction_jdnumber(dpuserType day, dpuserType month, dpuserType year, dpuserType hour, dpuserType minute, dpuserType second) {
    Dpuser2cUtils::checkFuncArgs("jdnumber", day, month, year, hour, minute, second);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = convertToJulianDay(day, month, year, hour, minute, second).GetNumber();

    return rv;
}

//Alex
/*!
jdfraction
Returns the fractional part of the Julian Date of given date. Hour, minute, and second default to 0.
Syntax
result = jdfraction(day, month, year [, hour [, minute [,second]]])
Arguments
day: An integer number.
month: An integer number. 
year: An integer number.
hour: An integer number.
minute: An integer number.
second: A real number.
See also
function_jd
function_jdnumber
function_calday
function_now
*/

dpuserType dpuserFunction_jdfraction(dpuserType day, dpuserType month, dpuserType year) {
    dpuserType hour;
    hour.type = typeCon;
    hour.lvalue = 0;

    return dpuserFunction_jdfraction(day, month, year, hour);
}

//Alex

dpuserType dpuserFunction_jdfraction(dpuserType day, dpuserType month, dpuserType year, dpuserType hour) {
    dpuserType min;
    min.type = typeCon;
    min.lvalue = 0;

    return dpuserFunction_jdfraction(day, month, year, hour, min);
}

//Alex

dpuserType dpuserFunction_jdfraction(dpuserType day, dpuserType month, dpuserType year, dpuserType hour, dpuserType minute) {
    dpuserType sec;
    sec.type = typeDbl;
    sec.dvalue = 0.0;

    return dpuserFunction_jdfraction(day, month, year, hour, minute, sec);
}

//Alex

dpuserType dpuserFunction_jdfraction(dpuserType day, dpuserType month, dpuserType year, dpuserType hour, dpuserType minute, dpuserType second) {
    Dpuser2cUtils::checkFuncArgs("jdfraction", day, month, year, hour, minute, second);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = convertToJulianDay(day, month, year, hour, minute, second).GetFraction();

    return rv;
}

/*!
upper
Converts the string X to upper case.
Syntax
result = upper(X)
Arguments
X: A string.
See also
function_lower
procedure_upper
*/

dpuserType dpuserFunction_upper(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("upper", arg);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString(arg.svalue->upper());

    return rv;
}

/*!
lower
Converts the string X to lower case.
Syntax
result = lower(X)
Arguments
X: A string.
See also
function_upper
procedure_lower
*/

dpuserType dpuserFunction_lower(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("lower", arg);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString(arg.svalue->lower());

    return rv;
}

// Alex
/*!
string
Converts the number X to a string.
Syntax
result = string(X [,/time] [,/deg] [,/rad]
Arguments
X: Any number (integer, real, complex).
Switches
/time: Convert to hours, minutes, and seconds
/deg: Convert to degrees, arcminutes, and arcseconds
/rad: Same as /deg, but the argument is converted to degrees
*/

dpuserType dpuserFunction_string(dpuserType number) {
    Dpuser2cUtils::checkFuncArgs("string", number);

    dpuserType rv;
    rv.type = typeStr;

    if ((number.type == typeDbl) && ((double)number.lvalue == number.dvalue)) {
        rv.svalue = CreateString(number.lvalue);
    } else if (number.type == typeDbl) {
        rv.svalue = CreateString(number.dvalue);
    } else if (number.type == typeStr) {
        rv.svalue = CreateString(*number.svalue);
    } else {
        rv.svalue = CreateString(*number.cvalue);
    }
    return rv;
}

// Alex

dpuserType dpuserFunction_string(dpuserType number, dpString option) {
    Dpuser2cUtils::checkFuncArgs("string", number);

    dpuserType rv;
    rv.type = typeStr;
    if (!option.isEmpty() && (number.type != typeCom)) {
        double value = number.dvalue;
        int hour, minute;
        double seconds;

        if (option == "rad") value *= 180.0 / M_PI;
        seconds = value;
        hour = (int)seconds;
        seconds = (seconds - hour) * 60.0;
        minute = (int)seconds;
        seconds = (seconds - minute) * 60.0;
        rv.svalue = CreateString();
        if (option == "time") {
            rv.svalue->sprintf("%02i:%02i:%06.3f", hour, minute, seconds);
        } else if ((option == "deg") || (option == "rad")) {
            rv.svalue->sprintf("%id%02i'%06.3f\"", hour, minute, seconds);
        }
    } else {
        rv = dpuserFunction_string(number);
    }
    return rv;
}

//Alex
/*!
calday
Returns a nicely formatted string telling the calendar date of given Julian Date JD. If JD_fraction is nonzero, it describes the fractional part.
Syntax
result = calday(JD, JD_fraction)
Arguments
JD: The julian date
JD_fraction: If nonzero, the calendar date for JD+JD_fraction will be returned
Switches
/mjd: The date given is a Modified Julian Date
See also
function_jd
function_now
*/

dpuserType dpuserFunction_calday(dpuserType jd) {
    Dpuser2cUtils::checkFuncArgs("calday", jd);

    char _tmp[256];
    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString(CJulianDay(jd.dvalue).GetUTCString(_tmp, 255));
    return rv;
}

//Alex

dpuserType dpuserFunction_calday(dpuserType jd, dpuserType jd_frac) {
    Dpuser2cUtils::checkFuncArgs("calday", jd, jd_frac);

    char _tmp[256];
    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString(CJulianDay((USHORT)jd.dvalue, jd_frac.lvalue).GetUTCString(_tmp, 255));

    return rv;
}

// Alex
/*!
header
Returns the FITS header of X.
Syntax
result = header(X [, extension])
Arguments
X: A matrix or a file name
extension: Integer number specifying which extension to read.
*/

dpuserType dpuserFunction_header(dpuserType X) {
    dpuserType extension;
    extension.type = typeCon;
    extension.lvalue = 0;	// extension = 0 is default

    return dpuserFunction_header(X, extension);
}

// Alex

dpuserType dpuserFunction_header(dpuserType X, dpuserType extension) {
    Dpuser2cUtils::checkFuncArgs("header", X, extension);

    dpuserType rv;
    rv.type = typeStr;
    if (X.type == typeFitsFile) {
        X.fvalue = CreateFits();
        if (X.fvalue->OpenFITS(X.ffvalue->c_str())) {
            if (extension < 1) {
                if (X.fvalue->ReadFitsHeader() >= 0) {
                    X.fvalue->CloseFITS();
                }
            } else {
                if (X.fvalue->ReadFitsExtensionHeader(extension.lvalue) >= 0) {
                    X.fvalue->CloseFITS();
                }
            }
        }
        rv.svalue = CreateString(X.fvalue->GetHeader());
    } else if (X.type == typeFits) {
        X.fvalue->updateHeader();
        rv.svalue = CreateString(X.fvalue->GetHeader());
    }
    return rv;
}

//Alex
/*!
dayofweek
Returns the day of the week of specified (julian) date.
Syntax
result = dayofweek(DAY, MONTH, YEAR)<br>
result = dayofweek(JD [,JD_fraction])
Arguments
DAY: An integer number.
MONTH: An integer number.
YEAR: An integer number.
JD: Julian Date.
JD_fraction: A real number.
Examples
A person who was born an Feb 14, 1970 was born on a Saturday:
<code>print dayofweek(14,2,1970)
See also
function_calday
function_jd
*/

dpuserType dpuserFunction_dayofweek(dpuserType jd) {
    Dpuser2cUtils::checkFuncArgs("dayofweek", jd);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString(CJulianDay(jd.dvalue).GetDayOfWeekString());
    return rv;
}

//Alex

dpuserType dpuserFunction_dayofweek(dpuserType jd, dpuserType jd_frac) {
    Dpuser2cUtils::checkFuncArgs("dayofweek", jd, jd_frac);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString(CJulianDay((long)jd.dvalue, jd_frac.dvalue).GetDayOfWeekString());
    return rv;
}

//Alex

dpuserType dpuserFunction_dayofweek(dpuserType day, dpuserType month, dpuserType year) {
    Dpuser2cUtils::checkFuncArgs("dayofweek", day, month, year);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString(CJulianDay((USHORT)day.dvalue, (USHORT)month.dvalue, year.lvalue).GetDayOfWeekString());

    return rv;
}

/*!
fits
Creates an array (or a matrix) of size X x Y x Z. The arguments Y and Z are optional. The resulting matrix will be of type R4 (single precision).
Syntax
result = fits(X [, Y [, Z]])
Arguments
X: An integer number giving the length of the first dimension.
Y: An integer number giving the length of the second dimension.
Z: An integer number giving the length of the third dimension.
See also
function_bytearray
function_shortarray
function_longarray
function_floatarray
function_doublearray
function_complexarray
*/

dpuserType dpuserFunction_fits(dpuserType x) {
    dpuserType y;
    y.type = typeCon;
    y.lvalue = 1;

    return dpuserFunction_fits(x, y);
}

dpuserType dpuserFunction_fits(dpuserType x, dpuserType y) {
    dpuserType z;
    z.type = typeCon;
    z.lvalue = 1;

    return dpuserFunction_fits(x, y, z);
}

dpuserType dpuserFunction_fits(dpuserType x, dpuserType y, dpuserType z) {
    Dpuser2cUtils::checkFuncArgs("fits", x, y, z);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->create(x.lvalue, y.lvalue, z.lvalue);

    return rv;
}

/*!
gauss
Creates an elliptical Gaussian. This is defined by respective FWHM1 and FWHM2, axis position angle (in degrees) and Gaussian center position (x, y) in the array. Optional keywords are naxis1, naxis2 which determine the size of the array. If not set, the size of the array is determined from global variables. If only one argument is given, return the value of a gauss as determined from exp(-4ln(2)*x^2).
Syntax
result = gauss(X0)<br>
result = gauss(X, Y, FWHM1 [, FWHM2 [, ANGLE]] [, naxis1=value] [, naxis2 = value])
Arguments
X0: A 1D vector or single number where to evaluate the function exp(-4ln(2)*X^2)
X: The center of the gaussian in the x-axes
Y: The center of the gaussian in the y-axes
FWHM1: Full-width at half maximum of the major axes
FWHM2: Optional, full-width at half maximum of the minor axes
ANGLE: Optional, position angle of the gaussian
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
See also
function_quickgauss
*/

dpuserType dpuserFunction_gauss(dpuserType x) {
    dpuserType rv;
    rv.clone(x);

    if (rv.type == typeDbl) {
        rv.dvalue = exp(-FOUR_LN2 * rv.dvalue * rv.dvalue);
    } else if (rv.type == typeFits) {
        rv.fvalue->setType(R8);
        for (long n = 0; n < rv.fvalue->Nelements(); n++) rv.fvalue->r8data[n] = exp(-FOUR_LN2 * rv.fvalue->r8data[n] * rv.fvalue->r8data[n]);
    }

    return rv;
}

// Alex

dpuserType dpuserFunction_gauss(dpuserType x, dpuserType y, dpuserType fwhm1, dpuserType naxis1, dpuserType naxis2) {
    dpuserType fwhm2;
    fwhm2.type = typeDbl;
    fwhm2.dvalue = -1.0;	// default value of ellipse() in fits.h

    return dpuserFunction_gauss(x, y, fwhm1, fwhm2, naxis1, naxis2);
}

// Alex

dpuserType dpuserFunction_gauss(dpuserType x, dpuserType y, dpuserType fwhm1, dpuserType fwhm2, dpuserType naxis1, dpuserType naxis2) {
    dpuserType angle;
    angle.type = typeDbl;
    angle.dvalue = 0.0;	// default value of ellipse() in fits.h

    return dpuserFunction_gauss(x, y, fwhm1, fwhm2, angle, naxis1, naxis2);
}

// Alex

dpuserType dpuserFunction_gauss(dpuserType x, dpuserType y, dpuserType fwhm1, dpuserType fwhm2, dpuserType angle, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("gauss", x, y, fwhm1, fwhm2, angle, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->gauss(naxis1.toInt(), naxis2.toInt(), x.toFloat(), y.toFloat(), fwhm1.toFloat(), fwhm2.toFloat(), angle.toFloat());

    return rv;
}

// Alex
/*!
circle
Creates a circle centered on X, Y with the respective radius. Optional keywords are naxis1, naxis2 which determine the size of the array. If not set, the size of the array is determined from global variables.
Syntax
result = circle(X, Y, RADIUS [, naxis1=value] [, naxis2 = value])
Arguments
X: The center of the circle in the x-axes
Y: The center of the circle in the y-axes
RADIUS: The radius of the circle
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
See also
function_ellipse
*/

dpuserType dpuserFunction_circle(dpuserType x, dpuserType y, dpuserType radius, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("circle", x, y, radius, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->circle(naxis1.toInt(), naxis2.toInt(), x.toFloat(), y.toFloat(), radius.toFloat());

    return rv;
}

// Alex
/*!
ellipse
Creates an ellipse. This is defined by respective RADIUS1 and RADIUS2, axis position angle (in degrees) and Ellipse center position (x, y) in the array. Optional keywords are naxis1, naxis2 which determine the size of the array. If not set, the size of the array is determined from global variables.
Syntax
result = ellipse(X, Y, RADIUS1 [, RADIUS2 [, ANGLE]] [, naxis1=value] [, naxis2 = value])
Arguments
X: The center of the ellipse in the x-axes
Y: The center of the ellipse in the y-axes
RADIUS1: Radius of the major axes
RADIUS2: Optional, radius of the minor axes
ANGLE: Optional, position angle of the ellipse
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
See also
function_circle
*/

dpuserType dpuserFunction_ellipse(dpuserType x, dpuserType y, dpuserType radius1, dpuserType naxis1, dpuserType naxis2) {
    return dpuserFunction_ellipse(x, y, radius1, radius1, naxis1, naxis2);
}

// Alex

dpuserType dpuserFunction_ellipse(dpuserType x, dpuserType y, dpuserType radius1, dpuserType radius2, dpuserType naxis1, dpuserType naxis2) {
    dpuserType angle;
    angle.type = typeDbl;
    angle.dvalue = 0.0;	// default value of ellipse() in fits.h

    return dpuserFunction_ellipse(x, y, radius1, radius2, angle, naxis1, naxis2);
}

// Alex

dpuserType dpuserFunction_ellipse(dpuserType x, dpuserType y, dpuserType radius1, dpuserType radius2, dpuserType angle, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("ellipse", x, y, radius1, radius2, angle, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->ellipse(naxis1.toInt(), naxis2.toInt(), x.toFloat(), y.toFloat(), radius1.toFloat(), radius2.toFloat(), angle.toFloat());

    return rv;
}

// Alex
/*!
rect
Creates a rectangular filter. This is defined by: Respective width and height and center position (X, Y) in the array. Optional keywords are naxis1, naxis2 which determine the size of the array. If not set, the size of the array is determined from global variables.
Syntax
result = rect(X, Y, WIDTH, HEIGHT [, naxis1=value] [, naxis2 = value])
Arguments
X: The center of the rectangle in the x-axes
Y: The center of the rectangle in the y-axes
WIDTH: The width of the rectangle
HEIGHT: the height of the rectangle
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
*/

dpuserType dpuserFunction_rect(dpuserType x, dpuserType y, dpuserType width, dpuserType height, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("rect", x, y, width, height, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->rect(naxis1.toInt(), naxis2.toInt(), x.toInt(), y.toInt(), width.toInt(), height.toInt());

    return rv;
}

//Alex
/*!
delta
Creates a delta function. This is defined by: All values in the array 0 except for (x, y) which is set to value. If value is not set, 1 will be used. Optional keywords are naxis1, naxis2 which determine the size of the array. If not set, the size of the array is determined from global variables.
Syntax
result = delta(X, Y [, VALUE] [, naxis1=value] [, naxis2 = value])
Arguments
X: The x-position of the nonzero pixel
Y: The y-position of the nonzero pixel
VALUE: Optional, the value of the nonzero pixel
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
*/

dpuserType dpuserFunction_delta(dpuserType x, dpuserType y, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("delta", x, y, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->delta(naxis1.toInt(), naxis2.toInt(), (int)(x.dvalue + .5), (int)(y.dvalue + .5));

    return rv;
}

//Alex

dpuserType dpuserFunction_delta(dpuserType x, dpuserType y, dpuserType factor, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("delta", x, y, factor, naxis1, naxis2);

    dpuserType rv = dpuserFunction_delta(x, y, naxis1, naxis2);
    *rv.fvalue *= factor.dvalue;

    return rv;
}

//Alex
/*!
cosbell
Creates a cosine bell filter. This is defined by: Center position (x, y), inner radius r1, and outer radius r2. Optional keywords are naxis1, naxis2 which determine the size of the array. If not set, the size of the array is determined from global variables.
Syntax
result = cosbell(X, Y, RADIUS1, RADIUS2 [, naxis1=value] [, naxis2 = value])
Arguments
X: The center of the cosine bell filter in the x-axes
Y: The center of the cosine bell filter in the y-axes
RADIUS1: The inner radius where the filter is equal to 1
RADIUS2: The outer radius where the filter is equal to 0
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
*/

dpuserType dpuserFunction_cosbell(dpuserType x, dpuserType y, dpuserType radius1, dpuserType radius2, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("cosbell", x, y, radius1, radius2, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->cosbell(naxis1.toInt(), naxis2.toInt(), x.dvalue, y.dvalue, radius1.dvalue, radius2.dvalue);

    return rv;
}

//Alex
/*!
chinhat
Creates a chinese hat filter. This is defined by: Center position (x, y) and radius. Optional keywords are naxis1, naxis2 which determine the size of the array. If not set, the size of the array is determined from global variables.
Syntax
result = chinhat(X, Y, RADIUS [, naxis1=value] [, naxis2 = value])
Arguments
X: The center of the chinese hat filter in the first dimension
Y: The center of the chinese hat filter in the second dimension
RADIUS: The radius of the chinese hat filter
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
*/

dpuserType dpuserFunction_chinhat(dpuserType x, dpuserType y, dpuserType radius, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("chinhat", x, y, radius, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->chinese_hat(naxis1.toInt(), naxis2.toInt(), x.dvalue, y.dvalue, radius.dvalue);

    return rv;
}

//Alex
/*!
airy
Creates an airy function at the center of the array. This is defined by: Telescope diameter [meters], pixel scale [arcsec/pixel], and wavelength [microns]. Optional keywords are naxis1, naxis2 which determine the size of the array. If not set, the size of the array is determined from global variables.
Syntax
result = airy(DIAMETER, PIXELSCALE, WAVELENGTH [, naxis1=value] [, naxis2 = value])
Arguments
DIAMETER: The telescope diameter in meters
PIXELSCALE: The size of a pixel in arcseconds
WAVELENGTH: The observing wavelength in microns
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
*/

dpuserType dpuserFunction_airy(dpuserType diameter, dpuserType pixelscale, dpuserType wavelength, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("airy", diameter, pixelscale, wavelength, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->airy(naxis1.toInt(), naxis2.toInt(), diameter.dvalue, pixelscale.dvalue, wavelength.dvalue, 0.0);

    return rv;
}

//Alex
/*!
cubemedian
Does a median of all images in X. The value reject can be used to exclude a value to be taken when calculating the median.
Syntax
result = cubemedian(X [, reject])
Arguments
X: A matrix or a FITS file name. If a FITS file name is given, the global variable tmpmem determines how many bytes of data are read in at a time.
reject: Optional, a value which should be discarded.
See also
function_collapse
Notes
If reject is given and no pixel along the cube is different from reject, a value of 0 will be inserted.
*/

dpuserType dpuserFunction_cubemedian(dpuserType X) {
    dpuserType first;
    first.type = typeCon;
    first.lvalue = 1;

    dpuserType last;
    last.type = typeCon;
    last.lvalue = 0;

    return dpuserFunction_cubemedian(X, first, last);
}


//Alex

dpuserType dpuserFunction_cubemedian(dpuserType X, dpuserType reject) {
    Dpuser2cUtils::checkFuncArgs("cubemedian", X, reject);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    if (X.type == typeFits)
        rv.fvalue->CubeMedian(*X.fvalue, reject.dvalue);
    else
        rv.fvalue->CubeMedian(X.ffvalue->c_str(), reject.dvalue);

    return rv;
}

//Alex

dpuserType dpuserFunction_cubemedian(dpuserType X, dpuserType first, dpuserType last) {
    Dpuser2cUtils::checkFuncArgs("cubemedian", X, first, last);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    if (X.type == typeFits)
        rv.fvalue->CubeMedian(*X.fvalue);
    else
        rv.fvalue->CubeMedian(X.ffvalue->c_str(), first.lvalue, last.lvalue);

    return rv;
}

//Alex
/*!
cubeavg
Does an average of all images in X. Optionally, a rejection value can be given
which will not be used in the averaging process.
Syntax
result = cubeavg(X [, reject])
Arguments
X: A matrix or a FITS file name. If a FITS file name is given, only one image is read into memory at a time thus saving memory.
reject: Optional, the value to disregard
Notes
If reject is given and no pixel along the cube is different from reject, a value of 0 will be inserted.
*/

dpuserType dpuserFunction_cubeavg(dpuserType X) {
    dpuserType first;
    first.type = typeCon;
    first.lvalue = 1;

    dpuserType last;
    last.type = typeCon;
    last.lvalue = 0;

    return dpuserFunction_cubeavg(X, first, last);
}

//Alex

dpuserType dpuserFunction_cubeavg(dpuserType X, dpuserType reject) {
    Dpuser2cUtils::checkFuncArgs("cubeavg", X, reject);
    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    if (X.type == typeFits)
        cube_avg(*X.fvalue, *rv.fvalue, reject.dvalue);
    else if (rv.type == typeFitsFile)
        cube_avg(X.ffvalue->c_str(), *rv.fvalue, reject.dvalue);
    else
        CubeAverage(*rv.fvalue, *X.arrvalue);

    return rv;
}

//Alex

dpuserType dpuserFunction_cubeavg(dpuserType X, dpuserType first, dpuserType last) {
    Dpuser2cUtils::checkFuncArgs("cubeavg", X, first, last);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    if (X.type == typeFits)
        cube_avg(*X.fvalue, *rv.fvalue, first.lvalue, last.lvalue);
    else if (rv.type == typeFitsFile)
        cube_avg(X.ffvalue->c_str(), *rv.fvalue, first.lvalue, last.lvalue);
    else
        CubeAverage(*rv.fvalue, *X.arrvalue);

    return rv;
}

//Alex
/*!
ssa
Calculates the simple shift-and-add of all images in the x datacube. If x is a fits file, a memory saving method will be used to do the ssa. If x is a string, this points to a text file where an arbitrary amount of fits filenames is given as a list. The first image in each of these files will be used for the ssa. If sky, flat, dpl, mask are given, they will be used. An optional keyword method can be given: method = 0 : Shift the brightest pixel to xcen, ycen method = 1 : Integer shift the centroid to xcen, ycen method = 2 : Subpixel shift the centroid to xcen, ycen If method < 0, the same is done as if method were set to 0, but the brightest pixel is searched in a smoothed version of the image. The smoothing is done by convolving with a gaussian of fwhm the negative of method.
Syntax
result = ssa(X, XCENTER, YCENTER, SKY, FLAT, DPL, MASK [, METHOD] [, /resize])
Arguments
X: A FITS datacube
XCENTER: The pixel where to shift in the first dimension
YCENTER: The pixel where to shift in the second dimension
SKY: A sky to be subtracted
FLAT: A flatfield to be multiplied
DPL: A dead pixel list to be applied
MASK: The peak pixel will be searched for in an image multiplied by this mask
METHOD: Optional, controls how the SSA will be done:
METHOD = 0: Shift the brightest pixel to XCENTER, YCENTER (default)
METHOD = 1: Integer shift the centroid to XCENTER, YCENTER
METHOD = 2: Subpixel shift the centroid to XCENTER, YCENTER
METHOD < 0: the same is done as if METHOD were set to 0, but the brightest pixel is searched in a smoothed version of the image. The smoothing is done by convolving with a gaussian of fwhm the negative of method.
Switches
/resize: The resulting images will be resized in the way, that in each direction the respective extend of MASK is added twice.
*/

dpuserType dpuserFunction_ssa(dpuserType X, dpuserType xcen, dpuserType ycen, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask, dpString resize) {
    dpuserType method;
    method.type = typeCon;
    method.lvalue = 0;

    return dpuserFunction_ssa(X, xcen, ycen, sky, flat, dpl, mask, method, resize);
}

//Alex

dpuserType dpuserFunction_ssa(dpuserType X, dpuserType xcen, dpuserType ycen, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask, dpuserType method, dpString resize) {
    Dpuser2cUtils::checkFuncArgs("ssa", X, xcen, ycen, sky, flat, dpl, mask, method);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    if (X.type == typeFitsFile)
        rv.fvalue->qssa(X.ffvalue->c_str(), xcen.lvalue, ycen.lvalue, method.lvalue, sky.fvalue, flat.fvalue, dpl.fvalue, mask.fvalue);
    else if (X.type == typeStrarr) {
        int doResize;

        if (resize.isEmpty())
            doResize = 0;
        else
            doResize = 1;

        qssa(*rv.fvalue, *X.arrvalue, xcen.lvalue, ycen.lvalue, method.lvalue, sky.fvalue, flat.fvalue, dpl.fvalue, mask.fvalue, doResize);
    } else
        rv.fvalue->qssa(*X.fvalue, xcen.lvalue, ycen.lvalue, method.lvalue, sky.fvalue, flat.fvalue, dpl.fvalue, mask.fvalue);

    return rv;
}

//Alex
/*!
mosaic
Make a mosaic of the images specified in the file pointed to by FNAME. The file formatis as follows: 3(4) Columns: Filename, xshift, yshift, (scale factor). Negative Shifts result in clipping of the image.
Syntax
result = mosaic(FNAME)<br>
result = mosaic(filename, xshift, yshift [, scale factor])
Arguments
FNAME: A string specifying the file name to use.
*/

dpuserType dpuserFunction_mosaic(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("mosaic", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->mosaic(X.svalue->c_str());

    return rv;
}

//Alex

dpuserType dpuserFunction_mosaic(dpuserType X, dpuserType xshift, dpuserType yshift) {
    Dpuser2cUtils::checkFuncArgs("mosaic", X, xshift, yshift);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    float *data1 = floatdata(*xshift.fvalue);
    float *data2 = floatdata(*yshift.fvalue);
    if (data1 && data2)
        mosaic(*rv.fvalue, *X.arrvalue, data1, data2);
    if (data1 && (data1 != xshift.fvalue->r4data)) free(data1);
    if (data2 && (data2 != yshift.fvalue->r4data)) free(data2);

    return rv;
}

//Alex

dpuserType dpuserFunction_mosaic(dpuserType X, dpuserType xshift, dpuserType yshift, dpuserType scaleFactor) {
    Dpuser2cUtils::checkFuncArgs("mosaic", X, xshift, yshift, scaleFactor);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    float *data1 = floatdata(*xshift.fvalue);
    float *data2 = floatdata(*yshift.fvalue);
    float *data3 = floatdata(*scaleFactor.fvalue);
    if (data1 && data2 && data3)
        mosaic(*rv.fvalue, *X.arrvalue, data1, data2, data3);
    if (data1 && (data1 != xshift.fvalue->r4data)) free(data1);
    if (data2 && (data2 != yshift.fvalue->r4data)) free(data2);
    if (data3 && (data3 != scaleFactor.fvalue->r4data)) free(data3);

    return rv;
}

/*!
shift
Shifts (and optionally wraps) X by specified values.
Syntax
result = shift(X, xs, ys [, zs] [, /wrap])
Arguments
X: The array to be shifted
xs: Shift vector in the first dimension
ys: Shift vector in the second dimension
zs: Shift vector in the third dimension
Switches
wrap: Pixels shifted off the array will be wrapped to the opposide side.
Notes
If either shift vector is non-integer, a subpixel shift is applied and the array type changed to R4. Subpixel shift is only supported in 2 dimensions.
*/

dpuserType dpuserFunction_shift(dpuserType X, dpuserType sx, dpuserType sy, dpString option) {
    Dpuser2cUtils::checkFuncArgs("shift", X, sx, sy);

    dpuserType rv;
    rv.clone(X);

    bool fs = TRUE;

    if (sx.isInt() && sy.isInt()) fs = FALSE;

    if (fs) {
        int method = 0;
        if (option == "linear") method = 0;
        if (option == "fft") method = 1;
        if (option == "poly3") method = 3;
        rv.fvalue->fshift(sx.toDouble(), sy.toDouble(), method);
    } else {
        if (option == "wrap") {
            rv.fvalue->wrap(sx.toInt(), sy.toInt(), 0);
        } else {
            rv.fvalue->ishift(sx.toInt(), sy.toInt(), 0);
        }
    }
    return rv;
}

//Alex
/*!
rotate
Rotates X by ANGLE (in degrees, counterclockwise)
Syntax
result = rotate(X, ANGLE [, XCENTER, YCENTER])
Arguments
X: The matrix to be rotated
ANGLE: Rotation angle in degrees. Rotation will we counterclockwise
XCENTER: Optional, the center of the rotation in the first dimension (defaults to the center of X).
YCENTER: Optional, the center of the rotation in the second dimension (defaults to the center of X).
*/

dpuserType dpuserFunction_rotate(dpuserType X, dpuserType angle) {
    dpuserType xcen;
    xcen.type = typeDbl;
    xcen.dvalue = -1.0;

    return dpuserFunction_rotate(X, angle, xcen);
}

//Alex

dpuserType dpuserFunction_rotate(dpuserType X, dpuserType angle, dpuserType xcen) {
    dpuserType ycen;
    ycen.type = typeDbl;
    ycen.dvalue = -1.0;

    return dpuserFunction_rotate(X, angle, xcen, ycen);
}

//Alex

dpuserType dpuserFunction_rotate(dpuserType X, dpuserType angle, dpuserType xcen, dpuserType ycen) {
    Dpuser2cUtils::checkFuncArgs("rotate", X, angle, xcen, ycen);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->rotate(angle.dvalue, xcen.dvalue, ycen.dvalue);

    return rv;
}

//Alex
/*!
fft
Calculates the fast fourier transform of X. If X is real, the forward transform is computed. If X is complex, the inverse transform is computed.
Syntax
result = fft(X)
Arguments
X: A matrix.
Notes
The fft function uses the FFTW (Fastest Fourier Transform in the West) library, which is not limited to any array size. Still, computing a fft is fastest for arrays of size 2^N (N being an integer number).
*/

dpuserType dpuserFunction_fft(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("fft", X);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->fft();

    return rv;
}

//Alex
/*!
reass
Reassembles X.
Syntax
result = reass(X)
Arguments
X: A matrix.
*/

dpuserType dpuserFunction_reass(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("reass", X);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->reass();

    return rv;
}

//Alex
/*!
norm
Normalizes X.
Syntax
result = norm(X [, /unity] [, /total] [, /average])
Arguments
X: A matrix.
Switches
/unity: All values are scaled to be between 0 and 1 (default)
/total: The total flux in the resulting array will be 1
/average: The average flux in the resulting array will be 1
*/

dpuserType dpuserFunction_norm(dpuserType X, dpString option) {
    Dpuser2cUtils::checkFuncArgs("norm", X);

    dpuserType rv;
    rv.clone(X);

    if (option == "total")
        rv.fvalue->norf(0);	//total
    else if (option == "average")
        rv.fvalue->norf(1);	// average
    else
        rv.fvalue->norm();	// unity

    return rv;
}

//Alex
/*!
clip
Clips values in X. If VALUE is not set, all values below low will be set to low, and all values above high will be set to high. If value is set, all values above low AND below high are set to value.
Syntax
result = clip(X, LOW, HIGH [, VALUE])
Arguments
X: The matrix to be clipped
LOW: All values below this value will be set to LOW
HIGH: All values above this value will be set to HIGH. If HIGH < LOW, no upper clipping will be done
VALUE: Optional, if given then all values above LOW and below HIGH will be set to this value (inclusive clipping)
*/

dpuserType dpuserFunction_clip(dpuserType X, dpuserType low, dpuserType high) {
    Dpuser2cUtils::checkFuncArgs("clip", X, low, high);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->clip(low.dvalue, high.dvalue);

    return rv;
}

//Alex

dpuserType dpuserFunction_clip(dpuserType X, dpuserType low, dpuserType high, dpuserType value) {
    Dpuser2cUtils::checkFuncArgs("clip", X, low, high, value);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->inclip(low.dvalue, high.dvalue, value.dvalue);

    return rv;
}

//Alex
/*!
smooth
Smooths X by multiplying with a gaussian of width FWHM in fourier space. The total flux is kept constant. This is equivalent to reass(fft(fft(X)*fft(gauss(naxis1(X)/2+1,naxis2(X)/2+1,FWHM))))
Syntax
result = smooth(X, FWHM [,/x | /y | /z | /xy])
Arguments
X: A matrix.
FWHM: The full width at half maximum of the gaussian used for smoothing.
Switches
/x: Smooth along the first axis
/y: Smooth along the second axis
/z: Smooth along the third axis
/xy: 2D Smooth along the first and second axes (default)
Examples
Smooth each spectrum in a 3D cube (if the spectra are along the 3rd dimension):
<code>result = smooth(cube, /z)
See also
function_boxcar
*/

dpuserType dpuserFunction_smooth(dpuserType X, dpuserType fwhm, dpString option) {
    Dpuser2cUtils::checkFuncArgs("smooth", X, fwhm);

    dpuserType rv;
    rv.clone(X);

    if ((option == "z") && (rv.fvalue->Naxis(3) > 1)) {
        rv.fvalue->smooth1d(fwhm.dvalue, 3);
    } else if (option == "x") {
        rv.fvalue->smooth1d(fwhm.dvalue, 1);
    } else if (option == "y") {
        rv.fvalue->smooth1d(fwhm.dvalue, 2);
    } else {
        rv.fvalue->smooth(fwhm.dvalue);
    }

    return rv;
}

//Alex
/*!
boxcar
Computes a running boxcar with radius WIDTH. By default, the value is replaces by the average in the box. This can be changed by specifying one of the switches /minimum, /maximum, or /median.
Syntax
result = boxcar(X, WIDTH [, /minimum | /maximum | /median] [,/x | /y | /z | /xy])
Arguments
X: A matrix.
WIDTH: An integer number specifying the radius of the box.
Switches
/minimum: Each value is replaced with the minimum within a box with radius WIDTH
/maximum: Each value is replaced with the maximum within a box with radius WIDTH
/median: Each value is replaced with the median within a box with radius WIDTH
/x: Boxcar along the first axis
/y: Boxcar along the second axis
/z: Boxcar along the third axis
/xy: 2D boxcar along the first and second axes (default)
See also
function_smooth
*/

dpuserType dpuserFunction_boxcar(dpuserType X, dpuserType width, dpString option1, dpString option2) {
    Dpuser2cUtils::checkFuncArgs("boxcar", X, width);

    int method1 = 0;			// average

    if (option1 == "minimum")	// minimum
        method1 = 1;
    else if (option1 == "maximum")	// maximum
        method1 = 2;
    else if (option1 == "median")	// median
        method1 = 3;

    int method2 = 0;
    if (option2 == "z") method2 = 1;
    else if (option2 == "x") method2 = 2;
    else if (option2 == "y") method2 = 3;

    dpuserType rv;
    rv.clone(X);
    Boxcar(*rv.fvalue, width.lvalue, method1, method2);

    return rv;
}

//Alex
/*!
3dexpand
Replaces all values in the single images of a datacube by the average in the respective images.
Syntax
result = 3dexpand(X)
Arguments
X: A matrix.
*/

dpuserType dpuserFunction_3dexpand(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("3dexpand", X);

    dpuserType rv;
    rv.clone(X);
    expandCal3d(*rv.fvalue);

    return rv;
}

//Alex
/*!
dpixcreate
Creates a dead pixel list of X using a magic threshold. Boxsize defaults to 3 and gives the radius of the running boxcar used. NPASS defaults to 3 and gives the number of passes used.
Syntax
result = dpixcreate(X, THRESHOLD [, BOXSIZE [, NPASS]])
Arguments
X: On this array the dead pixel list will be created
THRESHOLD: Sensitivity of the algorithm (use 5 for a start)
BOXSIZE: Optional, the size of the square box for comparing pixel values (default: 3)
NPASS: Optional, number of iterations
*/

dpuserType dpuserFunction_dpixcreate(dpuserType X, dpuserType threshold) {
    dpuserType boxsize;
    boxsize.type = typeCon;
    boxsize.lvalue = 3;	// default value of dpixCreate() in fits.h

    return dpuserFunction_dpixcreate(X, threshold, boxsize);
}

//Alex

dpuserType dpuserFunction_dpixcreate(dpuserType X, dpuserType threshold, dpuserType boxsize) {
    dpuserType npass;
    npass.type = typeCon;
    npass.lvalue = 3;	// default value of dpixCreate() in fits.h

    return dpuserFunction_dpixcreate(X, threshold, boxsize, npass);
}

//Alex

dpuserType dpuserFunction_dpixcreate(dpuserType X, dpuserType threshold, dpuserType boxsize, dpuserType npass) {
    Dpuser2cUtils::checkFuncArgs("dpixcreate", X, threshold, boxsize, npass);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->dpixCreate(threshold.dvalue, boxsize.lvalue, npass.lvalue);

    return rv;
}

//Alex
/*!
dpixapply
Corrects dead pixels in X using DPL by interpolation. BOXSIZE defaults to 1 and gives the radius of the boxcar used. Each value where DPL is not 0 will be regarded as a dead pixel. Those are not used in the interpolation process.
Syntax
        result = dpixapply(X, DPL [, BOXSIZE [, method = value]] [,/spiffik [,/bezier]])
Arguments
X: The array to be corrected for dead pixels
DPL: A dead pixel list in which all non-zero pixels mark dead pixels in X
BOXSIZE: Optional, gives the size of the averaging box for interpolation
method = value: Optional, gives the type of correction:
method = 0: Interpolation from boxcar average (default)
method = 1: Interpolation along first dimension
method = 2: Interpolation along second dimension
Switches
/spiffik : obsolete
/bezier : obsolete
*/

dpuserType dpuserFunction_dpixapply(dpuserType X, dpuserType dpl, dpString option) {
    dpuserType boxsize;
    boxsize.type = typeCon;
    boxsize.lvalue = 1;	// default value of DeadpixApply() in fits.h

    return dpuserFunction_dpixapply(X, dpl, boxsize, option);
}

//Alex

dpuserType dpuserFunction_dpixapply(dpuserType X, dpuserType dpl, dpuserType boxsize, dpString option) {
    Dpuser2cUtils::checkFuncArgs("dpixapply", X, dpl, boxsize);

    dpuserType rv;
    rv.clone(X);

    if (option == "spiffik") {
        Fits cube, dplcube;

        if (spifficube(*X.fvalue, cube, 1) && spifficube(*dpl.fvalue, dplcube, 1)) {
            if (option == "bezier")
                DeadpixApply(cube, dplcube, 3, boxsize.lvalue);
            else
                DeadpixApply(cube, dplcube, dpuser_vars["method"].toInt(), boxsize.lvalue);
            spiffiuncube(cube, *rv.fvalue, 1);
        }
        else
            throw dpuserTypeException("dpixapply: Failed applying spifficube()\n");
    } else
        DeadpixApply(*rv.fvalue, *X.fvalue, dpuser_vars["method"].toInt(), boxsize.lvalue);

    return rv;
}

//Alex
/*!
flip
Flips X by specified AXIS
Syntax
result = flip(X, AXIS)
Arguments
X: The array to be flipped
AXIS: Which axis to flip: 1 = x, 2 = y
*/

dpuserType dpuserFunction_flip(dpuserType X, dpuserType axis) {
    Dpuser2cUtils::checkFuncArgs("flip", X, axis);

    dpuserType rv;
    rv.clone(X);

    rv.fvalue->flip(axis.lvalue);

    return rv;
}

//Alex
/*!
enlarge
Enlarges X by specified integer SCALE.
Syntax
result = enlarge(X, SCALE [, method])
Arguments
X: The array to be enlarged
SCALE: The resulting array will be SIZE times bigger than X
method: Optional, determines how to enlarge:
method = 0: Center original image (default)
method = 1: Center original image, edge values are mean of edge
method = 2: Blow up pixels
*/

dpuserType dpuserFunction_enlarge(dpuserType X, dpuserType scale) {
    dpuserType method;
    method.type = typeCon;
    method.lvalue = 0;	// default value of enlarge() in fits.h

    return dpuserFunction_enlarge(X, scale, method);
}

//Alex

dpuserType dpuserFunction_enlarge(dpuserType X, dpuserType scale, dpuserType method) {
    Dpuser2cUtils::checkFuncArgs("enlarge", X, scale, method);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->enlarge(scale.lvalue, method.lvalue);

    return rv;
}

/*!
resize
Resizes X to the new axis lengths. The argument NAXIS3 is optional. If the new dimension is smaller than in X, the image will be clipped, else the image will be in the lower left corner.
Syntax
result = resize(X, NAXIS1, NAXIS2 [, NAXIS3])
Arguments
X: A matrix.
NAXIS1: The new length in the first dimension.
NAXIS2: The new length in the second dimension.
NAXIS3: The new length in the third dimension.
*/

dpuserType dpuserFunction_resize(dpuserType arg, dpuserType x, dpuserType y) {
    Dpuser2cUtils::checkFuncArgs("resize", arg, x, y);

    dpuserType rv;
    rv.clone(arg);

    rv.fvalue->resize(x.toInt(), y.toInt());
    return rv;
}

/*!
wien
Performs a wiener filtered deconvolution of X using PSF as dirty beam. HEIGHT gives the maximum of the delta function used as filter function and defaults to 1.
Syntax
result = wien(X, PSF [, HEIGHT])
Arguments
X: A matrix.
PSF: A matrix.
HEIGHT: A real number.
See also
function_lucy
function_clean
Notes
The memory required for wiener deconvolution (in bytes) calculates as 33*(Number of pixels in the image).
*/

dpuserType dpuserFunction_wien(dpuserType arg, dpuserType psf) {
    Dpuser2cUtils::checkFuncArgs("wien", arg, psf);

    dpuserType rv;
    rv.clone(arg);
    rv.fvalue->quick_wien(*psf.fvalue);

    return rv;
}

dpuserType dpuserFunction_wien(dpuserType arg, dpuserType psf, dpuserType factor) {
    Dpuser2cUtils::checkFuncArgs("wien", arg, psf, factor);

    dpuserType rv;
    rv.clone(arg);
    rv.fvalue->quick_wien(*psf.fvalue, factor.toDouble());

    return rv;
}

/*!
lucy
Performs a lucy deconvolution of X using PSF as dirty beam and N iterations. THRESHOLD defaults to 0.000003.
 
LUCY DECONVOLUTION ALGORITHM. "Lucy has shown that his iterative scheme is related to the maximum likelihood solution of the deconvolution problem and would converge to that solution after an infinite number of iterations."
 
<code>iteration number      : k
<code>dirty beam            : dbeam
<code>dirty map             : dmap
<code>estimated object      : o(k)
<code>reconvolved estimated
<code>object                : oc(k)
<code>correctin function    : t(k)
<code>multiplication        :  *
<code>convolution           : (*)
 
<code>start:    o(0) = dmap
 
<code>iteration:
<code>oc(k) = o(k) (*) dbeam
<code>t(k)  = (dmap/oc(k)) (*) dbeam
<code>o(k+1)= o(k) * t(k)
 
Due to calculation of the quotient for t(k) the algorithm is sensitive to noise. Therefore a weighting function has been implemented which is unity if the dmap signal is larger than "thresh" and zero elsewhere. t(k) is only calculated in that area and hence the deconvolution is only effective there. Threshold equals input if positive. If input is negative thresh is determined via mean and rms in the area below abs(thresh). If thresh is zero thresh is determined in the area below -0.1 times the maximum flux in dmap. The redetermined threshold is mean value plus 3 times rms.
 
To check on convergence of algorithm the rms between input dmap and current lucy deconvolved map reconvolved with dbeam is calculated.
 
Syntax
result = lucy(X, PSF, N [, THRESHOLD])
Arguments
X: A matrix.
PSF: A matrix.
N: An integer number.
THRESHOLD: A real number.
See also
function_clean
function_wien
Notes
The memory required for lucy deconvolution (in bytes) calculates as 44*(Number of pixels in the image).
*/

dpuserType dpuserFunction_lucy(dpuserType arg, dpuserType psf, dpuserType niter) {
    Dpuser2cUtils::checkFuncArgs("lucy", arg, psf, niter);

    dpuserType rv;
    rv.clone(arg);
    rv.fvalue->quick_lucy(*psf.fvalue, niter.toInt());

    return rv;
}

dpuserType dpuserFunction_lucy(dpuserType arg, dpuserType psf, dpuserType niter, dpuserType thresh) {
    Dpuser2cUtils::checkFuncArgs("lucy", arg, psf, niter, thresh);

    dpuserType rv;
    rv.clone(arg);
    rv.fvalue->quick_lucy(*psf.fvalue, niter.toInt(), thresh.toDouble());

    return rv;
}

/*!
center
Shifts the brightest pixel in X to the center.
Syntax
result = center(X)
Arguments
X: A matrix.
*/

dpuserType dpuserFunction_center(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("center", arg);

    int x, y;
    dpuserType rv;
    rv.clone(arg);
    rv.fvalue->max_pos(&x, &y);
    rv.fvalue->shift(rv.fvalue->Naxis(1) / 2 + 1 - x, rv.fvalue->Naxis(2) / 2 + 1 - y, 2);

    return rv;
}

//Alex
/*!
3dnorm (obsolete)
Normalizes a 3D datacube.
Syntax
result = 3dnorm(X, filename)
Arguments
X: A matrix.
filename: Path to a file
*/

dpuserType dpuserFunction_3dnorm(dpuserType X, dpuserType string) {
    Dpuser2cUtils::checkFuncArgs("3dnorm", X, string);

    dpuserType rv;
    rv.clone(X);
    norm3d(*rv.fvalue, string.svalue->c_str());
    return rv;
}

//Alex
/*!
conj
Returns the complex conjugate of X.
Syntax
result = conj(X)
Arguments
X: A matrix or a complex number.
See also
function_complex
function_real
function_imag
function_arg
function_abs
*/

dpuserType dpuserFunction_conj(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("conj", X);

    dpuserType rv;

    if (X.type == typeCom) {
        rv.type = typeCom;
        rv.cvalue = CreateComplex(complex_conj(*X.cvalue));
    } else {
        rv.clone(X);
        rv.fvalue->conj();
    }

    return rv;
}

//Alex
/*!
correl
Returns the cross-correlation of X and Y.
Syntax
result = correl(X, Y)
Arguments
X: A matrix.
Y: A matrix.
*/

dpuserType dpuserFunction_correl(dpuserType X, dpuserType Y, dpString option) {
    Dpuser2cUtils::checkFuncArgs("correl", X, Y);

    dpuserType rv;
    rv.clone(X);
    if (!option.isEmpty())
        rv.fvalue->correl_real(*Y.fvalue);
    else
        rv.fvalue->correl(*Y.fvalue);

    return rv;
}

/*!
readfits
Read specified FITS file into memory. The optional arguments x1, x2, y1, y2, z1, z2 can be used to read in a part of a FITS file. The combination 0, 0 specifies that the complete range of the respective axis should be read in.
Syntax
result = readfits(FILENAME [, x1, x2 [, y1, y2 [, z1, z2]]])
Arguments
FILENAME: A string.
x1, x2: Range in first dimension, defaults to 0, 0
y1, y2: Range in second dimension, defaults to 0, 0
z1, z2: Range in third dimension, defaults to 0, 0
Examples
Read in the 150th to 160th slice of a datacube:
<code>subcube = readfits("cube.fits", 0, 0, 0, 0, 150, 160)
See also
function_readfitsextension
function_readfitsall
*/

dpuserType dpuserFunction_readfits(dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("readfits", arg);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    if (!rv.fvalue->ReadFITS(arg.svalue->c_str()))
        throw dpuserTypeException("readfits: Could not read file.");

    return rv;
}

//Alex
/*!
3dspec
Returns an average 3D spectrum of X (dispersion along the third dimension) using the mask Y (2-dim) or the center (x,y) with radii r1 and r2.
Syntax
result = 3dspec(X, Y)<br>
result = 3dspec(X, xcenter, ycenter [, r1 [, r2]])
Arguments
X: A three dimensional matrix.
Y: A two dimensional matrix.
xcenter: The central pixel of the spectrum in the first dimension.
ycenter: The central pixel of the spectrum in the second dimension.
r1: The radius of the circular aperture to be used.
r2: The scaled average of all values in the annulus defined by r1 and r2 will be subtracted from the spectrum (local sky subtraction).
*/

dpuserType dpuserFunction_3dspec(dpuserType X, dpuserType Y) {
    Dpuser2cUtils::checkFuncArgs("3dspec", X, Y);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->create(X.fvalue->Naxis(3), 1);

    int i;
    Fits _tmp;
    for (i = 1; i <= X.fvalue->Naxis(3); i++) {
        X.fvalue->extractRange(_tmp, -1, -1, -1, -1, i, i);
        _tmp *= *Y.fvalue;
        rv.fvalue->r4data[i] = _tmp.get_avg();
    }

    return rv;
}

//Alex

dpuserType dpuserFunction_3dspec(dpuserType X, dpuserType xcenter, dpuserType ycenter) {
    dpuserType r1;
    r1.type = typeCon;
    r1.lvalue = 0;

    return dpuserFunction_3dspec(X, xcenter, ycenter, r1);
}



//Alex

dpuserType dpuserFunction_3dspec(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType r1, dpString option) {
    dpuserType r2;
    r2.type = typeCon;
    r2.lvalue = 0;

    return dpuserFunction_3dspec(X, xcenter, ycenter, r1, r2, option);
}

//Alex

dpuserType dpuserFunction_3dspec(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType r1, dpuserType r2, dpString option) {
    Dpuser2cUtils::checkFuncArgs("3dspec", X, xcenter, ycenter, r1, r2);

    int method = 0;
    if (option == "sum") method = 0;
    else if (option == "average") method = 1;
    else if (option == "median") method = 2;

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->create(X.fvalue->Naxis(3), 1);
    rv.fvalue->spec3d(*X.fvalue, xcenter.lvalue, ycenter.lvalue, r1.lvalue, r2.lvalue, method);

    return rv;
}

//Alex
/*!
stddev
Returns the standard deviation of X, optionally omitting a value.
Syntax
result = stddev(X [, omit])
Arguments
X: A matrix.
omit: A value to be omitted when calculating the standard deviation.
Switches
/x: Calculate the standard deviation along the first axis
/y: Calculate the standard deviation along the second axis
/z: Calculate the standard deviation along the third axis
/xy: Calculate the standard deviation along the first and second axes
/xz: Calculate the standard deviation along the first and third axes
/yz: Calculate the standard deviation along the second and third axes
Examples
To get a 1D vector of the standard deviation of all slices along a 3D cube:
<code>v = stddev(cube, /xy)
See also
function_max
function_min
function_avg
function_total
function_median
function_stddev
function_variance
function_meddev
*/

dpuserType dpuserFunction_stddev(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("stddev", X);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = X.fvalue->get_stddev();

    return rv;
}

//Alex

dpuserType dpuserFunction_stddev(dpuserType X, dpuserType ignore) {
    Dpuser2cUtils::checkFuncArgs("stddev", X, ignore);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = X.fvalue->get_stddev(ignore.dvalue);

    return rv;
}

//Alex
/*!
variance
Returns the variance of X.
Syntax
result = variance(X)
Arguments
X: A matrix.
Switches
/x: Calculate the variance along the first axis
/y: Calculate the variance along the second axis
/z: Calculate the variance along the third axis
/xy: Calculate the variance along the first and second axes
/xz: Calculate the variance along the first and third axes
/yz: Calculate the variance along the second and third axes
Examples
To get a 1D vector of the variance of all slices along a 3D cube:
<code>v = variance(cube, /xy)
See also
function_max
function_min
function_avg
function_total
function_median
function_stddev
function_variance
function_meddev
*/

dpuserType dpuserFunction_variance(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("variance", X);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = X.fvalue->get_variance();

    return rv;
}

//Alex

dpuserType dpuserFunction_variance(dpuserType X, dpuserType ignore) {
    Dpuser2cUtils::checkFuncArgs("variance", X, ignore);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = X.fvalue->get_variance(ignore.dvalue);
    return rv;
}

//Alex
/*!
meddev
Returns the median deviation from the median in X.
Syntax
result = meddev(X)
Arguments
X: A matrix.
Switches
/x: Calculate the median deviation along the first axis
/y: Calculate the median deviation along the second axis
/z: Calculate the median deviation along the third axis
/xy: Calculate the median deviation along the first and second axes
/xz: Calculate the median deviation along the first and third axes
/yz: Calculate the median deviation along the second and third axes
Examples
To get a 1D vector of the median deviation of all slices along a 3D cube:
<code>v = meddev(cube, /xy)
See also
function_max
function_min
function_avg
function_total
function_median
function_stddev
*/

dpuserType dpuserFunction_meddev(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("meddev", X);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = X.fvalue->get_meddev();
    return rv;
}

//Alex
/*!
saomark
Interactively mark pixels in ds9 which are marked on the display. The resulting array will have a value of 1 in each marked pixel, a value of 0 elsewhere. Press any key to mark a point, press 'd' to delete an already marked point, press 'q' to quit. (Use ds9 v5.2 or newer!)
Syntax
result = saomark()
Arguments
none
*/

dpuserType dpuserFunction_saomark() {
    if (ds9_running() == 0) {
        throw dpuserTypeException("ERROR: No ds9 is running! Please start first ds9 (at least v5.2)\n");
    }
    if (ds9_frame_loaded() == 0) {
        throw dpuserTypeException("ERROR: First load a fits-file into ds9 either in ds9 itself or with sao in dpuser!\n");
    }

    dpuserType rv;
    int nx, ny, wcs = 0, pos, ret;
    dpString key, tmp;
    size_t lens[NXPA];
    char *bufs[NXPA];
    char *names[NXPA];
    char *messages[NXPA];

    // get size of displayed fits
    ret = XPAGet(xpa, xpaServer, "fits size", NULL,
                 bufs, lens, names, messages, NXPA);
    if(messages[0] == NULL){
        /* process buf contents */
        tmp = bufs[0];
        dpString sp = dpString(" ");
        tmp = tmp.stripWhiteSpace();
        nx = atoi(tmp.left(tmp.strpos(sp, false)).c_str());
        ny = atoi(tmp.right(tmp.length() - tmp.strpos(sp, false)).c_str());
        free(bufs[0]);
    }
    else{
        /* error processing */
        dp_output("ERROR: %s (%s)\n", messages[0], names[0]);
    }
    if(names[0]) {
        free(names[0]);
    }
    if(messages[0]) {
        free(messages[0]);
    }

    // create fits of same size
    dp_output("size: [%i, %i]\n", nx, ny);
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    if (!rv.fvalue->create(nx, ny))
        throw dpuserTypeException("Failed creating return fits-array");

    dpString sp = dpString(" ");
    key = "t";
    try {
        while ((key != "q") && (xpa != NULL)) {

            ret = XPAGet(xpa, xpaServer, "imexam key coordinate image", NULL,
                         bufs, lens, names, messages, NXPA);

            if( messages[0] == NULL ){
                /* extract key and x/y-positions */
                tmp = bufs[0];
                tmp = tmp.stripWhiteSpace();
                pos = tmp.strpos(sp, false);
                key = tmp.left(pos);
                tmp.remove(0,pos);
                tmp = tmp.stripWhiteSpace();
                nx = atoi(tmp.left(tmp.strpos(sp, false)).c_str());
                ny = atoi(tmp.right(tmp.length() - tmp.strpos(sp, false)).c_str());
                free(bufs[0]);

                if (key != "d") {
                    if (key != "q") {
                        // mark point
                        tmp = "regions command { point " + dpString::number(nx) +
                                " " + dpString::number(ny) + " }";
                        ret = XPASet(xpa, xpaServer, (char*)tmp.c_str(), NULL,
                                    NULL, 0, names, messages, NXPA);

                        // set positions in return value
                        rv.fvalue->setValue(1.0, nx, ny);

                        dp_output("marked [%d %d]\n", nx, ny);
                    }
                } else {
                    // unmark point
                    ret = XPASet(xpa, xpaServer, "regions undo", NULL,
                                NULL, 0, names, messages, NXPA);

                    rv.fvalue->setValue(0.0, nx, ny);

                    dp_output("unmarked [%d %d]\n", nx, ny);
                }
            }
            else{
                /* error processing */
                dp_output("ERROR: %s (%s)\n", messages[0], names[0]);
            }
            if(names[0])
                free(names[0]);
            if(messages[0])
                free(messages[0]);
        }
    } catch (std::exception &e) {
        e;	// prevent warning in VS
        dp_output("Lost connection to ds9.\n");
        XPAClose(xpa);
        xpa = NULL;
    }

    return rv;
}

//Alex
/*!
random
Returns a random number between 0 and 1. If SEED is not set, a seed based on the current time is used.
Syntax
result = random([SEED])
Arguments
SEED: An integer number.
See also
function_randomg
*/

dpuserType dpuserFunction_random() {
    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = gsl_rng_uniform(gsl_rng_r);
    return rv;
}

//Alex

dpuserType dpuserFunction_random(dpuserType seed) {
    Dpuser2cUtils::checkFuncArgs("random", seed);

    dpuserType rv;
    rv.type = typeDbl;

    const gsl_rng_type *T;

    gsl_rng_free(gsl_rng_r);
    gsl_rng_default_seed = seed.lvalue;
    T = gsl_rng_default;
    gsl_rng_r = gsl_rng_alloc(T);
    rv.dvalue = gsl_rng_uniform(gsl_rng_r);

    return rv;
}

//Alex
/*!
import
Imports a text file. If the first character in the file is numeric, the values in the file are written into a numerical array. The number of lines in the file then determine NAXIS2, the number of individual entries in the first line of the file determines NAXIS1. Else the text file will be written into a string array.
Syntax
result = import(FILENAME [, /text] [, /number])
Arguments
FILENAME: A string.
Switches
/text: Force to read into a string array
/number: Force to read into a numerical array
*/

dpuserType dpuserFunction_import(dpuserType filename, dpString option) {
    dpuserType arrLength;
    arrLength.type = typeCon;
    arrLength.lvalue = -1;

    return dpuserFunction_import(filename, arrLength, option);
}

//Alex

dpuserType dpuserFunction_import(dpuserType filename, dpuserType arrLength, dpString option) {
    Dpuser2cUtils::checkFuncArgs("import", filename, arrLength);

    dpuserType rv;

    FILE *fd;
    dpStringList inp;
    dpString fl;
    char *newinput;
    int irv, l, s;

    if ((fd = fopen(filename.svalue->c_str(), "rb")) == NULL) {
        dpString s;
        s = "import: Could not open file " + *filename.svalue + " for reading\n";
        throw dpuserTypeException(const_cast<char*>(s.c_str()));
    }
    if ((newinput = (char *)malloc(256 * sizeof(char))) == NULL) {
        fclose(fd);
        throw dpuserTypeException("import: Couldn't allocate enough memory.\n");
    }
    strcpy(newinput, "");
    irv = 0;
    while (irv != EOF) {
        l = s = 0;
        do {
            irv = fscanf(fd, "%c", &newinput[l++]);
            s++;
            if (s == 255) {
                if ((newinput = (char *)realloc(newinput, (l + 256) * sizeof(char))) == NULL) {
                    fclose(fd);
                    throw dpuserTypeException("import: Couldn't reallocate memory.\n");
                }
                s = 0;
            }
        } while ((newinput[l-1] != '\n') && (irv != EOF));
        l--;
        while (((newinput[l] == '\n') || (newinput[l] == '\r')) && (l > -1)) l--;
        newinput[l+1] = (char)0;
        if (irv != EOF) {
            inp.append(newinput);
            free(newinput);
            if ((newinput = (char *)malloc(256 * sizeof(char))) == NULL) {
                fclose(fd);
                throw dpuserTypeException("import: Couldn't allocate enough memory.\n");
            }
            strcpy(newinput, "");
            s = 0;
        }
    }
    fclose(fd);
    free(newinput);
    int y = 0, x = 0, i, j, c = 0;
    dpChar f;

    fl = inp[0];
    fl = fl.stripWhiteSpace();
    f = fl[0];
    if (option == "number") {
        rv.type = typeStrarr;
        rv.arrvalue = CreateStringArray(inp);
    } else if ((f.isDigit()) || (f == '.') || (f == '-') || (option == "text")) {
        dpStringList line;

        rv.type = typeFits;
        rv.fvalue = CreateFits();

        fl = inp[0];
        fl = fl.simplifyWhiteSpace();
        if (arrLength.lvalue < 0)
            x = fl.contains(' ') + 1;
        else
            x = arrLength.lvalue;
        y = inp.count();
        dp_output("File is %i x %i\n", x, y);
        if (!rv.fvalue->create(x, y, R8)) {
            throw dpuserTypeException("import: function aborted.");
        }
        dpProgress(-y, "");
        for (i = 0; i < y; i++) {
            dpProgress(i, "importing...");
            c = rv.fvalue->C_I(0, i);
            fl = inp[i];
            fl = fl.simplifyWhiteSpace();
            line = dpStringList::split(' ', fl);
            for (j = 0; j < x; j++) {
                if (j < line.count()) rv.fvalue->r8data[c++] = atof(line[j].c_str());
            }
        }
        dpProgress(y, "");
    } else {
        rv.type = typeStrarr;
        rv.arrvalue = CreateStringArray(inp);
    }

    return rv;
}

//Alex
/*!
rebin
Rebins X to the new axis lengths X1 and X2 using a bilinear interpolation. The aspect ratio is not preserved.
Syntax
result = rebin(X, X1, X2)
Arguments
X: A matrix.
X1: An integer number.
X2: An integer number.
*/

dpuserType dpuserFunction_rebin(dpuserType X, dpuserType x1, dpuserType x2) {
    Dpuser2cUtils::checkFuncArgs("rebin", X, x1, x2);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->rebin(x1.lvalue, x2.lvalue);

    return rv;
}

//Alex
/*!
stringarray
Returns a string array. If X is an integer number, a string array will have X entries which are all initialized to empty strings. If X is a string, the string array will initially have one element which is set to X.
Syntax
result = stringarray(X)
Arguments
X: A positive integer number.
*/

dpuserType dpuserFunction_stringarray(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("stringarray", X);

    dpuserType rv;

    if (X.type == typeStr) {
        rv.type = typeStrarr;
        rv.arrvalue = CreateStringArray();
        rv.arrvalue->append(*X.svalue);
    } else {
        rv.type = typeStrarr;
        rv.arrvalue = CreateStringArray(X.lvalue);
    }

    return rv;
}

/*!
nelements
Returns the number of elements in X. For a matrix, this is the total number of pixels, for a string, the number of characters, and for a stringarray, the number of strings.
Syntax
result = nelements(X)
Arguments
X: A matrix, string, or string array.
*/

dpuserType dpuserFunction_nelements(dpuserType arg) {
    dpuserType rv;

    rv.type = typeCon;
    switch (arg.type) {
        case typeFits: rv.lvalue = arg.fvalue->Nelements(); break;
        case typeStr: rv.lvalue = arg.svalue->length(); break;
        case typeStrarr: rv.lvalue = arg.arrvalue->count(); break;
        default: rv.lvalue = 1; break;
    }

    return rv;
}

//Alex
/*!
ssastat
Returns statistics for ssa. Arguments like ssa. The resulting array will be [xshift, yshift, min, max, flux, fwhm, flag].
Syntax
result = ssastat(X, XCENTER, YCENTER, SKY, FLAT, DPL, MASK [, METHOD])
Arguments
X: A FITS datacube
XCENTER: The pixel where to shift in the first dimension
YCENTER: The pixel where to shift in the second dimension
SKY: A sky to be subtracted
FLAT: A flatfield to be multiplied
DPL: A dead pixel list to be applied
MASK: The peak pixel will be searched for in an image multiplied by this mask
METHOD: Optional, controls how the SSA will be done:
METHOD = 0: Shift the brightest pixel to XCENTER, YCENTER (default)
METHOD = 1: Integer shift the centroid to XCENTER, YCENTER
METHOD = 2: Subpixel shift the centroid to XCENTER, YCENTER
METHOD < 0: the same is done as if METHOD were set to 0, but the brightest pixel is searched in a smoothed version of the image. The smoothing is done by convolving with a gaussian of fwhm the negative of method.
See also
function_ssa
function_sssa
*/

dpuserType dpuserFunction_ssastat(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask) {
    dpuserType method;
    method.type = typeCon;
    method.lvalue = 0;

    return dpuserFunction_ssastat(X, xcenter, ycenter, sky, flat, dpl, mask, method);
}
//Alex

dpuserType dpuserFunction_ssastat(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask, dpuserType method) {
    Dpuser2cUtils::checkFuncArgs("ssastat", X, xcenter, ycenter, sky, flat, dpl, mask, method);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->ssastat(X.ffvalue->c_str(), xcenter.lvalue, ycenter.lvalue, method.lvalue, sky.fvalue, flat.fvalue, dpl.fvalue, mask.fvalue);

    return rv;
}

//Alex
/*!
sssa
Calculates the simple shift-and-add of all images in X. If X is a FITS file, a memory saving method will be used to do the ssa.
Syntax
result = sssa(X, STATS, SKY, FLAT, DPL, MASK [, METHOD])
Arguments
X: A FITS datacube
STATS: An array created with the ssastat function. If its flag is not 1, the image will be skipped
SKY: A sky to be subtracted
FLAT: A flatfield to be multiplied
DPL: A dead pixel list to be applied
MASK: The peak pixel will be searched for in an image multiplied by this mask
METHOD: Optional, controls how the SSA will be done:
METHOD = 0: Shift the brightest pixel to XCENTER, YCENTER (default)
METHOD = 1: Integer shift the centroid to XCENTER, YCENTER
METHOD = 2: Subpixel shift the centroid to XCENTER, YCENTER
See also
function_ssa
function_ssastat
*/

dpuserType dpuserFunction_sssa(dpuserType X, dpuserType stats, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask) {
    dpuserType method;
    method.type = typeCon;
    method.lvalue = 0;

    return dpuserFunction_sssa(X, stats, sky, flat, dpl, mask, method);
}
//Alex

dpuserType dpuserFunction_sssa(dpuserType X, dpuserType stats, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask, dpuserType method) {
    Dpuser2cUtils::checkFuncArgs("sssa", X, stats, sky, flat, dpl, mask, method);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->sssa(X.ffvalue->c_str(), *stats.fvalue, method.lvalue, sky.fvalue, flat.fvalue, dpl.fvalue, mask.fvalue);

    return rv;
}

//Alex
/*!
ssaselect
Obsolete function
*/

dpuserType dpuserFunction_ssaselect(dpuserType X, dpuserType x1, dpuserType x2, dpuserType x3, dpuserType x4, dpuserType x5) {
    Dpuser2cUtils::checkFuncArgs("ssaselect", X, x1, x2, x3, x4, x5);

    dpuserType rv;
    rv.clone(X);

    int i;
    float s1, s2;

    for (i = 1; i <= rv.fvalue->Naxis(2); i++) {
        s1 = rv.fvalue->ValueAt(rv.fvalue->F_I(1, i));
        s2 = rv.fvalue->ValueAt(rv.fvalue->F_I(2, i));
        if ((sqrt(s1 * s1 + s2 * s2) > x1.dvalue) ||
            (rv.fvalue->ValueAt(rv.fvalue->F_I(3, i)) < x2.dvalue) ||
            (rv.fvalue->ValueAt(rv.fvalue->F_I(4, i)) < x3.dvalue) ||
            (rv.fvalue->ValueAt(rv.fvalue->F_I(5, i)) < x4.dvalue) ||
            (rv.fvalue->ValueAt(rv.fvalue->F_I(6, i)) > x5.dvalue))
            rv.fvalue->setValue(0.0, 7, i);
    }

    return rv;
}

//Alex
/*!
maxentropy
Maximum entropy deconvolution of X using PSF.
Syntax
result = maxentropy(X, PSF, N [, MASK])
Arguments
X: A matrix.
PSF: A matrix.
N: An integer number.
MASK: An optional mask of image regions to be used.
*/

dpuserType dpuserFunction_maxentropy(dpuserType X, dpuserType PSF, dpuserType n) {
    dpuserType mask;
    mask.type = typeDbl;
    mask.dvalue = 1e-9;	// default value of maxEntropy() in fits.h

    return dpuserFunction_maxentropy(X, PSF, n, mask);
}

//Alex

dpuserType dpuserFunction_maxentropy(dpuserType X, dpuserType PSF, dpuserType n, dpuserType mask) {
    Dpuser2cUtils::checkFuncArgs("maxentropy", X);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->maxEntropy(*PSF.fvalue, n.lvalue, mask.dvalue);

    return rv;
}


//Alex
/*!
cubeminimum
Returns the minimum of each pixel along the 3rd axis if X is a cube.
Syntax
result = cubeminimum(X)
Arguments
X: A matrix or a FITS file name. If a FITS file name is given, only one image is read into memory at a time thus saving memory.
*/

dpuserType dpuserFunction_cubeminimum(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("cubeminimum", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->CubeMinimum(*X.fvalue);

    return rv;
}


//Alex
/*!
cubemaximum
Returns the maximum of each pixel along the 3rd axis if X is a cube.
Syntax
result = cubemaximum(X)
Arguments
X: A matrix or a FITS file name. If a FITS file name is given, only one image is read into memory at a time thus saving memory.
*/

dpuserType dpuserFunction_cubemaximum(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("cubemaximum", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->CubeMaximum(*X.fvalue);

    return rv;
}

//Alex
/*!
fwhm
Returns the second order moment of X centered at [XCENTER, YCENTER] within RADIUS, which corresponds to the FWHM at that location. The parameter RADIUS is optional. If skipped, a default of 5 pixels will be used.
Syntax
result = fwhm(X, XCENTER, YCENTER [, RADIUS])
Arguments
X: A matrix.
XCENTER: An integer number.
YCENTER: An integer number.
RADIUS: An integer number.
*/

dpuserType dpuserFunction_fwhm(dpuserType X, dpuserType xcenter, dpuserType ycenter) {
    Dpuser2cUtils::checkFuncArgs("fwhm", X);

    dpuserType radius;
    radius.type = typeCon;
    radius.lvalue = 5;	// default value of get_fwhm() in fits.h

    return dpuserFunction_fwhm(X, xcenter, ycenter, radius);
}

//Alex

dpuserType dpuserFunction_fwhm(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType radius) {
    Dpuser2cUtils::checkFuncArgs("fwhm", X, xcenter, ycenter, radius);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = (double)X.fvalue->get_fwhm(xcenter.lvalue, ycenter.lvalue, radius.lvalue);

    return rv;
}

//Alex
/*!
setbitpix
Change the pixel type of X.
Syntax
result = setbitpix(X, BITPIX [, BSCALE [, BZERO]])
Arguments
X: The array whose pixel type is to be changed
BITPIX: Desired pixel type:
BITPIX = -128: Complex (note that this does not conform to FITS standard)
BITPIX =  -64: Floating point, double precision
BITPIX =  -32: Floating point, single precision
BITPIX =    8: Unsigned char
BITPIX =   16: Short
BITPIX =   32: Long
BITPIX =   64: Long Long
BSCALE, BZERO: For integer BITPIX, optionally BSCALE and BZERO can be given. The value represented by each pixel is given by value * BSCALE + BZERO (the defaults are BSCALE = 1 and BZERO = 0)
See also
function_getbitpix
*/

dpuserType dpuserFunction_setbitpix(dpuserType X, dpuserType bitpix) {
    dpuserType bscale;
    bscale.type = typeDbl;
    bscale.dvalue = 1.0;	// default value of setType() in fits.h

    return dpuserFunction_setbitpix(X, bitpix, bscale);
}

//Alex

dpuserType dpuserFunction_setbitpix(dpuserType X, dpuserType bitpix, dpuserType bscale) {
    dpuserType bzero;
    bzero.type = typeDbl;
    bzero.dvalue = 0.0;	// default value of setType() in fits.h

    return dpuserFunction_setbitpix(X, bitpix, bscale, bzero);
}

//Alex

dpuserType dpuserFunction_setbitpix(dpuserType X, dpuserType bitpix, dpuserType bscale, dpuserType bzero) {
    Dpuser2cUtils::checkFuncArgs("setbitpix", X, bitpix, bscale, bzero);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->setType((FitsBitpix)bitpix.lvalue, bscale.dvalue, bzero.dvalue);

    return rv;
}

//Alex
/*!
bytearray
Returns an array with bitpix = 8 (all pixels are of type unsigned char). bscale will be set to 1.0, bzero to 0.0.
Syntax
result = bytearray(X [, Y [, Z]])
Arguments
X: An integer number specifying the length of the first dimension.
Y: An integer number specifying the length of the second dimension.
Z: An integer number specifying the length of the third dimension.
See also
function_fits
function_shortarray
function_longarray
function_floatarray
function_doublearray
function_complexarray
*/

dpuserType dpuserFunction_bytearray(dpuserType x) {
    dpuserType yval;
    yval.type = typeCon;
    yval.lvalue = 1;

    return dpuserFunction_bytearray(x, yval);
}

//Alex

dpuserType dpuserFunction_bytearray(dpuserType x, dpuserType y) {
    dpuserType zval;
    zval.type = typeCon;
    zval.lvalue = 1;

    return dpuserFunction_bytearray(x, y, zval);
}

//Alex

dpuserType dpuserFunction_bytearray(dpuserType x, dpuserType y, dpuserType z) {
    Dpuser2cUtils::checkFuncArgs("bytearray", x, y, z);

    return createarray(x.lvalue, y.lvalue, z.lvalue, I1);
}

//Alex
/*!
shortarray
Returns an array with bitpix = 16 (all pixels are of type short). bscale will be set to 1.0, bzero to 0.0.
Syntax
result = short(array(X [, Y [, Z]])
Arguments
X: An integer number specifying the length of the first dimension.
Y: An integer number specifying the length of the second dimension.
Z: An integer number specifying the length of the third dimension.
See also
function_fits
function_bytearray
function_longarray
function_floatarray
function_doublearray
function_complexarray
*/

dpuserType dpuserFunction_shortarray(dpuserType x) {
    dpuserType yval;
    yval.type = typeCon;
    yval.lvalue = 1;

    return dpuserFunction_shortarray(x, yval);
}

//Alex

dpuserType dpuserFunction_shortarray(dpuserType x, dpuserType y) {
    dpuserType zval;
    zval.type = typeCon;
    zval.lvalue = 1;

    return dpuserFunction_shortarray(x, y, zval);
}

//Alex

dpuserType dpuserFunction_shortarray(dpuserType x, dpuserType y, dpuserType z) {
    Dpuser2cUtils::checkFuncArgs("shortarray", x, y, z);

    return createarray(x.lvalue, y.lvalue, z.lvalue, I2);
}

//Alex
/*!
longarray
Returns an array with bitpix = 32 (all pixels are of type long). bscale will be set to 1.0, bzero to 0.0.
Syntax
result = longarray(X [, Y [, Z]])
Arguments
X: An integer number specifying the length of the first dimension.
Y: An integer number specifying the length of the second dimension.
Z: An integer number specifying the length of the third dimension.
See also
function_fits
function_bytearray
function_shortarray
function_floatarray
function_doublearray
function_complexarray
*/

dpuserType dpuserFunction_longarray(dpuserType x) {
    dpuserType yval;
    yval.type = typeCon;
    yval.lvalue = 1;

    return dpuserFunction_longarray(x, yval);
}

//Alex

dpuserType dpuserFunction_longarray(dpuserType x, dpuserType y) {
    dpuserType zval;
    zval.type = typeCon;
    zval.lvalue = 1;

    return dpuserFunction_longarray(x, y, zval);
}

//Alex

dpuserType dpuserFunction_longarray(dpuserType x, dpuserType y, dpuserType z) {
    Dpuser2cUtils::checkFuncArgs("longarray", x, y, z);

    return createarray(x.lvalue, y.lvalue, z.lvalue, I4);
}

//Alex
/*!
floatarray
Returns an array with bitpix = -32 (all pixels are of type float).
Syntax
result = floatarray(X [, Y [, Z]])
Arguments
X: An integer number specifying the length of the first dimension.
Y: An integer number specifying the length of the second dimension.
Z: An integer number specifying the length of the third dimension.
See also
function_fits
function_bytearray
function_shortarray
function_longarray
function_doublearray
function_complexarray
*/

dpuserType dpuserFunction_floatarray(dpuserType x) {
    dpuserType yval;
    yval.type = typeCon;
    yval.lvalue = 1;

    return dpuserFunction_floatarray(x, yval);
}

//Alex

dpuserType dpuserFunction_floatarray(dpuserType x, dpuserType y) {
    dpuserType zval;
    zval.type = typeCon;
    zval.lvalue = 1;

    return dpuserFunction_floatarray(x, y, zval);
}

//Alex

dpuserType dpuserFunction_floatarray(dpuserType x, dpuserType y, dpuserType z) {
    Dpuser2cUtils::checkFuncArgs("floatarray", x, y, z);

    return createarray(x.lvalue, y.lvalue, z.lvalue, R4);
}

//Alex
/*!
doublearray
Returns an array with bitpix = -64 (all pixels are of type double).
Syntax
result = doublearray(X [, Y [, Z]])
Arguments
X: An integer number specifying the length of the first dimension.
Y: An integer number specifying the length of the second dimension.
Z: An integer number specifying the length of the third dimension.
See also
function_fits
function_bytearray
function_shortarray
function_longarray
function_floatarray
function_complexarray
*/

dpuserType dpuserFunction_doublearray(dpuserType x) {
    dpuserType yval;
    yval.type = typeCon;
    yval.lvalue = 1;

    return dpuserFunction_doublearray(x, yval);
}

//Alex

dpuserType dpuserFunction_doublearray(dpuserType x, dpuserType y) {
    dpuserType zval;
    zval.type = typeCon;
    zval.lvalue = 1;

    return dpuserFunction_doublearray(x, y, zval);
}

//Alex

dpuserType dpuserFunction_doublearray(dpuserType x, dpuserType y, dpuserType z) {
    Dpuser2cUtils::checkFuncArgs("doublearray", x, y, z);

    return createarray(x.lvalue, y.lvalue, z.lvalue, R8);
}

//Alex
/*!
complexarray
Returns an array with bitpix = -128 (all pixels are of type complex). !!! WARNING: bitpix = -128 does NOT conform to the FITS standard !!!
Syntax
result = complexarray(X [, Y [, Z]])
Arguments
X: An integer number specifying the length of the first dimension.
Y: An integer number specifying the length of the second dimension.
Z: An integer number specifying the length of the third dimension.
See also
function_fits
function_bytearray
function_shortarray
function_longarray
function_floatarray
function_doublearray
*/

dpuserType dpuserFunction_complexarray(dpuserType x) {
    dpuserType yval;
    yval.type = typeCon;
    yval.lvalue = 1;

    return dpuserFunction_complexarray(x, yval);
}

//Alex

dpuserType dpuserFunction_complexarray(dpuserType x, dpuserType y) {
    dpuserType zval;
    zval.type = typeCon;
    zval.lvalue = 1;

    return dpuserFunction_complexarray(x, y, zval);
}

//Alex

dpuserType dpuserFunction_complexarray(dpuserType x, dpuserType y, dpuserType z) {
    Dpuser2cUtils::checkFuncArgs("complexarray", x, y, z);

    return createarray(x.lvalue, y.lvalue, z.lvalue, C16);
}

//Alex
/*!
shrink
Shrinks X or the specified axis of X by specified FACTOR
Syntax
result = shrink(X, FACTOR [, AXIS])
Arguments
X: A matrix.
FACTOR: An integer number.
AXIS: optionally, which axis to shrink (1, 2, or 3)
*/

dpuserType dpuserFunction_shrink(dpuserType X, dpuserType factor) {
    Dpuser2cUtils::checkFuncArgs("shrink", X, factor);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->shrink(factor.lvalue);

    return rv;
}

dpuserType dpuserFunction_shrink(dpuserType X, dpuserType axis, dpuserType factor) {
    Dpuser2cUtils::checkFuncArgs("shrink", X, axis, factor);

    dpuserType rv;
    rv.clone(X);
    rv.fvalue->shrink(axis.lvalue, factor.lvalue);

    return rv;
}

//Alex
/*!
getfitskey
Returns the value of specified FITS key in the header of X. The returned type can be either a string, an integer number or a real number. If the specified key does not exist, an empty string is returned.
Syntax
result = getfitskey(X, KEY [, extension] [, /text])
Arguments
X: A matrix or a FITS file.
KEY: A string.
extension: Integer number specifying which extension to read.
Switches
text: Return the fits key as a string.
*/

dpuserType dpuserFunction_getfitskey(dpuserType X, dpuserType key, dpString option) {
    Dpuser2cUtils::checkFuncArgs("getfitskey", X, key);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString("");

    char retstr[80];
    retstr[0] = '\0';

    int retint;
    double retdouble;
    Fits *fvalue;

    if (X.type == typeFits) {
        X.fvalue->updateHeader();
        fvalue = X.fvalue;
    } else {
        fvalue = CreateFits();
        if (fvalue->OpenFITS(X.ffvalue->c_str())) {
            fvalue->hdr = fvalue->ReadFitsHeader() >= 0;
            fvalue->CloseFITS();
        }
    }

    if (fvalue->GetStringKey(key.svalue->c_str(), retstr)) {
        rv.svalue = CreateString(retstr);
    }

    if (option != "text") {
        if (fvalue->GetIntKey(key.svalue->c_str(), &retint)) {
            if (fvalue->GetFloatKey(key.svalue->c_str(), &retdouble)) {
                if (retdouble == (double)retint) {
                    rv.type = typeCon;
                    rv.lvalue = (long)retint;
                } else {
                    rv.type = typeDbl;
                    rv.dvalue = retdouble;
                }
            }
        } else {
            dp_output("%s: No such Fits key\n", key.svalue->c_str());
        }
    }

    return rv;
}

//Alex
/*!
polyfit
Returns a N-th order polynomial fit to X. X must be 1-dimensional. The function returns a vector of size (N+1)*2. The fittet polynomial has the following form:
<code>f(x) = a0 + a1 * x + ... + aN * X^N
The values a0, ..., aN are returned in the first N elements of the returned vector, their respective errors in the elements N+1...2*N.
Syntax
result = polyfit(X, N [, CHISQ])
Arguments
X: A vector.
N: An integer number.
CHISQ: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
See also
function_polyfitxy
function_polyfitxyerr
function_polyfit2d
function_polyfit2derr
*/

dpuserType dpuserFunction_polyfit(dpuserType X, dpuserType N) {
    Dpuser2cUtils::checkFuncArgs("polyfit", X, N);	dpuserType rv;

    calcpolyfit(rv, X, N);
    return rv;
}

//Alex

//dpuserType dpuserFunction_polyfit(dpuserType X, dpuserType N, dpuserType CHISQ) {
//    Dpuser2cUtils::checkFuncArgs("polyfit", X, N, CHISQ);

//    dpuserType rv;

//    variables[CHISQ.lvalue].type = typeDbl;
//    variables[CHISQ.lvalue].dvalue = calcpolyfit(rv, X, N);
//    return rv;
//}

//Alex
/*!
sprintf
RETURNS X (either a real number or an integer) as a string using specified format string. The formatting is done like in the C printf function. The total length of the formatted string is limited to 256 characters.
Syntax
result = sprintf(FORMAT, X)
*/

dpuserType dpuserFunction_sprintf(dpuserType format, dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("sprintf", format, X);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString();

    if (X.type == typeCon)
        rv.svalue->sprintf(X.svalue->c_str(), format.lvalue);
    else
        rv.svalue->sprintf(X.svalue->c_str(), format.dvalue);

    return rv;
}

//Alex
/*!
photometry
Perform aperture photometry on X. This function returns a 3xn matrix with the following information: measured counts, normalized counts (Jansky), magnitude.
Syntax
result = photometry(X, POSLIST, REFLIST, SEARCHBOX, RADIUS, [,/u | /b | /v | /r | /i | /j | /h | /k | /l | /m | /n])
Arguments
X: The image on which to do aperture photometry
POSLIST: A 2xn matrix containing the positions of the stars
REFLIST: A 1xm matrix containing magnitudes of reference stars (in the same order as in poslist), with a value > 20.0 if not to be used
SEARCHBOX: Within a box with this radius the local maximum of the stars are searched
RADIUS: The radius of the circular aperture used for photometry
Switches
/u: Do photometry for U-Band (epsilon = 3.274)
/b: Do photometry for B-Band (epsilon = 3.674)
/v: Do photometry for V-Band (epsilon = 3.581)
/r: Do photometry for R-Band (epsilon = 3.479)
/i: Do photometry for I-Band (epsilon = 3.386)
/j: Do photometry for J-Band (epsilon = 3.248)
/h: Do photometry for H-Band (epsilon = 3.08)
/k: Do photometry for K-Band  (epsilon = 2.8) (default)
/l: Do photometry for L-Band (epsilon = 2.49)
/m: Do photometry for M-Band (epsilon = 2.26)
/n: Do photometry for N-Band (epsilon = 1.63)
*/

dpuserType dpuserFunction_photometry(dpuserType X, dpuserType poslist, dpuserType reflist, dpuserType searchbox, dpuserType radius, dpString option) {
    Dpuser2cUtils::checkFuncArgs("photometry", X, poslist, reflist, searchbox, radius);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    double refsum, meassum, scale, epsilon;

    if (option == "u")
        epsilon = 3.274;
    else if (option == "b")
        epsilon = 3.647;
    else if (option == "v")
        epsilon =3.581;
    else if (option == "r")
        epsilon = 3.479;
    else if (option == "i")
        epsilon = 3.386;
    else if (option == "j")
        epsilon = 3.248;
    else if (option == "h")
        epsilon = 3.08;
    else if (option == "k")	//default
        epsilon = 2.8;
    else if (option == "l")
        epsilon = 2.49;
    else if (option == "m")
        epsilon =  2.26;
    else if (option == "n")
        epsilon = 1.63;
    else
        epsilon = 2.8;

    X.fvalue->multiple_phot(*poslist.fvalue, radius.lvalue, rv.fvalue, 1, searchbox.lvalue);
    rv.fvalue->resize(3, -1);
    meassum = refsum = 0.0;
    int i = 0;
    for (i = 0; i < reflist.fvalue->Naxis(2); i++) {
        if (reflist.fvalue->ValueAt(i) < 20.0) {
            if (rv.fvalue->ValueAt(rv.fvalue->C_I(0, i)) > 0.0) {
                meassum += rv.fvalue->ValueAt(rv.fvalue->C_I(0, i));
                refsum += pow(10.0, epsilon - 0.4 * reflist.fvalue->ValueAt(i));
            }
        }
    }
    scale = refsum / meassum;
    for (i = 0; i < rv.fvalue->Naxis(2); i++) {
        rv.fvalue->r8data[i * 3 + 1] = rv.fvalue->r8data[i * 3] * scale;
        rv.fvalue->r8data[i * 3 + 2] = (epsilon - log10(rv.fvalue->r8data[i * 3 + 1])) / 0.4;
    }

    return rv;
}

//Alex
/*!
transcoords
Transform a set of coordinates from one image to another. The coordinate transformation matrix is described by a first order polynomial if the number of reference sources is <= 6, else a second order polynomial is used. The transformation function will be calculated using a least squares fit to given coordinates. Note that if either in REFSTARS or in IMSTARS a coordinate is exactly (-1, -1), this star will not be used to calculate the transformation matrix.
Syntax
result = transcoords(MASTERLIST, REFSTARS, IMSTARS [, xerror, yerror][, /silent] [, /linear] [, /cubic] [, /rotation])
Arguments
MASTERLIST: A matrix 2xn with the coordinates to be transformed
REFSTARS: A matrix 2xm (m >= 3) with positions of the reference stars in the reference frame (= masterlist)
IMSTARS: A matrix 2xm (same size as REFSTARS) with the positions of the same stars in the image to be transformed.
xerror: If set to a named variable, the residual error of the coordinate transform in the first axis is returned.
yerror: If set to a named variable, the residual error of the coordinate transform in the second axis is returned.
Switches
/silent: Printing of output is suppressed
/linear: A 1st order polynomial is fitted (even when more than 6 reference sources are supplied)
/cubic: A 3rd order polynomial is fitted (at least 10 reference sources)
/rotation: A rotational transformation is fitted (at least 4 reference sources): x' = x0 + f*cos(a)*x - f*sin(a)*y, y' = y0 + f*sin(a)*x + f*cos(a)*y
See also
function_transmatrix
function_transform
*/

dpuserType dpuserFunction_transcoords(dpuserType masterlist, dpuserType refstars, dpuserType imstars, dpString option1, dpString option2) {
    Dpuser2cUtils::checkFuncArgs("transcoords", masterlist, refstars, imstars);
    dpuserType rv;
    double xerr, yerr;

    calctranscoords(rv, xerr, yerr, masterlist, refstars, imstars, option1, option2);
    return rv;
}

//Alex

//dpuserType dpuserFunction_transcoords(dpuserType masterlist, dpuserType refstars, dpuserType imstars, dpuserType xerror, dpuserType yerror, dpString option1, dpString option2) {
//    Dpuser2cUtils::checkFuncArgs("transcoords", masterlist, refstars, imstars, xerror, yerror);

//    dpuserType rv;
//    double xerr, yerr;

//    if (calctranscoords(rv, xerr, yerr, masterlist, refstars, imstars, option1, option2)) {
//        variables[xerror.lvalue].type = typeDbl;
//        variables[xerror.lvalue].dvalue = xerr;
//        variables[yerror.lvalue].type = typeDbl;
//        variables[yerror.lvalue].dvalue = yerr;
//    }

//    return rv;
//}

//Alex
/*!
findfile
Returns all files that satisfy given PATTERN. All occurrences of a backslash are replaced by a slash. Also, a slash is appended to all directories. The search is started from the current directory and is done recursively through the directory structure.
Syntax
result = findfile(PATTERN)
Arguments
PATTERN: A string.
Examples
Find all text files (files with the suffix .txt) from the current working directory:
<code>print findfile("*.txt")
See also
function_dir
function_fileexists
*/

dpuserType dpuserFunction_findfile(dpuserType pattern) {
    Dpuser2cUtils::checkFuncArgs("findfile", pattern);

    dpuserType rv;
    rv.type = typeStrarr;
    rv.arrvalue = CreateStringArray();

    *rv.arrvalue = dpDir::findfile(*pattern.svalue);

    return rv;
}

//Alex
/*!
markpos
Mark positions using ds9. The positions are returned as a 2xn matrix, where n denotes the number of positions marked. Pressing the key 'q' will quit marking positions. When typing numbers between 1 and 9 the values are stored at that specific position. This way values can be overwritten. (Use ds9 v5.2 or newer!)
Syntax
result = markpos(X [, int radius] [, /nocenter])
Arguments
X: The image to be displayed on saoimage
radius: Optional, the radius around which the brightest pixel is searched for using centroid-method.
Switches
nocenter: Has no effect when no radius is supplied. Returns the local maximum in the radius around the marked pixel.
See also
function_saomark
*/

dpuserType dpuserFunction_markpos(dpuserType X, dpString option) {
    dpuserType radius;
    radius.type = typeCon;
    radius.lvalue = 0;

    return dpuserFunction_markpos(X, radius, option);
}

//Alex

dpuserType dpuserFunction_markpos(dpuserType X, dpuserType radius, dpString option) {
    Dpuser2cUtils::checkFuncArgs("markpos", X, radius);

    if (ds9_running() == 0) {
        throw dpuserTypeException("ERROR: No ds9 is running! Please start first ds9 (at least v5.2)\n");
    }
    if (xpa == NULL) {
        if ((xpa = XPAOpen(NULL)) == NULL) {
            throw dpuserTypeException("Can't open socket to image display server.");
        }
    }

    dpuserType rv;
    int nx, ny, count, ret, pos, x, y, success = 1;
    float *data1, *pix;
    double fx, fy;
    dpString tmp, key;
    Fits im, ci;
    size_t lens[NXPA];
    char *bufs[NXPA];
    char *names[NXPA];
    char *messages[NXPA];

    nx = X.fvalue->Naxis(1);
    ny = X.fvalue->Naxis(2);

    if ((pix = (float *)malloc(X.fvalue->Nelements() * sizeof(float))) == NULL)
        throw dpuserTypeException("ERROR: Failed allocating memory!");
    data1 = floatdata(*X.fvalue);
    if (data1)
        memcpy(pix, data1, X.fvalue->Nelements() * sizeof(float));
    else {
        free(pix);
        throw dpuserTypeException("ERROR: Failed copying memory!");
    }
    if (data1 && (data1 != X.fvalue->r4data))
        free(data1);

    // display fits
    dpString accessPoint = "array [xdim=" + dpString::number(nx) +
                           ",ydim=" + dpString::number(ny) +
                           ",bitpix=-32]";

    ret = XPASet(xpa, xpaServer, (char*)accessPoint.c_str(), NULL,
                 (char*)pix, nx * ny * sizeof(float),
                 names, messages, NXPA);

    /* error processing */
    for(int i = 0; i < ret; i++){
        if(messages[i]) {
            dp_output("ERROR: %s (%s)\n", messages[i], names[i]);
        }
        if(names[i]) {
            free(names[i]);
        }
        if(messages[i]) {
            free(messages[i]);
        }
    }

    count = 0;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    if (!rv.fvalue->create(2,100))
        success = 0;

    if (success)
        if (radius.lvalue != 0)
            if (!im.copy(*X.fvalue))
                success = 0;

    if (success) {
        dpString sp = dpString(" ");
        key = "t";
        try {
            while ((key != "q") && (xpa != NULL)) {
                ret = XPAGet(xpa, xpaServer, "imexam key coordinate image", NULL,
                             bufs, lens, names, messages, NXPA);

                if( messages[0] == NULL ){
                    /* extract key and x/y-positions */
                    tmp = bufs[0];
                    tmp = tmp.stripWhiteSpace();
                    pos = tmp.strpos(sp, false);
                    key = tmp.left(pos);
                    tmp.remove(0,pos);
                    tmp = tmp.stripWhiteSpace();
                    x = atoi(tmp.left(tmp.strpos(sp, false)).c_str());
                    y = atoi(tmp.right(tmp.length() - tmp.strpos(sp, false)).c_str());
                    free(bufs[0]);

                    if (key != "q") {
                        if (radius.lvalue != 0) {
                            if (option != "nocenter") {
                                im.max_pixel(&x, &y, radius.lvalue);
                            }

                            ci.circle(im.Naxis(1), im.Naxis(2), x, y, radius.lvalue);
                            im.mul(ci);
                            im.centroid(&fx, &fy);
                            im.copy(*X.fvalue);
                        } else {
                            fx = (double)x;
                            fy = (double)y;
                        }
                        dp_output("markpos: key %s at (%i, %i)\n", key.c_str(), x, y);

                        char ckey;
                        ckey = *(key.c_str());
                        if ((ckey <= '9') && (ckey >= '1')) {
                            rv.fvalue->r4data[rv.fvalue->C_I(0, ckey - '1')] = (float)fx;
                            rv.fvalue->r4data[rv.fvalue->C_I(1, ckey - '1')] = (float)fy;
                            if (count < ckey - '1')
                                count = ckey - '1';
                            else
                                count--;
                        } else {
                            rv.fvalue->r4data[rv.fvalue->C_I(0, count)] = (float)fx;
                            rv.fvalue->r4data[rv.fvalue->C_I(1, count)] = (float)fy;
                        }
                        count++;
                    }
                }
                else{
                    /* error processing */
                    dp_output("ERROR: %s (%s)\n", messages[0], names[0]);
                }
                if(names[0])
                    free(names[0]);
                if(messages[0])
                    free(messages[0]);
            }
        } catch (std::exception &e) {
            e;	// prevent warning in VS
            dp_output("Lost connection to ds9.\n");
            XPAClose(xpa);
            xpa = NULL;
        }
    }
    rv.fvalue->resize(2, count);

    free(pix);

    return rv;
}

//Alex
/*!
lmst
Calculate the local mean siderial time for given Julian date. If longitude is given (east negative), the local mean sidereal time for that location is returned, else the lmst for Greenwich (longitude 0). NOTE that timezones for given longitude are not taken into account, so always give jd in Universal Time.
Syntax
result = lmst(JD [, LATITUDE])
Arguments
JD: A real number.
LATITUDE: A real number.
See also
function_jd
*/

dpuserType dpuserFunction_lmst(dpuserType jd) {
    dpuserType latitude;
    latitude.type = typeDbl;
    latitude.dvalue = 0.0;

    return dpuserFunction_lmst(jd, latitude);
}

//Alex

dpuserType dpuserFunction_lmst(dpuserType jd, dpuserType latitude) {
    Dpuser2cUtils::checkFuncArgs("lmst", jd, latitude);

    dpuserType rv;
    rv.type = typeDbl;

    double mjd0, mjd, gmst, t, ut;

    mjd = jd.dvalue - 2400000.5;
    mjd0 = (double)((long)mjd);
    ut = (mjd - mjd0) * 24.0;
    t = (mjd0 - 51544.5) / 36525.0;
    gmst = 6.697374558 + 1.0027379093 * ut + (8640184.812866 + (0.093104 - 6.2e-6 * t) * t) * t / 3600.0;
    rv.dvalue = 24.0 * frac((gmst - latitude.dvalue / 15.0) / 24.0);

    return rv;
}

//Alex
/*!
wsa
Compute weighted shift-and-add of the fits cube X (must be a file). The shift location is defined by xcen, ycen. thresh gives the fraction of the maximum pixel which will be regarded as a speckle. speckles determines the maximum number of speckles used (if there are more, then the image will be smoothed until it is not exceeded). smooth optionally gives the fwhm of a gaussian with which the image where speckles are searched for is smoothed. If bigmask is given, the centroid of mask will be shifted to the centroid of bigmask for each individual image.
Syntax
result = wsa(X, XCENTER, YCENTER, THRESHOLD, SPECKLES, SMOOTH, SKY, FLAT, DPL, MASK [, BIGMASK])
Arguments
X: A FITS filename (enclosed in single quotes).
XCENTER: An integer number specifying the shift position in the first dimension.
YCENTER: An integer number specifying the shift position in the second dimension.
THRESHOLD: A real number.
SPECKLES: An integer number.
SMOOTH: An integer number.
SKY: A matrix.
FLAT: A matrix.
DPL: A matrix.
MASK: A matrix.
BIGMASK: A matrix.
*/

dpuserType dpuserFunction_wsa(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType threshold, dpuserType speckles,
                dpuserType smooth, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask) {
    dpuserType bigmask;
    bigmask.type = typeFits;
    bigmask.fvalue = NULL;

    return dpuserFunction_wsa(X, xcenter, ycenter, threshold, speckles, smooth, sky, flat, dpl, mask, bigmask);
}

//Alex

dpuserType dpuserFunction_wsa(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType threshold, dpuserType speckles,
                dpuserType smooth, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask, dpuserType bigmask) {
    Dpuser2cUtils::checkFuncArgs("wsa", X, xcenter, ycenter, threshold, speckles, smooth, sky, flat, dpl, mask, bigmask);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->wsa(X.ffvalue->c_str(), xcenter.lvalue, ycenter.lvalue, threshold.dvalue, speckles.lvalue, smooth.lvalue, sky.fvalue, flat.fvalue, dpl.fvalue, mask.fvalue, bigmask.fvalue);

    return rv;
}

//Alex
/*!
swsa
Compute weighted shift-and-add of the fits cube X (must be a file). flags is a 1-dimensional array that has a value of zero for each image that is to be skipped. The shift location is defined by xcen, ycen. thresh gives the fraction of the maximum pixel which will be regarded as a speckle. speckles determines the maximum number of speckles used (if there are more, then the image will be smoothed until it is not exceeded). smooth optionally gives the fwhm of a gaussian with which the image where speckles are searched for is smoothed. If bigmask is given, the centroid of mask will be shifted to the centroid of bigmask for each individual image.
Syntax
result = wsa(X, FLAGS, XCENTER, YCENTER, THRESHOLD, SPECKLES, SMOOTH, SKY, FLAT, DPL, MASK, BIGMASK)
Arguments
X: A FITS filename (enclosed in single quotes).
FLAGS: A vector.
XCENTER: An integer number specifying the shift position in the first dimension.
YCENTER: An integer number specifying the shift position in the second dimension.
THRESHOLD: A real number.
SPECKLES: An integer number.
SMOOTH: An integer number.
SKY: A matrix.
FLAT: A matrix.
DPL: A matrix.
MASK: A matrix.
BIGMASK: A matrix.
*/

dpuserType dpuserFunction_swsa(dpuserType X, dpuserType flags, dpuserType xcenter, dpuserType ycenter, dpuserType threshold, dpuserType speckles,
                dpuserType smooth, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask) {
    dpuserType bigmask;
    bigmask.type = typeFits;
    bigmask.fvalue = NULL;

    return dpuserFunction_swsa(X, flags, xcenter, ycenter, threshold, speckles, smooth, sky, flat, dpl, mask, bigmask);
}

//Alex

dpuserType dpuserFunction_swsa(dpuserType X, dpuserType flags, dpuserType xcenter, dpuserType ycenter, dpuserType threshold, dpuserType speckles,
                dpuserType smooth, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask, dpuserType bigmask) {
    Dpuser2cUtils::checkFuncArgs("swsa", X, flags, xcenter, ycenter, threshold, speckles, smooth, sky, flat, dpl, mask, bigmask);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->wsa(X.ffvalue->c_str(), xcenter.lvalue, ycenter.lvalue, threshold.dvalue, speckles.lvalue, smooth.lvalue, sky.fvalue, flat.fvalue, dpl.fvalue, mask.fvalue, bigmask.fvalue, flags.fvalue);

    return rv;
}

//Alex
/*!
centroids
Returns a 2xn matrix with the centroids (in a RADIUS) on given POSITIONS in X.
Syntax
result = centroids(X, POSITIONS, RADIUS)
Arguments
X: A matrix.
POSITIONS: A 2xn matrix with initial positions.
RADIUS: An integer number.
See also
function_maxima
function_gausspos
*/

dpuserType dpuserFunction_centroids(dpuserType X, dpuserType positions, dpuserType radius) {
    Dpuser2cUtils::checkFuncArgs("centroids", X, positions, radius);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    X.fvalue->centroids(*rv.fvalue, *positions.fvalue, radius.lvalue);

    return rv;
}

//Alex
/*!
maxima
Refind local maxima in X given POSITIONS around RADIUS.
Syntax
result = maxima(X, POSITIONS, RADIUS [, /circle, /box])
Arguments
X: A matrix.
POSITIONS: A 2xn matrix with initial positions.
RADIUS: An integer number.
Switches
/circle: Local maxima are searched for within a circle with RADIUS
/box: Local maxima are search for within a quadratic box with RADIUS
See also
function_centroids
function_gausspos
*/

/*!
gausspos
Returns a 2xn matrix with 2D gaussfits (in a RADIUS) on given POSITIONS in X.
Syntax
result = gausspos(X, POSITIONS, RADIUS)
Arguments
X: A matrix.
POSITIONS: A 2xn matrix with initial positions.
RADIUS: An integer number.
See also
function_maxima
function_centroids
*/



dpuserType dpuserFunction_maxima(dpuserType X, dpuserType positions, dpuserType radius, dpString option) {
    Dpuser2cUtils::checkFuncArgs("maxima", X, positions, radius);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    if (rv.fvalue->copy(*positions.fvalue)) {
        if ((option == "") || (option == "circle"))
            X.fvalue->maxima(*rv.fvalue, radius.lvalue, 0);
        else if (option == "box")
            X.fvalue->maxima(*rv.fvalue, radius.lvalue, 1);
    }
    else
        throw dpuserTypeException("maxima: Failed copying, not enough memory?\n");

    return rv;
}

//Alex
/*!
fileexists
Test for existence of a file. Returns 1 if the file exist, 0 otherwise.
Syntax
result = fileexists(FILENAME)
Arguments
FILENAME: A string.
See also
function_findfile
function_dir
function_filesize
*/

dpuserType dpuserFunction_fileexists(dpuserType filename) {
    Dpuser2cUtils::checkFuncArgs("fileexists", filename);

    dpuserType rv;
    rv.type = typeCon;
    FILE *fd;

    rv.lvalue = ((fd = fopen(filename.svalue->c_str(), "rb")) != NULL);
    if (rv.lvalue)
        fclose(fd);

    return rv;
}

//Alex
/*!
filesize
Return the length in bytes of a file. Returns -1 if the file does not exist.
Syntax
result = filesize(FILENAME)
Arguments
FILENAME: A string.
See also
function_findfile
function_dir
function_fileexists
*/

dpuserType dpuserFunction_filesize(dpuserType filename) {
    Dpuser2cUtils::checkFuncArgs("filesize", filename);

    dpuserType rv;
    rv.type = typeCon;
    FILE *fd;

    fd = fopen(filename.svalue->c_str(), "rb");
    if (fd != NULL) {
        fseek(fd, 0L, SEEK_END);
        rv.lvalue = ftell(fd);
        fclose(fd);
    } else
        rv.lvalue = -1;

    return rv;
}

//Alex
/*!
dir
Return a directory listing.
Syntax
result = dir([DIRECTORY], [, /recursive])
Arguments
DIRECTORY: Optional, do a directory listing of specified directory, else of the current working directory
Switches
/recursive: Do a recursive listing
Examples
To print a recursive listing of the current directory, type
<code>print dir("", /recursive)
See also
function_findfile
function_filesize
function_fileexists
*/

dpuserType dpuserFunction_dir(dpString option) {
    dpuserType dir;
    dir.type = typeStr;
    *dir.svalue = "*";

    return dpuserFunction_dir(dir, option);
}
//Alex

dpuserType dpuserFunction_dir(dpuserType directory, dpString option) {
    Dpuser2cUtils::checkFuncArgs("dir", directory);

    dpuserType rv;
    rv.type = typeStrarr;
    rv.arrvalue = CreateStringArray();

    dpString filter;
/*
#ifndef NO_QT
    dpStringList filelist;
    dpDir listing, dirs;
    dpString path;
    QFileInfo info;
    bool recursive = (option == "recursive");
    ULONG n;

    filter = *directory.svalue;
    filelist = listing.entryList(filter);
    for (n = 0; n < filelist.count(); n++) {
        if (filelist[n][0].c_str() != '.') *rv.arrvalue += filelist[n];
    }
    if (recursive) {
        dpStringList directories, subdirs;
        dpString root = listing.currentDirPath() + "/";
        long newn;
        ULONG m;
        filelist = dirs.entryList(dpDir::Dirs);
        for (n = 0; n < filelist.count(); n++) {
            if (filelist[n][0].c_str() != '.') directories += filelist[n];
        }
        n = directories.count();
        newn = -1;
        while (n > 0) {
            n--;
            newn++;
            dpDir newdir(root + directories[newn]);
            subdirs = newdir.entryList(dpDir::Dirs);
            for (m = 0; m < subdirs.count(); m++) {
                if (subdirs[m][0].c_str() != '.') {
                    directories.append(directories[newn] + "/" + subdirs[m]);
                    n++;
                }
            }
        }
        for (n = 0; n < directories.count(); n++) {
            listing.setPath(root + directories[n]);
            subdirs = listing.entryList(filter);
            for (m = 0; m < subdirs.count(); m++) rv.arrvalue->append(directories[n] + "/" + subdirs[m]);
        }
    }
#else*/
    filter = *directory.svalue;
    if (filter == "") filter = "*";
    if (option == "recursive") {
        *rv.arrvalue = dpDir::findfile(filter);
    } else {
        *rv.arrvalue = dpDir::dir(filter);
    }
//#endif


    return rv;
}

//Alex
/*!
polyfitxy
Return an n-th order polynomial fit to the vectors X and Y, where y = f(x). If a variable is given as the 4th argument, it will be overwritten with the chi squared of the fit.
Syntax
result = polyfitxy(X, Y, N [, CHISQ])
Arguments
X: A vector.
Y: A vector.
N: An integer number.
CHISQ: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
See also
function_polyfit
function_polyfitxyerr
function_polyfit2d
function_polyfit2derr
*/

dpuserType dpuserFunction_polyfitxy(dpuserType X, dpuserType Y, dpuserType N) {
    Dpuser2cUtils::checkFuncArgs("polyfitxy", X, Y, N);

    dpuserType rv;

    calcpolyfitxy(rv, X, Y, N);
    return rv;
}

//Alex

//dpuserType dpuserFunction_polyfitxy(dpuserType X, dpuserType Y, dpuserType N, dpuserType CHISQ) {
//    Dpuser2cUtils::checkFuncArgs("polyfitxy", X, Y, N, CHISQ);

//    dpuserType rv;

//    variables[CHISQ.lvalue].type = typeDbl;
//    variables[CHISQ.lvalue].dvalue = calcpolyfitxy(rv, X, Y, N);
//    return rv;
//}

//Alex
/*!
sort
Returns an index list of X in ascending order.
Syntax
result = sort(X [, /reverse])
Arguments
X: A matrix, a string or a string array.
Switches
/reverse: The index list will be in descending order
Examples
To sort a directory listing in alphabetical order, type:
<code>list = dir("/tmp"); list = list[sort(list)]
*/

dpuserType dpuserFunction_sort(dpuserType X, dpString option) {
    Dpuser2cUtils::checkFuncArgs("sort", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    bool doOption = option == "reverse";

    if (X.type == typeFits)
        rv.fvalue->createFitsIndex(*X.fvalue, doOption);
    else if (X.type == typeStr)
        createStringIndex(*rv.fvalue, *X.svalue, doOption);
    else if (X.type == typeStrarr)
        createStringArrayIndex(*rv.fvalue, *X.arrvalue, doOption);

    return rv;
}

//Alex
/*!
char
Returns the ASCII character which number is N. N is truncated to the range 0...255.
Syntax
result = char(N)
Arguments
N: An integer number.
Examples
To print out the ASCII character set, type:
<code>for (i = 32; i < 256; i++) print char(i)
*/

dpuserType dpuserFunction_char(dpuserType n) {
    Dpuser2cUtils::checkFuncArgs("char", n);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString();

    char value[2];
    value[1] = 0;
    value[0] = (char)n.lvalue;
    rv.svalue->append(value);

    return rv;
}

//Alex
/*!
complex
Returns a complex number generated of the two given values.
Syntax
result = complex(r, i)
Arguments
r: The real part (either a real number or a matrix)
i: the imaginary part (either a real number or a matrix)
See also
function_real
function_imag
function_conj
function_arg
function_abs
*/

dpuserType dpuserFunction_complex(dpuserType re, dpuserType im) {
    Dpuser2cUtils::checkFuncArgs("complex", re, im);

    dpuserType rv;

    if (re.type != im.type) {
        throw dpuserTypeException("complex: Arguments must be of same type\n");
    }
    if (re.type == typeFits) {
        rv.fvalue = CreateFits();
        rv.fvalue->rr_ri(*re.fvalue, *im.fvalue);
    } else {
        rv.type = typeCom;
        rv.cvalue = CreateComplex(re.dvalue, im.dvalue);
    }

    return rv;
}

//Alex
/*!
strpos
Returns the position of a substring within a string, or -1 if the substring does not occur. If the first argument is a string array, a matrix with the same number of elements as the string is returned.
Syntax
result = strpos(X, Y)
Arguments
X: A string or a string array
Y: The substring to be searched for
Switches
/nocase: The search is done case-insensitive
*/

dpuserType dpuserFunction_strpos(dpuserType X, dpuserType Y, dpString option) {
    Dpuser2cUtils::checkFuncArgs("strpos", X, Y);

    dpuserType rv;
    bool doOption = option == "nocase";

    if (X.type == typeStr) {
        rv.type = typeCon;
//#ifdef NO_QT
        rv.lvalue = X.svalue->strpos(*Y.svalue, doOption);
/*#else
        rv.lvalue = X.svalue->find(*Y.svalue, 0, doOption);
#endif*/
        if (rv.lvalue > -1) rv.lvalue++;
    } else {
        dpint64 i;
        int l;

        rv.type = typeFits;
        rv.fvalue = CreateFits();
        if (!rv.fvalue->create(X.arrvalue->count(), 1, I4))
            throw dpuserTypeException("strpos: Failed allocating memory\n");
        for (i = 0; i < X.arrvalue->count(); i++) {
//#ifdef NO_QT
            l = (*X.arrvalue)[i].strpos(*Y.svalue, doOption);
/*#else
            l = (*X.arrvalue)[i].find(*Y.svalue, 0, doOption);
#endif*/
            if (l > -1) l++;
            rv.fvalue->i4data[i] = l;
        }
    }

    return rv;
}

//Alex
/*!
clean
Performs "cleaning" of the input image. This iterative deconvolution algorithm
searches for the global maximum in the image, shifts the psf to that position and subtracts a scaled psf (multiplied by the gain). The subtracted flux is added to a delta map at the position of the maximum (this is the clean map). Note that clean does not conserve the total flux in the image.
Syntax
result = clean(IMAGE, PSF, N, GAIN [, variable SUB])
Arguments
IMAGE: input map to be cleaned (dirty map)
PSF: Point spread function (dirty beam)
N: Number of iterations
GAIN: Loop gain factor
SUB: Optional, the variable SUB will contain an image in which the sources are subtracted
See also
function_lucy
function_wien
*/

dpuserType dpuserFunction_clean(dpuserType IMAGE, dpuserType PSF, dpuserType N, dpuserType GAIN) {
    Dpuser2cUtils::checkFuncArgs("clean", IMAGE, PSF, N, GAIN);

    dpuserType rv;
    rv.type = typeFits;
    rv.clone(IMAGE);

    rv.fvalue->clean(*PSF.fvalue, N.lvalue, GAIN.dvalue);
    return rv;
}

//Alex

//dpuserType dpuserFunction_clean(dpuserType IMAGE, dpuserType PSF, dpuserType N, dpuserType GAIN , dpuserType SUB) {
//    Dpuser2cUtils::checkFuncArgs("clean", IMAGE, PSF, N, GAIN , SUB);

//    dpuserType rv;
//    rv.type = typeFits;
//    rv.clone(IMAGE);

//    variables[SUB.lvalue].type = typeFits;
//    variables[SUB.lvalue].fvalue = CreateFits();
//    rv.fvalue->clean(*PSF.fvalue, N.lvalue, GAIN.dvalue, variables[SUB.lvalue].fvalue);
//    return rv;
//}

//Alex
/*!
collapse
Collapse one axis of an array. All values along the specified axis will be summed up. The resulting array will have one dimension less than the input.
Syntax
result = collapse(IMAGE, AXIS)
Arguments
IMAGE: A matrix.
AXIS: An integer number.
Examples
Do a sum of all images in a datacube:
<code>s = collapse(cube, 3)
This is exactly the same as:
<code>s = cubeavg(cube) * naxis3(cube)
See also
function_cubeavg
*/

dpuserType dpuserFunction_collapse(dpuserType image, dpuserType axis) {
    Dpuser2cUtils::checkFuncArgs("collapse", image, axis);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->collapse(*image.fvalue, axis.lvalue);
    return rv;
}

//Alex
/*!
magnify
Resize given array by the scaling factor. Each pixel in the resized array will be blown up and consequently contain scale^2 pixels. The optional argument smooth gives the fwhm of a gaussian with which the resultant array will we smoothed. The total flux in the array will be conserved.
Syntax
result = magnify(IMAGE, scale [, fwhm])
Arguments
IMAGE: A matrix
scale: An integer number.
fwhm: A real number.
*/

dpuserType dpuserFunction_magnify(dpuserType image, dpuserType scale) {
    Dpuser2cUtils::checkFuncArgs("magnify", image, scale);

    dpuserType rv;
    rv.clone(image);
    rv.fvalue->enlarge(scale.lvalue, 2);

    return rv;
}

//Alex

dpuserType dpuserFunction_magnify(dpuserType image, dpuserType scale, dpuserType fwhm) {
    Dpuser2cUtils::checkFuncArgs("magnify", image, scale, fwhm);

    dpuserType rv;
    rv.clone(image);
    rv.fvalue->enlarge(scale.lvalue, 2);
    rv.fvalue->smooth(fwhm.dvalue);

    return rv;
}

//Alex
/*!
wsastat
Compute statistics for the weighted shift-and-add of the fits cube X (must be a file). The shift location is defined by xcen, ycen. thresh gives the fraction of the maximum pixel which will be regarded as a speckle. speckles determines the maximum number of speckles used (if there are more, then the image will be smoothed until it is not exceeded). smooth optionally gives the fwhm of a gaussian with which the image where speckles are searched for is smoothed. If bigmask is given, the centroid of mask will be shifted to the centroid of bigmask for each individual image.
Syntax
result = wsastat(X, XCENTER, YCENTER, THRESHOLD, SPECKLES, SMOOTH, SKY, FLAT, DPL, MASK, BIGMASK)
Arguments
X: A FITS filename (enclosed in single quotes).
XCENTER: An integer number specifying the shift position in the first dimension.
YCENTER: An integer number specifying the shift position in the second dimension.
THRESHOLD: A real number.
SPECKLES: An integer number.
SMOOTH: An integer number.
SKY: A matrix.
FLAT: A matrix.
DPL: A matrix.
MASK: A matrix.
BIGMASK: A matrix.
*/

dpuserType dpuserFunction_wsastat(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType threshold, dpuserType speckles,
                dpuserType smooth, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask) {
    dpuserType bigmask;
    bigmask.type = typeFits;
    bigmask.fvalue = NULL;

    return dpuserFunction_wsastat(X, xcenter, ycenter, threshold, speckles, smooth, sky, flat, dpl, mask, bigmask);
}

//Alex

dpuserType dpuserFunction_wsastat(dpuserType X, dpuserType xcenter, dpuserType ycenter, dpuserType threshold, dpuserType speckles,
                dpuserType smooth, dpuserType sky, dpuserType flat, dpuserType dpl, dpuserType mask, dpuserType bigmask) {
    Dpuser2cUtils::checkFuncArgs("wsastat", X, xcenter, ycenter, threshold, speckles, smooth, sky, flat, dpl, mask, bigmask);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->wsastat(X.ffvalue->c_str(), xcenter.lvalue, ycenter.lvalue, threshold.dvalue, speckles.lvalue, smooth.lvalue, sky.fvalue, flat.fvalue, dpl.fvalue, mask.fvalue, bigmask.fvalue);
    return rv;
}

//Alex
/*!
gaussfit
Fit a one-dimensional gaussian to a vector. The gauss function is defined as G(x) = A + B*exp(-4ln2*(x-x0)^2/fwhm^2).
Syntax
result = gaussfit(X, Y, errors, estimate [, chisq])
Arguments
X: A vector containing the x values to be fit
Y: A vector containing the y values to be fit
errors: The errors in Y
estimate: Initial estimate for the fit of the form [A, B, x0, fwhm]
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [A, B, x0, fwhm, d_A, d_B, d_x0, d_fwhm]
See also
function_evalgaussfit
*/

dpuserType dpuserFunction_gaussfit(dpuserType X, dpuserType Y, dpuserType errors, dpuserType estimate) {
    Dpuser2cUtils::checkFuncArgs("gaussfit", X, Y, errors, estimate);

    dpuserType rv;
    calcgaussfit(rv, X, Y, errors, estimate);
    return rv;
}

//Alex

//dpuserType dpuserFunction_gaussfit(dpuserType X, dpuserType Y, dpuserType errors, dpuserType estimate, dpuserType CHISQ) {
//    Dpuser2cUtils::checkFuncArgs("gaussfit", X, Y, errors, estimate, CHISQ);

//    dpuserType rv;

//    variables[CHISQ.lvalue].type = typeDbl;
//    variables[CHISQ.lvalue].dvalue = calcgaussfit(rv, X, Y, errors, estimate);

//    return rv;
//}

//Alex
/*!
gauss2dfit
Fit a two-dimensional gaussian to a rectangular grid (i.e. an image).
Syntax
result = gauss2dfit(image, errors, estimate [, chisq])
Arguments
image: The image to be fit
errors: The errors in the image values
estimate: Initial estimate for the fit of the form [Offset,Max,x0,y0,fwhm1,fwhm2,angle]
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [Offset, Max, x0, y0, fwhm1, fwhm2, angle, d_Offset, d_Max, d_x0, d_y0, d_fwhm1, d_fwhm2, d_angle]
See also
function_gauss2dsimplefit
function_multigauss2dfit
*/

dpuserType dpuserFunction_gauss2dfit(dpuserType image, dpuserType errors, dpuserType estimate) {
    Dpuser2cUtils::checkFuncArgs("gauss2dfit", image, errors, estimate);

    dpuserType rv;
    calcgauss2dfit(rv, image, errors, estimate);
    return rv;
}

//Alex

//dpuserType dpuserFunction_gauss2dfit(dpuserType image, dpuserType errors, dpuserType estimate, dpuserType CHISQ) {
//    Dpuser2cUtils::checkFuncArgs("gauss2dfit", image, errors, estimate, CHISQ);

//    dpuserType rv;

//    variables[CHISQ.lvalue].type = typeDbl;
//    variables[CHISQ.lvalue].dvalue = calcgauss2dfit(rv, image, errors, estimate);

//    return rv;
//}

//Alex
/*!
polyfitxyerr
Return an n-th order polynomial fit to the vectors X and Y, where y = f(x). The typical error of the dependent variables is given in ERROR. If a variable is given as the 4th argument, it will be overwritten with the chi squared of the fit.
Syntax
result = polyfitxyerr(X, Y, N, ERROR [, CHISQ])
Arguments
X: A vector.
Y: A vector.
N: An integer number.
ERROR: The error of the Y-values. If this is a real number, all Y-values are assumed to have the same error. If ERROR is a vector, it must have the same length as X and Y and gives the error or each individual Y value.
CHISQ: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
See also
function_polyfit
function_polyfitxy
function_polyfit2d
function_polyfit2derr
*/

dpuserType dpuserFunction_polyfitxyerr(dpuserType X, dpuserType Y, dpuserType N, dpuserType error) {
    Dpuser2cUtils::checkFuncArgs("polyfitxyerr", X, Y, N, error);

    dpuserType rv;

    calcpolyfitxyerr(rv, X, Y, N, error);
    return rv;
}

//Alex

//dpuserType dpuserFunction_polyfitxyerr(dpuserType X, dpuserType Y, dpuserType N, dpuserType error, dpuserType CHISQ) {
//    Dpuser2cUtils::checkFuncArgs("polyfitxyerr", X, Y, N, error, CHISQ);

//    dpuserType rv;

//    variables[CHISQ.lvalue].type = typeDbl;
//    variables[CHISQ.lvalue].dvalue = calcpolyfitxyerr(rv, X, Y, N, error);
//    return rv;
//}

//Alex
/*!
psf
Return the average (or median, minimum, maximum) of several stars in an image. The stars locations are given in the second argument.
Syntax
result = psf(X, POSITIONS [, /median] [, /average] [, /minimum] [, /maximum])
Arguments
X: A matrix.
POSITIONS: A 2xn matrix giving the positions of the stars to be averaged.
Switches
/median: Return the median of the stars (default).
/average: Return the average of the stars.
/minimum: Return the minimum of the stars.
/maximum: Return the maximum of the stars.
*/

dpuserType dpuserFunction_psf(dpuserType X, dpuserType positions, dpString option) {
    Dpuser2cUtils::checkFuncArgs("psf", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    Fits *cube;
    int xc, yc;

    cube = CreateFits();
    if (!cube->create(X.fvalue->Naxis(1), X.fvalue->Naxis(2), positions.fvalue->Naxis(2)))
        throw dpuserTypeException("psf: Memory allocation failed.\n");;
    xc = X.fvalue->Naxis(1) / 2 + 1;
    yc = X.fvalue->Naxis(2) / 2 + 1;

    for (int i = 1; i <= positions.fvalue->Naxis(2); i++) {
        rv.fvalue->copy(*X.fvalue);
        rv.fvalue->shift((int)(xc - (*positions.fvalue)(1, i)), (int)(yc - (*positions.fvalue)(2, i)), 2);
        rv.fvalue->div((*rv.fvalue)(xc, yc));
        cube->setRange(*rv.fvalue, -1, -1, -1, -1, i, i);
    }

    if ((option == "") || (option =="median"))	// default
        rv.fvalue->CubeMedian(*cube);
    else if (option == "average")
        cube_avg(*cube, *rv.fvalue);
    else if (option == "minimum")
        rv.fvalue->CubeMinimum(*cube);
    else if (option == "maximum")
        rv.fvalue->CubeMaximum(*cube);

    return rv;
}

//Alex
/*!
pwd
Return the absolute path of the current directory.
Syntax
result = pwd()
Arguments
none
Returns
A string with the absolute path of the current directory.
See also
function_dir
procedure_cd
*/

dpuserType dpuserFunction_pwd() {
    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString(dpDir::currentDirPath());

    return rv;
}

//Alex
/*!
quickgauss
Create a circular gaussian.
Syntax
result = quickgauss(X, Y, FWHM, speed [, naxis1=value] [, naxis2=value])
Arguments
X: The center of the gaussian in the x-axes
Y: The center of the gaussian in the y-axes
FWHM: Full-width at half maximum of the circular gaussian
speed: The gaussian is calculated up to a distance of speed*FWHM from the center location
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
Returns
A two-dimensional circular gaussian.
See also
function_gauss
*/

dpuserType dpuserFunction_quickgauss(dpuserType X, dpuserType Y, dpuserType FWHM, dpuserType speed, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("quickgauss", X, Y, FWHM, speed, naxis1, naxis2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->qgauss(naxis1.lvalue, naxis2.lvalue, X.dvalue, Y.dvalue, FWHM.dvalue, speed.lvalue);

    return rv;
}

//Alex
/*!
getenv
Return the content of an environment variable.
Syntax
result = getenv(S)
Arguments
S: A string with the name of the environment variable.
Returns
A string with the content of the environment variable. If the environment variable does not exist, an empty string is returned.
See also
procedure_setenv
*/

dpuserType dpuserFunction_getenv(dpuserType S) {
    Dpuser2cUtils::checkFuncArgs("getenv", S);

    char *var;
    var = getenv(S.svalue->c_str());

    dpuserType rv;
    rv.type = typeStr;
    if (var == NULL)
        rv.svalue = CreateString("");
    else
        rv.svalue = CreateString(var);

    return rv;
}

//Alex
/*!
cblank
Replace all undefined values (like NaN or Inf) by another value. If no value is specified, these values will be replace by 0.0.
Syntax
result = cblank(ARRAY [, value])
Arguments
ARRAY: A matrix
value: Optional, the value to be assigned (defaults to 0)
See also
procedure_cblank
*/

dpuserType dpuserFunction_cblank(dpuserType ARRAY) {
    dpuserType val;
    val.type = typeDbl;
    val.dvalue = 0.0;	// default value from cblank() in fits.h

    return dpuserFunction_cblank(ARRAY, val);
}

//Alex

dpuserType dpuserFunction_cblank(dpuserType ARRAY, dpuserType val) {
    Dpuser2cUtils::checkFuncArgs("cblank", ARRAY, val);

    dpuserType rv;
    rv.type = typeFits;
    rv.clone(ARRAY);

    rv.fvalue->cblank(val.dvalue);
    return rv;
}

//Alex
/*!
now
Return the julian date of the current system time. The timezone (and daylight saving time, if appropriate) are taken into account.
Syntax
result = now()
Arguments
none
See also
function_jd
function_calday
*/

dpuserType dpuserFunction_now() {
    dpuserType rv;
    rv.type = typeDbl;

    CJulianDay jd;
    time_t t_time;
    struct tm *n_time;

    time(&t_time);
    n_time = gmtime(&t_time);
    jd.SetJD(n_time->tm_mday, n_time->tm_mon + 1, n_time->tm_year + 1900, n_time->tm_hour, n_time->tm_min, n_time->tm_sec);
    rv.dvalue = (double)jd;

    return rv;
}
#ifdef HAS_PGPLOT
//Alex

//dpuserType dpuserFunction_pgband(dpuserType val0, dpuserType val1, dpuserType val2, dpuserType val3, dpuserType val4, dpuserType val5, dpuserType val6) {
//    Dpuser2cUtils::checkFuncArgs("pgband", val0, val1, val2, val3, val4, val5, val6);

//    dpuserType rv;
//    rv.type = typeCon;

//    float x, y;
//    char ch[2];

//    ch[1] = (char)0;
//    if (variables[val4.lvalue].type == typeCon) x = (float)variables[val4.lvalue].lvalue;
//    else x = (float)variables[val4.lvalue].dvalue;
//    if (variables[val5.lvalue].type == typeCon) y = (float)variables[val5.lvalue].lvalue;
//    else y = (float)variables[val5.lvalue].dvalue;
//    rv.lvalue = cpgband(val0.lvalue, val1.lvalue, val2.dvalue, val3.dvalue, &x, &y, ch);
//    variables[val4.lvalue].type = typeDbl;
//    variables[val4.lvalue].dvalue = x;
//    variables[val5.lvalue].type = typeDbl;
//    variables[val5.lvalue].dvalue = y;
//    variables[val6.lvalue].type = typeStr;
//    variables[val6.lvalue].svalue = CreateString(ch);

//    return rv;
//}

//Alex

dpuserType dpuserFunction_pgbeg(dpuserType val0, dpuserType val1, dpuserType val2, dpuserType val3) {
    Dpuser2cUtils::checkFuncArgs("pgbeg", val0, val1, val2, val3);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = cpgbeg(val0.lvalue, val1.svalue->c_str(), val2.lvalue, val3.lvalue);

    return rv;
}

//Alex

dpuserType dpuserFunction_pgbegin(dpuserType val0, dpuserType val1, dpuserType val2, dpuserType val3) {
    return dpuserFunction_pgbeg(val0, val1, val2, val3);
}

//Alex

//dpuserType dpuserFunction_pgcurs(dpuserType val0, dpuserType val1, dpuserType val2) {
//    Dpuser2cUtils::checkFuncArgs("pgcurs", val0, val1, val2);

//    dpuserType rv;
//    rv.type = typeCon;

//    float x, y;
//    char ch[2];
//    ch[1] = (char)0;
//    if (variables[val0.lvalue].type == typeCon) x = (float)variables[val0.lvalue].lvalue;
//    else x = (float)variables[val0.lvalue].dvalue;
//    if (variables[val1.lvalue].type == typeCon) y = (float)variables[val1.lvalue].lvalue;
//    else y = (float)variables[val1.lvalue].dvalue;
//    rv.lvalue = cpgcurs(&x, &y, ch);
//    variables[val0.lvalue].type = typeDbl;
//    variables[val0.lvalue].dvalue = x;
//    variables[val1.lvalue].type = typeDbl;
//    variables[val1.lvalue].dvalue = y;
//    variables[val2.lvalue].type = typeStr;
//    variables[val2.lvalue].svalue = CreateString(ch);

//    return rv;
//}

//Alex

//dpuserType dpuserFunction_pgcurse(dpuserType val0, dpuserType val1, dpuserType val2) {
//    return dpuserFunction_pgcurs(val0, val1, val2);
//}

//Alex

dpuserType dpuserFunction_pgopen(dpuserType val0) {
    Dpuser2cUtils::checkFuncArgs("pgopen", val0);

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = cpgopen(val0.svalue->c_str());

    return rv;
}

//Alex

//dpuserType dpuserFunction_pgrnd(dpuserType val0, dpuserType val1) {
//    Dpuser2cUtils::checkFuncArgs("pgrnd", val0, val1);

//    int nsub;
//    dpuserType rv;
//    rv.type = typeDbl;
//    rv.dvalue = cpgrnd(val0.dvalue, &nsub);
//    variables[val1.lvalue].type = typeCon;
//    variables[val1.lvalue].lvalue = nsub;

//    return rv;
//}
#endif /* HAS_PGPLOT */

//Alex
/*!
polar
Returns a complex number generated of the two given values in polar representation. This is equivalent to complex(a * cos(b), a * sin(b)).
Syntax
result = polar(ampl, arg)
Arguments
ampl: The amplitude (either a real number or a matrix)
arg: The argument (either a real number or a matrix)
See also
function_complex
function_real
function_imag
function_conj
function_arg
function_abs
*/

dpuserType dpuserFunction_polar(dpuserType ampl, dpuserType arg) {
    Dpuser2cUtils::checkFuncArgs("polar", ampl, arg);

    dpuserType rv;

    if (ampl.type != arg.type) {
        throw dpuserTypeException("polar: Arguments must be of same type\n");
    }
    if (ampl.type == typeFits) {
        rv.fvalue = CreateFits();
        rv.fvalue->rr_aa(*ampl.fvalue, *arg.fvalue);
    } else {
        rv.type = typeCom;
        rv.cvalue = CreateComplex(complex_polar(ampl.dvalue, arg.dvalue));
    }

    return rv;
}

//Alex
/*!
transmatrix
Compute the transformation matrix for two reference coordinate systems. It is described by a first order polynomial if the number of reference sources is <= 6, else a second order polynomial is used. The transformation function will be calculated using a least squares fit to given coordinates. Note that if either in REFSTARS or in IMSTARS a coordinate is exactly (-1, -1), this star will not be used to calculate the transformation matrix.
Syntax
result = transmatrix(REFSTARS, IMSTARS [, xerror, yerror][, /silent] [, /linear] [, /cubic] [, /rotation])
Arguments
REFSTARS: A matrix 2xm (m >= 3) with positions of the reference stars in the reference frame (= masterlist)
IMSTARS: A matrix 2xm (same size as REFSTARS) with the positions of the same stars in the image to be transformed.
xerror: If set to a named variable, the residual error of the coordinate transform in the first axis is returned.
yerror: If set to a named variable, the residual error of the coordinate transform in the second axis is returned.
Switches
/silent: Printing of output is suppressed
/linear: A 1st order polynomial is fitted (even when more than 6 reference sources are supplied)
/cubic: A 3rd order polynomial is fitted (at least 10 reference sources)
/rotation: A rotational transformation is fitted (at least 4 reference sources): x' = x0 + f*cos(a)*x - f*sin(a)*y, y' = y0 + f*sin(a)*x + f*cos(a)*y
Returns
For 1st and 2nd order polynomials: A matrix 2x6 which describes the coordinate transformation matrix:
<code>x' = result[1,1] + result[1,2]*x + result[1,3]*y + result[1,4]*xy + result[1,5]*x^2 + result[1,6]*y^2
<code>y' = result[2,1] + result[2,2]*x + result[2,3]*y + result[2,4]*xy + result[2,5]*x^2 + result[2,6]*y^2<br>
For 3rd order polynomials: A matrix 2x10 which describes the coordinate transformation matrix:
<code>x' = result[1,1] + result[1,2]*x + result[1,3]*y + result[1,4]*xy + result[1,5]*x^2 + result[1,6]*y^2 + result[1,7]*x^2y + result[1,8]*xy^2 + result[1,9]*x^3 + result[1,10]*y^3
<code>y' = result[2,1] + result[2,2]*x + result[2,3]*y + result[2,4]*xy + result[2,5]*x^2 + result[2,6]*y^2 + result[2,7]*x^2y + result[2,8]*xy^2 + result[2,9]*x^3 + result[2,10]*y^3<br> 
For rotational transformation: A matrix 1x4 which describes the coordinate transformation matrix:
<code>x' = result[1] + result[3] * cos(result[4])*x - result[3] * sin(result[4])*y
<code>y' = result[2] + result[3] * sin(result[4])*x + result[3] * cos(result[4])*y
See also
function_transcoords
function_transform
*/

dpuserType dpuserFunction_transmatrix(dpuserType refstars, dpuserType imstars, dpString option) {
    Dpuser2cUtils::checkFuncArgs("transmatrix", refstars, imstars);

    double xerr, yerr;
    return calctransmatrix(xerr, yerr, refstars, imstars, option);
}

//Alex

//dpuserType dpuserFunction_transmatrix(dpuserType refstars, dpuserType imstars, dpuserType xerrors, dpuserType yerrors, dpString option) {
//    Dpuser2cUtils::checkFuncArgs("transmatrix", refstars, imstars, xerrors, yerrors);

//    double xerr, yerr;
//    dpuserType rv = calctransmatrix(xerr, yerr, refstars, imstars, option);
//    variables[xerrors.lvalue].type = typeDbl;
//    variables[xerrors.lvalue].dvalue = xerr;
//    variables[yerrors.lvalue].type = typeDbl;
//    variables[yerrors.lvalue].dvalue = yerr;

//    return rv;
//}

//Alex
/*!
transform
Apply a transformation matrix to a set of coordinates.
Syntax
result = transform(COORDS, TRANS)
Arguments
COORDS: A matrix 2xN with the initial coordinates.
TRANS: A matrix 2x6 which contains the coordinate transformation matrix.
Returns
A matrix 2xN with the transformed coordinates.
See also
function_transcoords
function_transmatrix
*/

dpuserType dpuserFunction_transform(dpuserType coords, dpuserType trans) {
    Dpuser2cUtils::checkFuncArgs("transform", coords, trans);

    double x, y, SX[6], SY[6];
    int i;

    if ((trans.fvalue->Naxis(1) != 2) || (trans.fvalue->Naxis(2) != 6))
        throw dpuserTypeException("transform: Transformation matrix must be 2x6\n");
    if (coords.fvalue->Naxis(1) != 2)
        throw dpuserTypeException("transform: The master coordinate list does not contain 2 coordinates per star\n");

    dpuserType rv;
    rv.type = typeFits;
    rv.clone(coords);
    if (rv.fvalue->setType(R8)) {
        SX[5] = trans.fvalue->r8data[0];
        SX[0] = trans.fvalue->r8data[1];
        SX[1] = trans.fvalue->r8data[2];
        SX[4] = trans.fvalue->r8data[3];
        SX[2] = trans.fvalue->r8data[4];
        SX[3] = trans.fvalue->r8data[5];
        SY[5] = trans.fvalue->r8data[6];
        SY[0] = trans.fvalue->r8data[7];
        SY[1] = trans.fvalue->r8data[8];
        SY[4] = trans.fvalue->r8data[9];
        SY[2] = trans.fvalue->r8data[10];
        SY[3] = trans.fvalue->r8data[11];
        for (i = 0; i < rv.fvalue->Naxis(2); i++) {
            x = coords.fvalue->ValueAt(coords.fvalue->C_I(0, i));
            y = coords.fvalue->ValueAt(coords.fvalue->C_I(1, i));
            rv.fvalue->r8data[rv.fvalue->C_I(0, i)] = SX[0]*x+SX[1]*y+SX[2]*x*x+SX[3]*y*y+SX[4]*x*y+SX[5];
            rv.fvalue->r8data[rv.fvalue->C_I(1, i)] =  SY[0]*x+SY[1]*y+SY[2]*x*x+SY[3]*y*y+SY[4]*x*y+SY[5];
        }
    }

    return rv;
}

//Alex
/*!
invert
Return the inverse of a 2-dimensional, square matrix.
Syntax
result = invert(X)
Arguments
X: A 2-dimensional, square matrix.
Returns
The inverse of X.
Examples
<code>print X # invert(X), /values<
will return a unity matrix
*/

dpuserType dpuserFunction_invert(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("invert", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.clone(X);
    rv.fvalue->ginvert();
    return rv;
}

//Alex
/*!
transpose
Return the matrix X transposed, i.e. rows and columns are interchanged.
Syntax
result = transpose(X)
Arguments
X: A vector or a 2-dimensional matrix.
*/

dpuserType dpuserFunction_transpose(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("transpose", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.clone(X);
    rv.fvalue->rot90(90);
    rv.fvalue->flip(1);

    return rv;
}

//Alex
/*!
isvariable
Check whether given variable name (as a string) exists in memory.
Syntax
result = isvariable(S)
Arguments
S: A string with the variable name
Returns
1 if the variable exists, 0 otherwise
*/

dpuserType dpuserFunction_isvariable(dpuserType S) {
    Dpuser2cUtils::checkFuncArgs("isvariable", S);

    dpuserType rv;
    rv.type = typeCon;

    rv.lvalue = dpuser_vars.count(*S.svalue);

    return rv;
}

/*!
pi
Return the number PI. It will be computet as 2 * acos(0).
Syntax
result = pi()
Arguments
none
Examples
Print the number pi to 15 significant digits:
<code>DPUSER> print sprintf("%.15f", pi())
<code>3.141592653589793
See also
category_trigonometry
*/

dpuserType dpuserFunction_pi() {
    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = M_PI;

    return rv;
}


//Alex
/*!
convolve
Convolve an image with a kernel. The resulting array is returned with bitpix -32.
Syntax
result = convolve(a, b)
Arguments
a: The array to be convolved.
b: The kernel to convolve the image with.
Notes
If a and b are of exactly the same size, the two are multiplied in fourier space. If b is smaller than a and a square matrix, a convolved with the kernel b is returned.
*/

dpuserType dpuserFunction_convolve(dpuserType a, dpuserType b) {
    Dpuser2cUtils::checkFuncArgs("convolve", a, b);

    dpuserType rv;
    rv.type  = typeFits;
    rv.clone(a);
    rv.fvalue->convolve(*b.fvalue);

    return rv;
}

//Alex
/*!
gammp
Return the incomplete gamma function P(a, x).
Syntax
result = gammp(a, x)
Arguments
a: A number > 0
x: A number > 0, the value where to evaluate the incomplete gamma function
*/

dpuserType dpuserFunction_gammp(dpuserType a, dpuserType x) {
    Dpuser2cUtils::checkFuncArgs("gammp", a, x);

    dpuserType rv;
    rv.type  = typeDbl;
    rv.dvalue = gsl_sf_gamma_inc_P(a.dvalue, x.dvalue);

    return rv;
}

//Alex
/*!
reform
Resize an array without actually moving data. The number of new elements must exactly match the number of elements in the array to be resized. If no size arguments are given, all array axes with length 1 are removed.
Syntax
result = reform(a, [n1 [, n2 [, n3]]])
Arguments
a: The array to be resized
n1: New axis length
n2: New axis length
n3: New axis length
*/

dpuserType dpuserFunction_reform(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("reform", X);

    dpuserType dummy;
    return calcreform(1, X, dummy, dummy, dummy);
}

//Alex

dpuserType dpuserFunction_reform(dpuserType X, dpuserType n1) {
    Dpuser2cUtils::checkFuncArgs("reform", X, n1);

    dpuserType dummy;
    return calcreform(2, X, n1, dummy, dummy);
}

//Alex

dpuserType dpuserFunction_reform(dpuserType X, dpuserType n1, dpuserType n2) {
    Dpuser2cUtils::checkFuncArgs("reform", X, n1, n2);

    dpuserType dummy;
    return calcreform(3, X, n1, n2, dummy);
}

//Alex

dpuserType dpuserFunction_reform(dpuserType X, dpuserType n1, dpuserType n2, dpuserType n3) {
    Dpuser2cUtils::checkFuncArgs("reform", X, n1, n2, n3);

    return calcreform(4, X, n1, n2, n3);
}

//Alex
/*!
find
[This documentation is copied literally from IDL's astrolib]
 
Find positive brightness perturbations (i.e stars) in an image. Also returns centroids and shape parameters (roundness & sharpness). Adapted from 1986 STSDAS version of DAOPHOT.
Syntax
result = find(image, hmin, fwhm [, roundlim [, sharplim]] [, /silent])
Arguments
image: 2 dimensional image array for which one wishes to identify the stars present.
hmin: Threshold intensity for a point source - should generally be 3 or 4 sigma above background.
fwhm: FWHM to be used in the convolve filter.
sharplim: 2 element vector giving low and high cutoff for the sharpness statistic (Default: [0.2,1.0]). Change this default only if the stars have significantly larger or or smaller concentration than a Gaussian.
roundlim: 2 element vector giving low and high cutoff for the roundness statistic (Default: [-1.0,1.0]). Change this default only if the stars are significantly elongated.
Switches
silent: Normally, FIND will write out each star that meets all selection criteria. If the SILENT keyword is set then this printout is suppressed.
Returns
The find function returns an array of size 5xnstars with the following entries:<br>
x: vector containing x position of all stars identified by FIND<br> 
y: vector containing y position of all stars identified by FIND<br>
flux: vector containing flux of identified stars as determined by a gaussian fit. Fluxes are NOT converted to magnitudes.<br>
sharp: vector containing sharpness statistic for identified stars<br>
round: vector containing roundness statistic for identified stars
Notes
The sharpness statistic compares the central pixel to the mean of the surrounding pixels.   If this difference is greater than the originally estimated height of the Gaussian or less than 0.2 the height of the Gaussian (for the default values of SHARPLIM) then the star will be rejected.
*/

dpuserType dpuserFunction_find(dpuserType image, dpuserType hmin, dpuserType fwhm, dpString option) {
    dpuserType roundlim;
    roundlim.type = typeFits;
    roundlim.fvalue = CreateFits();
    roundlim.fvalue->create(2, 1);
    roundlim.fvalue->r4data[0] = -1.0;
    roundlim.fvalue->r4data[1] = 1.0;

    return dpuserFunction_find(image, hmin, fwhm, roundlim, option);
}

//Alex

dpuserType dpuserFunction_find(dpuserType image, dpuserType hmin, dpuserType fwhm, dpuserType roundlim, dpString option) {
    dpuserType sharplim;
    sharplim.type = typeFits;
    sharplim.fvalue = CreateFits();
    sharplim.fvalue->create(2, 1);
    sharplim.fvalue->r4data[0] = (float)0.2;
    sharplim.fvalue->r4data[1] = 1.0;

    return dpuserFunction_find(image, hmin, fwhm, roundlim, sharplim, option);
}

//Alex

dpuserType dpuserFunction_find(dpuserType image, dpuserType hmin, dpuserType fwhm, dpuserType rlim, dpuserType slim, dpString option) {
    Dpuser2cUtils::checkFuncArgs("find", image, hmin, fwhm, rlim, slim);

    dpuserType rv;
    rv.type  = typeFits;
    rv.fvalue = CreateFits();
    find(*image.fvalue, *rv.fvalue, hmin.dvalue, fwhm.dvalue, *rlim.fvalue, *slim.fvalue, option == "");

    return rv;
}

//Alex
/*!
histogram
Create a histogram of given array.
Syntax
result = histogram(a [, min [, max [, binsize]]] [, /reverse])
Arguments
a: 1D or 2D Array
min: Datapoints with a value smaller than min are neglected.
max: Datapoints with a value greater than max are neglected.
binsize: Default is 1. Values between are rounded
Switches
/reverse: Create the reverse indices of a histogram
*/

dpuserType dpuserFunction_histogram(dpuserType X, dpString option) {
    dpuserType min;
    min.type = typeDbl;
    min.dvalue = X.fvalue->get_min();

    return dpuserFunction_histogram(X, min, option);
}

//Alex

dpuserType dpuserFunction_histogram(dpuserType X, dpuserType min, dpString option) {
    dpuserType max;
    max.type = typeDbl;
    max.dvalue = X.fvalue->get_max();

    return dpuserFunction_histogram(X, min, max, option);
}

//Alex

dpuserType dpuserFunction_histogram(dpuserType X, dpuserType min, dpuserType max, dpString option) {
    dpuserType binsize;
    binsize.type = typeDbl;
    binsize.dvalue = 1.0;

    return dpuserFunction_histogram(X, min, max, binsize, option);
}

//Alex

dpuserType dpuserFunction_histogram(dpuserType X, dpuserType min, dpuserType max, dpuserType binsize, dpString option) {
    Dpuser2cUtils::checkFuncArgs("histogram", X, min, max, binsize);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    if (option == "")
        rv.fvalue->histogram(*X.fvalue, min.dvalue, max.dvalue, binsize.dvalue);
    else if (option == "reverse")
        rv.fvalue->histogram_indices(*X.fvalue, min.dvalue, max.dvalue, binsize.dvalue);

    return rv;
}

//Alex
/*!
meandev
Calculate the mean deviation of given array, optionally omitting a value. This is defined as 1/sqrt(n-1)*stddev(a).
Syntax
result = meandev(a [, omit])
Arguments
a: The array which mean deviation from the average should be returned.
omit: An optional value which should be ignored.
See also
function_stddev
function_avg
*/

dpuserType dpuserFunction_meandev(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("meandev", X);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = X.fvalue->get_meandev();

    return rv;
}

//Alex

dpuserType dpuserFunction_meandev(dpuserType X, dpuserType omit) {
    Dpuser2cUtils::checkFuncArgs("meandev", X, omit);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = X.fvalue->get_meandev(omit.dvalue);

    return rv;
}

//Alex
/*!
version
Return the version string of the currently used DPUSER.
Syntax
result = version()
Arguments
none
*/

dpuserType dpuserFunction_version() {
    dpuserType rv;
    rv.type = typeStr;
    dpString txt(DPUSERVERSION);
    txt += DP_VERSION;

    rv.svalue = CreateString(txt);

    return rv;
}

//Alex
/*!
spifficube (obsolete)
Create a cube of a SPIFFI raw frame. The wavelength of each slitlet is shifted to one pixel accuracy. The wavelength scale is not being linearized.
Syntax
result = spifficube(inframe, [HK], [K])
Arguments
inframe: A raw SPIFFI frame (1024 x 1024 pixels)
Switches
HK: The shift in wavelength scale is done for the HK grism.
K: The shift in wavelength scale is done for the K grism.
See also
function_spiffiuncube
function_spiffishift
*/

dpuserType dpuserFunction_spifficube(dpuserType X, dpString option) {
    Dpuser2cUtils::checkFuncArgs("spifficube", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    int mode = 0;
    if (option == "HK") mode = 1;
    if (option == "K") mode = 2;

    spifficube(*X.fvalue, *rv.fvalue, mode);

    return rv;
}

//Alex
/*!
spiffiuncube (obsolete)
Undo cube creation done with spifficube. The resulting frame will be 1024 x 1024 (as for spiffi raw data).
Syntax
result = spiffiuncube(incube, [HK], [K])
Arguments
incube: A SPIFFI cube created with spifficube
Switches
HK: The shift in wavelength scale is done for the HK grism.
K: The shift in wavelength scale is done for the K grism.
See also
function_spifficube
function_spiffishift
*/

dpuserType dpuserFunction_spiffiuncube(dpuserType X, dpString option) {
    Dpuser2cUtils::checkFuncArgs("spiffiuncube", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    int mode = 0;
    if (option == "HK") mode = 1;
    if (option == "K") mode = 2;

    spiffiuncube(*X.fvalue, *rv.fvalue, mode);

    return rv;
}

//Alex
/*!
spiffishift (obsolete)
Shift slitlets of a raw SPIFFI frame so that the wavelength of each slitlet is shifted to one pixel accuracy. The wavelength scale is not being linearized.
Syntax
result = spiffishift(inframe, [HK], [K])
Arguments
inframe: A raw SPIFFI frame (1024 x 1024 pixels)
Switches
HK: The shift in wavelength scale is done for the HK grism.
K: The shift in wavelength scale is done for the K grism.
See also
function_spifficube
function_spiffiuncube
*/

dpuserType dpuserFunction_spiffishift(dpuserType X, dpString option) {
    Dpuser2cUtils::checkFuncArgs("spiffishift", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    int mode = 0;
    if (option == "HK") mode = 1;
    if (option == "K") mode = 2;

    spiffishift(*X.fvalue, *rv.fvalue, mode);

    return rv;
}

//Alex
/*!
bezier (obsolete)
xxx
Syntax
result = bezier(xxx [, xxx])
Arguments
xxx: xxx
xxx: xxx
See also
function_bezier1d
*/

dpuserType dpuserFunction_bezier(dpuserType X) {
    dpuserType val;
    val.type = typeCon;
    val.lvalue = 1;

    return dpuserFunction_bezier(X, val);
}

//Alex

dpuserType dpuserFunction_bezier(dpuserType X, dpuserType val) {
    Dpuser2cUtils::checkFuncArgs("bezier", val);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    X.fvalue->BezierInterpolate(*rv.fvalue, val.lvalue);

    return rv;
}

//Alex
/*!
bezier1d (obsolete)
xxx
Syntax
result = bezier1d(xxx , xxx)
Arguments
xxx: xxx
xxx: xxx
See also
function_bezier
*/

dpuserType dpuserFunction_bezier1d(dpuserType X,dpuserType Y) {
    Dpuser2cUtils::checkFuncArgs("bezier1d", X, Y);

    Dim dim;
    dim.x = X.fvalue->Naxis(1);
    dim.y = dim.z = 1;

    Fits actioncube;
    actioncube.copy(*Y.fvalue);
    actioncube.setType(I2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->copy(*X.fvalue);
    rv.fvalue->setType(R4);
    interpol1d(rv.fvalue->r4data, actioncube.i2data, dim, 100);

    return rv;
}

//Alex
/*!
moffat
Create an elliptical moffat function.
Syntax
result = moffat(x, y, power, fwhm1 [, fwhm2 [, angle]] [, naxis1=value] [, naxis2 = value])
Arguments
X: The center of the moffat in the x-axes
Y: The center of the moffat in the y-axes
power: The power index of the moffat function
fwhm1: Full-width at half maximum of the major axes
fwhm2: Optional, full-width at half maximum of the minor axes
angle: Optional, position angle of the moffat
naxis1=value: Optional, the size of the resulting array in the first dimension
naxis2=value: Optional, the size of the resulting array in the second dimension
*/

dpuserType dpuserFunction_moffat(dpuserType X, dpuserType Y, dpuserType power, dpuserType fwhm1, dpuserType naxis1, dpuserType naxis2) {
    dpuserType fwhm2;
    fwhm2.type = typeDbl;
    fwhm2.dvalue = -1.0;	// default value of moffat() in fits.h

    return dpuserFunction_moffat(X, Y, power, fwhm1, fwhm2, naxis1, naxis2);
}

//Alex

dpuserType dpuserFunction_moffat(dpuserType X, dpuserType Y, dpuserType power, dpuserType fwhm1, dpuserType fwhm2, dpuserType naxis1, dpuserType naxis2) {
    dpuserType angle;
    angle.type = typeDbl;
    angle.dvalue = 0.0;	// default value of moffat() in fits.h

    return dpuserFunction_moffat(X, Y, power, fwhm1, fwhm2, angle, naxis1, naxis2);

}
//Alex

dpuserType dpuserFunction_moffat(dpuserType X, dpuserType Y, dpuserType power, dpuserType fwhm1, dpuserType fwhm2, dpuserType angle, dpuserType naxis1, dpuserType naxis2) {
    Dpuser2cUtils::checkFuncArgs("moffat", X);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->moffat(naxis1.lvalue, naxis2.lvalue, X.dvalue, Y.dvalue, power.dvalue, fwhm1.dvalue, fwhm2.dvalue, angle.dvalue);

    return rv;
}

//Alex
/*!
readfitsextension
Read specified FITS file extension into memory. The optional arguments x1, x2, y1, y2, z1, z2 can be used to read in a part of a FITS file. The combination 0, 0 specifies that the complete range of the respective axis should be read in.
Syntax
result = readfitsextension(FILENAME, extension [, x1, x2 [, y1, y2 [, z1, z2]]])
Arguments
FILENAME: A string.
extension: Integer number specifying which extension to read.
x1, x2: Range in first dimension, defaults to 0, 0
y1, y2: Range in second dimension, defaults to 0, 0
z1, z2: Range in third dimension, defaults to 0, 0
See also
function_readfits
function_readfitsall
*/

dpuserType dpuserFunction_readfitsextension(dpuserType filename, dpuserType extension) {
    dpuserType def;
    def.type = typeCon;
    def.lvalue = 0;	// default value of RaedFITS() in fits.h

    return dpuserFunction_readfitsextension(filename, extension, def, def);
}

//Alex

dpuserType dpuserFunction_readfitsextension(dpuserType filename, dpuserType extension, dpuserType x1, dpuserType x2) {
    dpuserType def;
    def.type = typeCon;
    def.lvalue = 0;	// default value of RaedFITS() in fits.h

    return dpuserFunction_readfitsextension(filename, extension, x1, x2, def, def);
}

//Alex

dpuserType dpuserFunction_readfitsextension(dpuserType filename, dpuserType extension, dpuserType x1, dpuserType x2, dpuserType y1, dpuserType y2) {
    dpuserType def;
    def.type = typeCon;
    def.lvalue = 0;	// default value of RaedFITS() in fits.h

    return dpuserFunction_readfitsextension(filename, extension, x1, x2, y1, y2, def, def);
}
//Alex

dpuserType dpuserFunction_readfitsextension(dpuserType filename, dpuserType extension, dpuserType x1, dpuserType x2, dpuserType y1, dpuserType y2, dpuserType z1, dpuserType z2) {
    Dpuser2cUtils::checkFuncArgs("readfitsextension", filename, extension, x1, x2, y1, y2, z1, z2);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    if (extension.lvalue < 1)
        rv.fvalue->ReadFITS(filename.svalue->c_str(), x1.lvalue, x2.lvalue, y1.lvalue, y2.lvalue, z1.lvalue, z2.lvalue);
    else
        rv.fvalue->ReadFITSExtension(filename.svalue->c_str(), extension.lvalue, x1.lvalue, x2.lvalue, y1.lvalue, y2.lvalue, z1.lvalue, z2.lvalue);

    return rv;
}

//Alex
/*!
multigauss2dfit
Fit a number of circular gaussians to an image. All gaussians have the save FWHM.
Syntax
result = multigauss2dfit(image, errors, estimate [, chisq])
Arguments
image: The image to be fit
errors: The errors in the image values
estimate: Initial estimate for the fit of the form [Offset,fwhm,height_1,x_1,y_1,height_2,x_2,y_2,...,height_n,x_n,y_n]
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [Offset,fwhm,height_1,x_1,y_1,...,height_n,x_n,y_n,d_Offset,d_fwhm,d_height_1,d_x_1,d_y_1,...,d_height_n,d_x_n,d_y_n]
See also
function_gauss2dfit
function_gauss2dsimplefit
*/

dpuserType dpuserFunction_multigauss2dfit(dpuserType image, dpuserType errors, dpuserType estimate) {
    Dpuser2cUtils::checkFuncArgs("multigauss2dfit", image, errors, estimate);

    dpuserType rv;

    calcmultigauss2dfit(rv, image, errors, estimate);

    return rv;
}

//Alex

//dpuserType dpuserFunction_multigauss2dfit(dpuserType image, dpuserType errors, dpuserType estimate, dpuserType chisq) {
//    Dpuser2cUtils::checkFuncArgs("multigauss2dfit", image, errors, estimate, chisq);

//    dpuserType rv;

//    variables[chisq.lvalue].type = typeDbl;
//    variables[chisq.lvalue].dvalue = calcmultigauss2dfit(rv, image, errors, estimate);

//    return rv;
//}

//Alex
/*!
sincfit
Fit a sinc function to a set of data points. The function fit is y(x)=A+B*sinc((x+phi)/width)
Syntax
result = sincfit(X, Y, errors, estimate [, chisq])
Arguments
X: A vector containing the x values to be fit
Y: A vector containing the y values to be fit (in radians)
errors: The errors in Y
estimate: Initial estimate for the fit of the form [A,B,phi,width]
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [A,B,phi,width,d_A,d_B,d_phi,d_width]
*/

dpuserType dpuserFunction_sincfit(dpuserType X, dpuserType Y, dpuserType errors, dpuserType estimate) {
    Dpuser2cUtils::checkFuncArgs("sincfit", X, Y, errors, estimate);

    dpuserType rv;

    calcsincfit(rv, X, Y, errors, estimate);

    return rv;
}
//Alex

//dpuserType dpuserFunction_sincfit(dpuserType X, dpuserType Y, dpuserType errors, dpuserType estimate, dpuserType chisq) {
//    Dpuser2cUtils::checkFuncArgs("sincfit", X, Y, errors, estimate, chisq);

//    dpuserType rv;

//    variables[chisq.lvalue].type = typeDbl;
//    variables[chisq.lvalue].dvalue = calcsincfit(rv, X, Y, errors, estimate);

//    return rv;
//}

//Alex
/*!
rebin1d
Rebin a one-dimensional vector to new values.
Syntax
result = rebin1d(spectrum, xstart, xend, xdelta [, xvalues])
Arguments
spectrum: A one-dimensional FITS. It should have the correct FITS keywords (CRVAL1, CRPIX1, and CDELT1) or the optional argument xvalues should be given.
xstart: The first channel in the rebinned data
xend: The last channel in the rebinned data
xdelta: The increment step size for the rebinned data
xvalues: A vector of same length as spectrum with the corresponding x-values.
Returns
The returned vector has the data rebinned with the appropritate FITS keywords set.
See also
function_interpol
*/

dpuserType dpuserFunction_rebin1d(dpuserType spectrum, dpuserType xstart, dpuserType xend, dpuserType xdelta) {
    Dpuser2cUtils::checkFuncArgs("rebin1d", spectrum, xstart, xend, xdelta);

    dpuserType rv;
    rv.type = typeFits;
    rv.clone(spectrum);
    rv.fvalue->rebin1d(xstart.dvalue, xend.dvalue, xdelta.dvalue);

    return rv;
}

//Alex

dpuserType dpuserFunction_rebin1d(dpuserType spectrum, dpuserType xstart, dpuserType xend, dpuserType xdelta, dpuserType xvalues) {
    Dpuser2cUtils::checkFuncArgs("rebin1d", spectrum, xstart, xend, xdelta, xvalues);

    dpuserType rv;
    rv.type = typeFits;
    rv.clone(spectrum);
    rv.fvalue->rebin1d(*xvalues.fvalue, xstart.dvalue, xend.dvalue, xdelta.dvalue);

    return rv;
}

//Alex
/*!
velmap
Go through a datacube and fit a gaussian to a line. The arguments center and fwhm are used as initial estimate for the gauss-fit.
Syntax
result = velmap(cube, center, fwhm [, threshold])
Arguments
cube: A datacube
center: The approximate center (wavelength) of the line to fit
fwhm: The approximate FWHM of the line to fit
threshold: A lower limit under which no fit is attempted (given as percentage of peak flux)
Returns
A datacube with the following slices:<br>
result[*,*,1]: fit background<br>
result[*,*,2]: fit continuum<br>
result[*,*,3]: fit line center<br>
result[*,*,4]: fit line FWHM<br>
result[*,*,5]: error in background fit<br>
result[*,*,6]: error in continuum fit<br>
result[*,*,7]: error in line center fit<br>
result[*,*,8]: error in line FWHM fit<br>
result[*,*,9]: chi squared of fit<br>
*/

dpuserType dpuserFunction_velmap(dpuserType cube, dpuserType center, dpuserType fwhm, dpString option) {
    dpuserType threshold;
    threshold.type = typeDbl;
    threshold.dvalue = 0.0;

    return dpuserFunction_velmap(cube, center, fwhm, threshold, option);
}
//Alex

dpuserType dpuserFunction_velmap(dpuserType cube, dpuserType center, dpuserType fwhm, dpuserType threshold, dpString option) {
    Dpuser2cUtils::checkFuncArgs("velmap", cube, center, fwhm, threshold);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    int method = 0;				// gauss
    if (option == "centroid") method = 1;	// centroid

    rv.fvalue->velmap(*cube.fvalue, center.dvalue, fwhm.dvalue, threshold.dvalue, method);

    return rv;
}

//Alex
/*!
fxcor
Compute the cross-correlation of two spectra. The spectra will be rebinned to the lowest dispersion, continuum subtracted and normalised prior to the cross-correlation.
Syntax
result = fxcor(template, spectrum)
Arguments
template: The template spectrum
spectrum: The object spectrum
Returns
The cross-correlation of the two spectra
See also
function_correlmap
*/

dpuserType dpuserFunction_fxcor(dpuserType templ, dpuserType spectrum) {
    Dpuser2cUtils::checkFuncArgs("fxcor",templ, spectrum);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    fxcor(*rv.fvalue, *templ.fvalue, *spectrum.fvalue, 3);

    return rv;
}

//Alex
/*!
correlmap
Compute the cross-correlation maximum of a datacube and a template spectrum.
Syntax
result = correlmap(cube, template)
Arguments
cube: The datacube to be cross-correlated
template: The template spectrum
Returns
A 2D-map with the maximum of the cross-correlation
See also
function_fxcor
*/

dpuserType dpuserFunction_correlmap(dpuserType cube, dpuserType templ) {
    Dpuser2cUtils::checkFuncArgs("correlmap", cube, templ);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    correlmap(*rv.fvalue, *cube.fvalue, *templ.fvalue, 0);

    return rv;
}

//Alex
/*!
longslit
Create a pseudo-longslit from a datacube.
Syntax
result = longslit(cube, x, y, theta, width [, opening_angle])
Arguments
cube: A datacube
x: The x center for the longslit
y: The y center for the longslit
theta: The position angle, in degrees
width: The width of the longslit, in pixels
opening_angle: An opening angle of the pseudo-longslit, in degrees
Returns
A 2D longslit spectrum
See also
function_twodcut
*/

dpuserType dpuserFunction_longslit(dpuserType cube, dpuserType x, dpuserType y, dpuserType theta, dpuserType width) {
    Dpuser2cUtils::checkFuncArgs("longslit", cube, x, y, theta, width);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    longslit(*rv.fvalue, *cube.fvalue, x.lvalue, y.lvalue, theta.dvalue, width.lvalue);

    return rv;
}

//Alex
/*!
evalvelmap
Evaluate the result of velmap.
Syntax
result = evalvelmap(fit, template)
Arguments
fit: The result of the velmap function
template: A template cube which defines how big the resulting cube will be.
Returns
The evaluated cube, with gaussians at the appropriate places along the third dimension.
See also
function_velmap
*/

dpuserType dpuserFunction_evalvelmap(dpuserType fit, dpuserType templ) {
    Dpuser2cUtils::checkFuncArgs("evalvelmap", fit, templ);

    dpuserType rv;
    rv.type = typeFits;
    rv.clone(templ);

    Fits line, xvalues, fitparams;
    int x, y;
    dpProgress(-templ.fvalue->Naxis(1), "evalvelmap");
    abszissaGenerate(xvalues, *templ.fvalue, 3);
    templ.fvalue->extractRange(line, 1, 1, 1, 1, -1, -1);
    for (x = 1; x <= templ.fvalue->Naxis(1); x++) {
        for (y = 1; y <= templ.fvalue->Naxis(2); y++) {
            fit.fvalue->extractRange(fitparams, x, x, y, y, -1, -1);
            fitparams.setType(R8);
            fitparams.r8data[0] = 0.0;
            evaluate_gauss(line, xvalues, fitparams);
            rv.fvalue->setRange(line, x, x, y, y, -1, -1);
        }
        dpProgress(x, "evalvelmap");
    }

    return rv;
}

//Alex
/*!
sinfit
Fit a sine curve to a set of data points. The function fit is y(x)=A+B*sin(x+phi)
Syntax
result = sinfit(X, Y, errors, estimate [, chisq])
Arguments
X: A vector containing the x values to be fit
Y: A vector containing the y values to be fit (in radians)
errors: The errors in Y
estimate: Initial estimate for the fit of the form [A,B,phi]
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [A,B,phi,d_A,d_B,d_phi]
*/

dpuserType dpuserFunction_sinfit(dpuserType X, dpuserType Y, dpuserType errors, dpuserType estimate) {
    Dpuser2cUtils::checkFuncArgs("sinfit", X, Y, errors, estimate);

    dpuserType rv;

    calcsinfit(rv, X, Y, errors, estimate);

    return rv;
}

//Alex

//dpuserType dpuserFunction_sinfit(dpuserType X, dpuserType Y, dpuserType errors, dpuserType estimate, dpuserType chisqIndex) {
//    Dpuser2cUtils::checkFuncArgs("sinfit", X, Y, errors, estimate, chisqIndex);

//    dpuserType rv;

//    variables[chisqIndex.lvalue].type = typeDbl;
//    variables[chisqIndex.lvalue].dvalue = calcsinfit(rv, X, Y, errors, estimate);

//    return rv;
//}

//Alex
/*!
indexbase
Return the base index: 0 for C-notation, 1 for Fortran-notation
Syntax
result = indexbase()
Arguments
none
Returns
The index base
See also
procedure_cnotation
procedure_fortrannotation
procedure_setindexbase
*/

dpuserType dpuserFunction_indexbase() {

    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = indexBase;

    return rv;
}

//Alex
/*!
voronoi
Perform adaptive spatial binning of Integral-Field Spectroscopic (IFS) data to reach a chosen constant signal-to-noise ratio per bin. This method is required for the proper analysis of IFS observations, but can also be used for standard photometric imagery or any other two-dimensional data. Further information on VORONOI_2D_BINNING algorithm can be found in Cappellari M., Copin Y., 2003, MNRAS, 342, 345
Syntax
result = voronoi(signal, noise, target, targetSN [, /map])
Arguments
signal: A 2D map with the signal in each pixel
noise: A 2D map specifying the noise in each pixel. Pixels with a noise value of zero will be neglected in the voronoi binning
target: The image which should be binned (can be the same as the signal map)
targetSN: The Signal/Noise value which should be reached
Switches
/map: Instead of applying the binning to an image, return a map with regions and their respective IDs
Returns
Either the binned image or the map of regions
Notes
The original IDL code was written by Michele Cappellari, Leiden Observatory, The Netherlands cappellari@strw.leidenuniv.nl. If you have found this software useful for your research, he would appreciate an acknowledgment to use of `the Voronoi 2D-binning method by Cappellari & Copin (2003)'.
*/

dpuserType dpuserFunction_voronoi(dpuserType signal, dpuserType noise, dpuserType target, dpuserType targetSN, dpString option) {
    Dpuser2cUtils::checkFuncArgs("voronoi", signal, noise, target, targetSN);

    dpuserType rv;
    rv.type =

    rv.type = typeFits;
    rv.fvalue = CreateFits();
    int mode = 0;
    if (option == "map") mode = 1;
    voronoi(*signal.fvalue, *noise.fvalue, *target.fvalue, targetSN.dvalue, *rv.fvalue, mode);

    return rv;
}

/*!
gauss2dsimplefit
Fit a two-dimensional gaussian to a rectangular grid (i.e. an image).
Syntax
result = gauss2dsimplefit(image, x, y, width [, chisq])
Arguments
image: The image to be fit
x: The x-position of the fit center
y: The x-position of the fit center
width: The half-width of the fitting window
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [Offset,Max,x0,y0,fwhm1,fwhm2,angle,d_Offset,d_Max,d_x0,d_y0,d_fwhm1,d_fwhm2,d_angle]
See also
function_gauss2dfit
function_multigauss2dfit
*/

dpuserType dpuserFunction_gauss2dsimplefit(dpuserType image, dpuserType x, dpuserType y, dpuserType width) {
    Dpuser2cUtils::checkFuncArgs("gauss2dsimplefit", image, x, y, width);

    dpuserType rv;

    calcgauss2dsimplefit(rv, image, x, y, width);

    return rv;
}

//Alex

//dpuserType dpuserFunction_gauss2dsimplefit(dpuserType image, dpuserType x, dpuserType y, dpuserType width, dpuserType chisqIndex) {
//    Dpuser2cUtils::checkFuncArgs("gauss2dsimplefit", image, x, y, width, chisqIndex);

//    dpuserType rv;

//    variables[chisqIndex.lvalue].type = typeDbl;
//    variables[chisqIndex.lvalue].dvalue = calcgauss2dsimplefit(rv, image, x, y, width);

//    return rv;
//}


//Alex
/*!
transpoly
Compute the transformation matrix for two reference coordinate systems, with errors. It is described by a second order polynomial. The number of reference sources must be >= 6. The transformation function will be calculated using a least squares fit to given coordinates. Note that if either in REFSTARS or in IMSTARS a coordinate is exactly (-1, -1), this star will not be used to calculate the transformation matrix.
Syntax
result = transmatrix(REFSTARS, IMSTARS, ERRORS)
Arguments
REFSTARS: A matrix 2xm (m >= 6) with positions of the reference stars in the reference frame (= masterlist)
IMSTARS: A matrix 2xm (same size as REFSTARS) with the positions of the same stars in the image to be transformed.
ERRORS: A matrix 2xm (same size as REFSTARS) with the errors of the positions in REFSTARS.
Returns
A matrix 2x12 which describes the coordinate transformation matrix:
<code>x' = result[1,1] + result[2,1]*x + result[1,2]*y + result[2,2]*xy + result[1,3]*x^2 + result[2,3]*y^2
<code>y' = result[1,4] + result[2,4]*x + result[1,5]*y + result[2,5]*xy + result[1,6]*x^2 + result[2,6]*y^2<br>
The remaining coordinates [1:2,7:12] are the errors of the respective elements of the transformation matrix.
See also
function_transcoords
function_transform
*/

dpuserType dpuserFunction_transpoly(dpuserType ref, dpuserType im, dpuserType errors) {
    Dpuser2cUtils::checkFuncArgs("transpoly", ref, im, errors);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    double *xref = NULL, *yref = NULL, *xim = NULL, *yim = NULL, *xerror = NULL, *yerror = NULL;
    double SY[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
    double SX[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
    int i, llen, ncoords;

    llen = ref.fvalue->Naxis(2);
    if (llen < 6)
        throw dpuserTypeException("transpoly: Must have at least 6 reference sources.");
    if ((ref.fvalue->Naxis(1) != 2) || (im.fvalue->Naxis(1) != 2))
        throw dpuserTypeException("transpoly: Input matrices are not of correct size: Must be 2xn");
    if (ref.fvalue->Naxis(2) != im.fvalue->Naxis(2))
        throw dpuserTypeException("transpoly: The two reference lists do not contain the same number of sources.");

    if (rv.fvalue->create(4, 6, R8)) {
        xref = (double *)malloc(llen * sizeof(double));
        yref = (double *)malloc(llen * sizeof(double));
        xim = (double *)malloc(llen * sizeof(double));
        yim = (double *)malloc(llen * sizeof(double));
        xerror = (double *)malloc(llen * sizeof(double));
        yerror = (double *)malloc(llen * sizeof(double));

        if ((xref == NULL) || (yref == NULL) || (xim == NULL) || (yim == NULL) || (xerror == NULL) || (yerror == NULL)) {
            if (xref != NULL) free(xref);
            if (yref != NULL) free(yref);
            if (xim != NULL) free(xim);
            if (yim != NULL) free(yim);
            if (xerror != NULL) free(xerror);
            if (yerror != NULL) free(yerror);
            throw dpuserTypeException("transpoly: Memory allocation failed.");
        } else {
            ncoords = 0;
            for (i = 0; i < llen; i++) {
                xref[ncoords] = ref.fvalue->ValueAt(ref.fvalue->C_I(0, i));
                yref[ncoords] = ref.fvalue->ValueAt(ref.fvalue->C_I(1, i));
                xim[ncoords] = im.fvalue->ValueAt(im.fvalue->C_I(0, i));
                yim[ncoords] = im.fvalue->ValueAt(im.fvalue->C_I(1, i));
                xerror[ncoords] = errors.fvalue->ValueAt(errors.fvalue->C_I(0, i));
                yerror[ncoords] = errors.fvalue->ValueAt(errors.fvalue->C_I(1, i));
                if ((xim[ncoords] == -1.) || (yim[ncoords] == -1.) || (xref[ncoords] == -1.) || (yref[ncoords] == -1.)) ncoords--;
                ncoords++;
            }
            trans_matrix_errors(ncoords, 6, xref, yref, xim, yim, SX, SY, xerror, yerror);
        }

        dp_output("transformation matrix is:\nx = %5g + %5gx + %5gy + %5gxy + %5gx^2 + %5gy^2\ny = %5g + %5gx + %5gy + %5gxy + %5gx^2 + %5gy^2\n", SX[5], SX[0], SX[1], SX[4], SX[2], SX[3], SY[5], SY[0], SY[1], SY[4], SY[2], SY[3]);
        rv.fvalue->r8data[rv.fvalue->F_I(1,1)] = SX[5];
        rv.fvalue->r8data[rv.fvalue->F_I(1,2)] = SX[0];
        rv.fvalue->r8data[rv.fvalue->F_I(1,3)] = SX[1];
        rv.fvalue->r8data[rv.fvalue->F_I(1,4)] = SX[4];
        rv.fvalue->r8data[rv.fvalue->F_I(1,5)] = SX[2];
        rv.fvalue->r8data[rv.fvalue->F_I(1,6)] = SX[3];
        rv.fvalue->r8data[rv.fvalue->F_I(2,1)] = SY[5];
        rv.fvalue->r8data[rv.fvalue->F_I(2,2)] = SY[0];
        rv.fvalue->r8data[rv.fvalue->F_I(2,3)] = SY[1];
        rv.fvalue->r8data[rv.fvalue->F_I(2,4)] = SY[4];
        rv.fvalue->r8data[rv.fvalue->F_I(2,5)] = SY[2];
        rv.fvalue->r8data[rv.fvalue->F_I(2,6)] = SY[3];
        rv.fvalue->r8data[rv.fvalue->F_I(3,1)] = xerror[5];
        rv.fvalue->r8data[rv.fvalue->F_I(3,2)] = xerror[0];
        rv.fvalue->r8data[rv.fvalue->F_I(3,3)] = xerror[1];
        rv.fvalue->r8data[rv.fvalue->F_I(3,4)] = xerror[4];
        rv.fvalue->r8data[rv.fvalue->F_I(3,5)] = xerror[2];
        rv.fvalue->r8data[rv.fvalue->F_I(3,6)] = xerror[3];
        rv.fvalue->r8data[rv.fvalue->F_I(4,1)] = yerror[5];
        rv.fvalue->r8data[rv.fvalue->F_I(4,2)] = yerror[0];
        rv.fvalue->r8data[rv.fvalue->F_I(4,3)] = yerror[1];
        rv.fvalue->r8data[rv.fvalue->F_I(4,4)] = yerror[4];
        rv.fvalue->r8data[rv.fvalue->F_I(4,5)] = yerror[2];
        rv.fvalue->r8data[rv.fvalue->F_I(4,6)] = yerror[3];
    }
    free(xref);
    free(yref);
    free(xim);
    free(yim);
    free(xerror);
    free(yerror);

    return rv;
}

//Alex
/*!
strtrim
Removes leading and/or trailing blank from the input String.
Syntax
result = strtrim(str)
Arguments
str: A string
Returns
Returns a copy of the string with removed blanks.
*/

dpuserType dpuserFunction_strtrim(dpuserType X) {
    Dpuser2cUtils::checkFuncArgs("strtrim", X);

    dpuserType rv;

    if (X.type == typeStrarr) {
        int i;

        rv.type = typeStrarr;
        rv.clone(X);
//#ifdef NO_QT
        for (i = 0; i < rv.arrvalue->size(); i++)
            (*rv.arrvalue)[i].strtrim(0);
/*#else
        for (i = 0; i < rv.arrvalue->size(); i++)
            (*rv.arrvalue)[i] = (*rv.arrvalue)[i].stripWhiteSpace();
#endif*/
    } else {
        rv.type = typeStr;
        switch (X.type) {
            case typeCon:
                rv.svalue = CreateString(X.lvalue);
                break;
            case typeDbl:
                rv.svalue = CreateString(X.dvalue);
                break;
            case typeCom:
                rv.svalue = CreateString(*X.cvalue);
                break;
            case typeStr:
                rv.clone(X);
                break;
            default:
                rv.svalue = CreateString();
                break;
        }
//#ifdef NO_QT
        rv.svalue->strtrim(0);
/*#else
        (*rv.svalue) = rv.svalue->stripWhiteSpace();
#endif*/
    }

    return rv;
}

//Alex
/*!
right
Tail of a string.
Syntax
result = right(str, n)
Arguments
str: A text string
n: An integer 
Returns
Return the last n characters from string str.
*/

dpuserType dpuserFunction_right(dpuserType str, dpuserType nr) {
    Dpuser2cUtils::checkFuncArgs("right", str, nr);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString();
    *rv.svalue = str.svalue->right(nr.lvalue);

    return rv;
}

//Alex
/*!
ten
Transforms hours:minutes:seconds into decimal notation
Syntax
result = ten(hh , mm, ss)
Arguments
hh: hours
mm: minutes
ss: seconds
Returns
A decimal value with hours as unit
Examples
Transform 1h, 29min, 60 sec to decimal:
<code>print = ten(1, 29, 60)
<code>      1.5
*/

dpuserType dpuserFunction_ten(dpuserType x, dpuserType y, dpuserType z) {
    Dpuser2cUtils::checkFuncArgs("ten", x, y, z);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = ten(x.toDouble(), y.toDouble(), z.toDouble());

    return rv;
}

//Alex
/*!
primes
Creates a vector of given length with prime numbers.
Syntax
result = primes(n)
Arguments
n: The number of prime numbers to return
Returns
A vector of length n with the first n prime numbers.
*/

dpuserType dpuserFunction_primes(dpuserType nr) {
    Dpuser2cUtils::checkFuncArgs("primes", nr);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    primes(nr.lvalue, *rv.fvalue);

    return rv;
}

//Alex
/*!
twodcut
Create a 2D-cut from an image.
Syntax
result = twodcut(image, x, y, theta, width)
Arguments
image: An image
x: The x center for the 2D-cut
y: The y center for the 2D-cut
theta: The position angle, in degrees
width: The width of the cut, in pixels
Returns
A 1D spectrum
See also
function_longslit
*/

dpuserType dpuserFunction_twodcut(dpuserType X, dpuserType nr1, dpuserType nr2, dpuserType nr3, dpuserType nr4) {
    Dpuser2cUtils::checkFuncArgs("twodcut", X, nr1, nr2, nr3, nr4);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    twodcut(*rv.fvalue, *X.fvalue, nr1.lvalue, nr2.lvalue, nr3.dvalue, nr4.lvalue);

    return rv;
}

//Alex
/*!
simplifywhitespace
Strip all whitespaces at the beginning and the end, and replace all multiple occurrences of whitespaces by one single space.
Syntax
result = simplifywhitespace(s)
Arguments
s: The string to be simplified
*/

dpuserType dpuserFunction_simplifywhitespace(dpuserType s) {
    Dpuser2cUtils::checkFuncArgs("simplifywhitespace", s);

    dpuserType rv;
    rv.type = typeStr;
    rv.svalue = CreateString();
    *rv.svalue = s.svalue->simplifyWhiteSpace();

    return rv;
}

//Alex
/*!
strsplit
Split a string into a string array
Syntax
result = strsplit(s [, delimiter])
Arguments
s: The string to be split
delimiter: A single character at which to split (defaults to ' ')
*/

dpuserType dpuserFunction_strsplit(dpuserType s) {
    dpuserType delimiter;
    delimiter.type = typeStr;
    delimiter.svalue += ' ';

    return dpuserFunction_strsplit(s, delimiter);
}
//Alex

dpuserType dpuserFunction_strsplit(dpuserType s, dpuserType delimiter) {
    Dpuser2cUtils::checkFuncArgs("strsplit", s, delimiter);

    dpuserType rv;
    rv.type = typeStrarr;
    rv.arrvalue = CreateStringArray();
    *rv.arrvalue = dpStringList::split(delimiter.svalue->c_str()[0], *s.svalue);

    return rv;
}

//Alex
/*!
sersic2dfit
Fit a two-dimensional sersic function to a rectangular grid (i.e. an image).<br>
The 2D sersic function is defined as I(x,y) = c + Ie * exp(-bn * (R/Re)^(1/n) - 1).<br>
R is an elliptical equation: R = sqrt(xp^2 + (yp / q)^2).<br>
xp and yp describe a rotation: xp = (x - x0) * cos(angle) + (y - y0) * sin(angle), yp = - (x - x0) * sin(angle) + (y - y0) * cos(angle) and bn = 1.9992 * n - 0.3271
Syntax
result = sersic2dfit(image, errors, estimate [, chisq])
Arguments
image: The image to be fit
errors: The errors in the image values
estimate: Initial estimate for the fit of the form [c, Ie, Re, x0, y0, angle, q, n]. If n is provided with a negative sign, it won't be fitted and assumed as fixed (with positive sign, of course).
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [c, Ie, Re, x0, y0, angle, q, n] or [c, Ie, Re, x0, y0, angle, q] if n was kept fixed.
See also
function_sersic2dsimplefit
*/

dpuserType dpuserFunction_sersic2dfit(dpuserType image, dpuserType errors, dpuserType estimate, dpuserType chisq) {
    Dpuser2cUtils::checkFuncArgs("sersic2dfit", image, errors, estimate, chisq);

    dpuserType rv;

    rv.type = typeFits;
    rv.fvalue = CreateFits();

    rv.fvalue->copy(*estimate.fvalue);
    chisq =  sersic2dfit(*rv.fvalue, *image.fvalue, *image.fvalue, *errors.fvalue);

    return rv;
}

//Alex
/*!
sersic2dsimplefit
Fit a two-dimensional sersic function to a rectangular grid (i.e. an image) without any given estimates.<br>
The 2D sersic function is defined as I(x,y) = c + Ie * exp(-bn * (R/Re)^(1/n) - 1).<br>
R is an elliptical equation: R = sqrt(xp^2 + (yp / q)^2).<br>
xp and yp describe a rotation: xp = (x - x0) * cos(angle) + (y - y0) * sin(angle), yp = - (x - x0) * sin(angle) + (y - y0) * cos(angle) and bn = 1.9992 * n - 0.3271.
Syntax
result = sersic2dsimplefit(image, x_cen, y_cen, width [, chisq [, n]])
Arguments
image: The image to be fit
x_cen: The x-position of the fit center
y_cen: The x-position of the fit center
width: The half-width of the fitting window
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
n: If n should be fixed and not fitted.
Returns
The returned vector contains the best-fit parameters in the form [c, Ie, Re, x0, y0, angle, q, n] or [c, Ie, Re, x0, y0, angle, q] if n was kept fixed. Angle is in degrees. When the negative reciprocal value in q is returned, then the angle is also increased by 90 degrees.
See also
function_sersic2dfit
*/

dpuserType dpuserFunction_sersic2dsimplefit(dpuserType image, dpuserType x, dpuserType y, dpuserType width, dpuserType chisq, dpuserType fixed_n) {
    Dpuser2cUtils::checkFuncArgs("sersic2dsimplefit", image, x, y, width, chisq);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    chisq = sersic2dsimplefit(*rv.fvalue, *image.fvalue, (int)(x.dvalue + .5), (int)(y.dvalue + .5), (int)(width.dvalue + .5), fixed_n.dvalue);

    return rv;
}

//Alex
/*!
sersicfit
Fit a one-dimensional sersic function to a vector. The sersic function is defined as y(x) = c + Ie * exp(-bn * (x/Re)^(1/n) - 1), with bn = 1.9992 * n - 0.3271.
Syntax
result = sersicfit(X, Y, errors, estimates [, chisq])
Arguments
X: A vector containing the x values to be fit
Y: A vector containing the y values to be fit
errors: The errors in Y
estimate: Initial estimate for the fit of the form [c, Ie, Re, n]. If n is provided with a negative sign, it won't be fitted and assumed as fixed (with positive sign, of course).
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [c, Ie, Re, n] or [c, Ie, Re] if n was kept fixed. When the negative reciprocal value in q is returned, then the angle is also increased by 90 degrees.
*/

dpuserType dpuserFunction_sersicfit(dpuserType X, dpuserType Y, dpuserType error, dpuserType estimate, dpuserType chisq) {
    Dpuser2cUtils::checkFuncArgs("sersicfit", X, Y, error, estimate, chisq);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    rv.fvalue->copy(*estimate.fvalue);

    chisq = sersicfit(*rv.fvalue, *X.fvalue, *Y.fvalue, *error.fvalue);

    return rv;
}

//Alex
/*!
sersic2d
Creates a 2D sersic. The sersic function is defined as y(x) = exp(-bn * (x/Re)^(1/n) - 1), with bn = 1.9992 * n - 0.3271.
Syntax
result = sersic2d(c, x0, y0, angle, q, n [, naxis1=value] [, naxis2 = value])
Arguments
Re: Effective radius that encloses half of the total light
x0: x-position of center
y0: y-position of center
angle: angle in degrees
q: diskiness
n: shape of light model (0.5 = gaussian, 1 = exponential, 4 = de Vaucouleurs)
Examples
<code>result = sersic2d(10,80,70,870,-2,1, naxis1=128, naxis2=128)
*/

dpuserType dpuserFunction_sersic2d(dpuserType naxis1, dpuserType naxis2, dpuserType Re, dpuserType x0, dpuserType y0, dpuserType angle, dpuserType q, dpuserType n){
    Dpuser2cUtils::checkFuncArgs("sersic2d", naxis1, naxis2, Re, x0, y0, angle, q, n);

    double  naxis11 = naxis1.toInt(),
            naxis21 = naxis2.toInt(),
            cosa = cos(angle.toDouble() * M_PI / 180.),
            sina = sin(angle.toDouble() * M_PI / 180.),
            Re1 = Re.toDouble(),
            x01 = x0.toDouble(),
            y01 = y0.toDouble(),
            q1 = q.toDouble(),
            n1 = n.toDouble(),
            bn = 1.9992 * n.toDouble() - 0.3271,
            xp, yp, R;

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->create(naxis11, naxis21);

    for (int x = 1; x <= naxis11; x++) {
        for (int y = 1; y <= naxis21; y++) {
            xp = (x-x01) * cosa + (y-y01) * sina;
            yp = -(x-x01) * sina + (y-y01) * cosa;
            R = sqrt(pow(xp, 2) + pow(yp / q1, 2));

            rv.fvalue->setValue(exp(-bn * pow(R / Re1, 1 / n1) - 1), x, y);
        }
    }

    return rv;
}

//Thomas
/*!
randomg
Returns a random number drawn from a Gaussian distribution centered on 0 with width SIGMA. If no SEED is given, a seed based on the current time is used.
Syntax
result = randomg(SIGMA [, SEED])
Arguments
SIGMA: Width of the Gaussian profile from which the number is drawn (real number).
SEED: Seed of the random number algorithm (integer number).
Returns
A random number
See also
function_random
*/

dpuserType dpuserFunction_randomg(dpuserType sigma) {
    Dpuser2cUtils::checkFuncArgs("randomg", sigma);

    dpuserType rv;
    rv.type = typeDbl;
    rv.dvalue = gsl_ran_gaussian(gsl_rng_r, sigma.toDouble());
    return rv;
}

//Thomas

dpuserType dpuserFunction_randomg(dpuserType sigma, dpuserType seed) {
    Dpuser2cUtils::checkFuncArgs("randomg", sigma, seed);

    dpuserType rv;
    rv.type = typeDbl;

    const gsl_rng_type *T;

    gsl_rng_free(gsl_rng_r);
    gsl_rng_default_seed = seed.lvalue;
    T = gsl_rng_default;
    gsl_rng_r = gsl_rng_alloc(T);
    rv.dvalue = gsl_ran_gaussian(gsl_rng_r, sigma.toDouble());

    return rv;
}

//Thomas
/*!
poly
Create a polynomial
Syntax
result = poly(x, c)
Arguments
x: An array or real number
c: The coefficients of the polynomial
Returns
The polynomial of form c[1] + c[2]*x + c[3]*x^2 + ...
*/

dpuserType dpuserFunction_poly(dpuserType x, dpuserType c) {
    Dpuser2cUtils::checkFuncArgs("poly", x, c);

    dpuserType rv;
    long n, m;
    long order = c.fvalue->Nelements() - 1;

    rv.deep_copy(x);

    if (x.type == typeFits) {
        long ne = x.fvalue->Nelements();
        rv.fvalue->setType(R8);
        (*rv.fvalue) = (*c.fvalue)[order];
        for (m = order - 1; m >= 0; m--) {
            for (n = 0; n < ne; n++) {
                rv.fvalue->r8data[n] = rv.fvalue->r8data[n] * (*x.fvalue)[n] + (*c.fvalue)[m];
            }
        }
    } else {
        double xx;
        rv.type = typeDbl;
        if (x.type == typeDbl) xx = x.dvalue;
        else xx = (double)(x.lvalue);
        rv.dvalue = (*c.fvalue)[order];
        for (m = order - 1; m >= 0; m--) {
            rv.dvalue = rv.dvalue * xx + (*c.fvalue)[m];
        }
    }

    return rv;
}

dpuserType dpuserFunction_poly(dpuserType x, dpuserType c, dpuserType derivative) {
    Dpuser2cUtils::checkFuncArgs("poly", x, c, derivative);

    dpuserType rv;
    long n, m;
    int i, d;
    long order = c.fvalue->Nelements() - 1;

    if (order < derivative.toInt()) throw dpuserTypeException("poly: The order of the polynomial must be greater than the order of the derivative");
    double *a = (double *)malloc((order+1) * sizeof(double));

    rv.deep_copy(x);

    for (i = 0; i <= order; i++) a[i] = c.fvalue->ValueAt(i);
    for (d = 0; d < derivative.toInt(); d++) {
        for (i = 0; i <= order - 1; i++) {
            a[i] = (i+1) * a[i+1];
        }
        order--;
    }

    if (x.type == typeFits) {
        long ne = x.fvalue->Nelements();
        rv.fvalue->setType(R8);
        (*rv.fvalue) = a[order];
        for (m = order - 1; m >= 0; m--) {
            for (n = 0; n < ne; n++) {
                rv.fvalue->r8data[n] = rv.fvalue->r8data[n] * (*x.fvalue)[n] + a[m];
            }
        }
    } else {
        double xx;
        rv.type = typeDbl;
        if (x.type == typeDbl) xx = x.dvalue;
        else xx = (double)(x.lvalue);
        rv.dvalue = a[order];
        for (m = order - 1; m >= 0; m--) {
            rv.dvalue = rv.dvalue * xx + a[m];
        }
    }
    free(a);

    return rv;
}

//Alex
/*!
polyfit2d
Returns a 2D N-th order polynomial fit to X. X must be 2-dimensional. The function returns a vector of size sum(i+1)*2 (for i=0 to N). The fittet polynomial has the following form:
<code>for N=3: f(x,y) = a0 + a1*x + a2*y + a3*x^2 + a4*x*y + a5*y^2 + a6*x^3 + a7*x^2*y + a8*x*y^2 + a9*y^3
The values a0, ..., a9 are returned in the first 10 elements of the returned vector, their respective errors in the elements 11...20.
Syntax
result = polyfit2d(X, N [, CHISQ])
Arguments
X: A vector.
N: An integer number.
CHISQ: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
See also
function_polyfit
function_polyfitxy
function_polyfitxyerr
function_polyfit2derr
*/

dpuserType dpuserFunction_polyfit2d(dpuserType &image, dpuserType &degree, dpuserType &chisq) {
    Dpuser2cUtils::checkFuncArgs("polyfit2d", image, degree, chisq);

    int i = 0, x = 0, y = 0,
        img_size = image.fvalue->Nelements();
    Fits x_val, y_val, func_val, errors, result;

    dpuserType fit_pars;

    // create needed Fits structures
    x_val.create(img_size, 1, I4);
    y_val.create(img_size, 1, I4);
    func_val.create(img_size, 1, R8);
    errors.create(img_size, 1, I1);

    // fill created Fits structures
    for (y = 1; y <= image.fvalue->Naxis(2); y++) {
        for (x = 1; x <= image.fvalue->Naxis(1); x++) {
            x_val.i4data[i] = x;
            y_val.i4data[i] = y;
            func_val.r8data[i] = image.fvalue->ValueAt(i);
            errors.i1data[i] = 1;
            i++;
        }
    }

    chisq.type = typeDbl;
    chisq.dvalue =  polyfit2d(result, func_val, x_val, y_val, errors, degree.lvalue);

    fit_pars.type = typeFits;
    fit_pars.fvalue = CreateFits();
    fit_pars.fvalue->copy(result);

    return fit_pars;
}

//Alex
/*!
polyfit2derr
Returns a 2D N-th order polynomial fit to X. X must be 2-dimensional. The function returns a vector of size sum(i+1)*2 (for i=0 to N). The fittet polynomial has the following form:
<code>for N=3: f(x,y) = a0 + a1*x + a2*y + a3*x^2 + a4*x*y + a5*y^2 + a6*x^3 + a7*x^2*y + a8*x*y^2 + a9*y^3
The values a0, ..., a9 are returned in the first 10 elements of the returned vector, their respective errors in the elements 11...20.
Syntax
result = polyfit2d(X, N, ERROR [, CHISQ])
Arguments
X: A vector.
N: An integer number.
ERROR: The error of the X-values. If this is a real number, all X-values are assumed to have the same error. If ERROR is a vector, it must have the same length as X and gives the error or each individual X value.
CHISQ: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
See also
function_polyfit
function_polyfitxy
function_polyfitxyerr
function_polyfit2derr
*/

dpuserType dpuserFunction_polyfit2derr(dpuserType &image, dpuserType &degree, dpuserType &errors, dpuserType &chisq) {
    Dpuser2cUtils::checkFuncArgs("polyfit2derr", image, degree, errors, chisq);

    int i = 0, x = 0, y = 0,
        img_size = image.fvalue->Nelements();
    Fits x_val, y_val, func_val, result, err;
        dpuserType *fit_pars = new dpuserType();

    // create needed Fits structuresreturn
    x_val.create(img_size, 1, I4);
    y_val.create(img_size, 1, I4);
    func_val.create(img_size, 1, R8);
    err.create(img_size, 1, R8);

    // fill created Fits structures
    for (y = 1; y <= image.fvalue->Naxis(2); y++) {
        for (x = 1; x <= image.fvalue->Naxis(1); x++) {
            x_val.i4data[i] = x;
            y_val.i4data[i] = y;
            func_val.r8data[i] = image.fvalue->ValueAt(i);
            if (errors.type == typeFits) {
                err.r8data[i] = errors.fvalue->ValueAt(i);
            } else {
                err.r8data[i] = errors.dvalue;
            }

            i++;
        }
    }

    chisq.type = typeDbl;
    chisq.dvalue =  polyfit2d(result, func_val, x_val, y_val, err, degree.lvalue);

    fit_pars->type = typeFits;
    fit_pars->fvalue = &result;

    return *fit_pars;
}

/*!
getbitpix
Get the pixel type of X.
Syntax
result = getbitpix(X)
Arguments
X: The array whose pixel type is to be output
Returns
-128: Complex (note that this does not conform to FITS standard)<br>
-64: Floating point, double precision<br>
-32: Floating point, single precision<br>
8: Unsigned char<br>
16: Short<br>
32: Long
64: Long Long
See also
function_setbitpix
*/

dpuserType dpuserFunction_getbitpix(dpuserType x) {
    Dpuser2cUtils::checkFuncArgs("getbitpix", x);
    dpuserType rv;
    rv.type = typeCon;
    rv.lvalue = x.fvalue->getType();

    return rv;
}

/*!
isnan
Returns a non-zero value if value is 'not-a-number' (NaN), and 0 otherwise.
Syntax
result = isnan(a)
Arguments
a: The value to check
See also
function_isinf
*/

dpuserType dpuserFunction_isnan(dpuserType x) {
    Dpuser2cUtils::checkFuncArgs("isnan", x);
    dpuserType rv;
    rv.type = typeCon;

    if (x.type == typeDbl) {
        rv.lvalue = std::isnan(x.dvalue);
    } else if (x.type == typeCom) {
        if ((std::isnan(x.cvalue->real()) == 0) &&
            (std::isnan(x.cvalue->imag()) == 0)) {
            rv.lvalue = 0;
        } else {
            rv.lvalue = 1;
        }
    }

    return rv;
}

/*!
isinf
Returns -1 if value represents negative infinity, 1 if value represents positive infinity, and 0 otherwise.
If the value is complex and 'inf + -inf i' or '-inf + inf i' then -0.5 will be returned.
Syntax
result = isinf(a)
Arguments
a: The value to check
See also
function_isnan
*/

dpuserType dpuserFunction_isinf(dpuserType x) {
    Dpuser2cUtils::checkFuncArgs("isinf", x);
    dpuserType rv;

    if (x.type == typeDbl) {

        rv.type = typeCon;
        rv.lvalue = _ISINF(x.dvalue);

    } else if (x.type == typeCom) {
        rv.type = typeCon;

        if ((_ISINF(x.cvalue->real()) == 0) &&
            (_ISINF(x.cvalue->imag()) == 0)) {
            /* e.g. complex(2,5) */
            rv.lvalue = 0;
        } else if ( ((_ISINF(x.cvalue->real()) > 0) &&
                    (_ISINF(x.cvalue->imag()) < 0)) ||
                    ((_ISINF(x.cvalue->real()) < 0) &&
                    (_ISINF(x.cvalue->imag()) > 0))) {
            /* complex(inf, -inf) or complex(-inf, inf)*/
            rv.type = typeDbl;
            rv.dvalue = -0.5;
        } else if ((_ISINF(x.cvalue->real()) > 0) ||
                   (_ISINF(x.cvalue->imag()) > 0)) {
            /* e.g. complex(inf, 2) or complex(2, inf)*/
            rv.lvalue = 1;
        } else if ((_ISINF(x.cvalue->real()) < 0) ||
                   (_ISINF(x.cvalue->imag()) < 0)) {
            /* e.g. complex(-inf, 2) or complex(2, -inf)*/
            rv.lvalue = -1;
        }
    }

    return rv;
}

/*!
evalgaussfit
Evaluate a gaussfit along a vector of x values.
Syntax
result = evalgaussfit(X, fit)
Arguments
X: A vector of x values at which to evaluate the gaussian fit
fit: The gaussian fit, as returned by the gaussfit function
Returns
A vectors, same length as X, with the gaussian evaluated at the X positions supplied.
See also
function_gaussfit
*/

dpuserType dpuserFunction_evalgaussfit(dpuserType x, dpuserType fit) {
    Dpuser2cUtils::checkFuncArgs("evalgaussfit", x, fit);

    dpuserType rv;
    rv = x;
    if (rv.type == typeFits) {
        evaluate_gauss(*rv.fvalue, *x.fvalue, *fit.fvalue);
    } else if (rv.type == typeDbl || rv.type == typeCon) {
        rv.dvalue = evaluate_gauss(x.toDouble(), *fit.fvalue);
    }

    return rv;
}

/*!
polyroots
Return the roots of a polynomial. The polynomial is defined as P(x) = P[1] + P[2]*x + P[3]*x^2 + ... + P[n]*x^(n-1)
Syntax
result = polyroots(P [, derivative])
Arguments
P: The polynomial
derivative: Optional, if an integer number is given, the nth derivative is returned.
Returns
A vector with the roots (or derivatives of) the polynomial
Examples
Calculate the roots of the polynomias 1 + x^2 = 0:
<code>print polyroots([1, 0, 1]), /values
*/

dpuserType dpuserFunction_polyroots(dpuserType polynomial) {
    Dpuser2cUtils::checkFuncArgs("polyroots", polynomial);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    if (polyroots(*polynomial.fvalue, *rv.fvalue) < 0) {
        throw dpuserTypeException("polyroots failed");
    }

    return rv;
}

dpuserType dpuserFunction_polyroots(dpuserType polynomial, dpuserType derivative) {
    Dpuser2cUtils::checkFuncArgs("polyroots", polynomial, derivative);

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    if (polyroots(*polynomial.fvalue, *rv.fvalue, derivative.lvalue) < 0) {
        throw dpuserTypeException("polyroots failed");
    }

    return rv;
}

//TODO
/*!
interpolate
Returns the interpolated value of a given position (x,y). If x and y are integers the value at that position is returned without any interpolation.
Syntax
result = interpolate(A, x, y [, kernel_size])
Arguments
A: A 2D-image.
x: An real number.
x: An real number.
kernel_size: Any integer number, default is 4. Remember that points near the image border can't be interpolated (border-size is half the kernel-size)
*/

//TODO
/*!
radialprofile
Returns a radial profile centered on (x,y)
Syntax
result = radialprofile(A, x, y)
Arguments
A: A 2D-image
x: An integer number, the x center of the profile
y: An integer number, the y center of the profile
See also
function_ellipticalprofile
*/

//TODO
/*!
ellipticalprofile
Returns an elliptical profile centered on (x,y) with position angle a, aspect ratio r, and width w.
Syntax
result = radialprofile(A, x, y, a, r, w)
Arguments
A: A 2D-image
x: An integer number, the x center of the profile
y: An integer number, the y center of the profile
a: A real number denoting the angle of the ellipse
r: A real number denoting the aspect ratio of the ellipse
w: An integer number giving the width of single elements of the ellipse
See also
function_radialprofile
*/

//TODO
/*!
multigaussfit
Fit multiple gaussians to a vector. The gauss function is defined as G(x) = SUM_i [A_i + B_i*exp(-4ln2*(x-x0_i)^2/fwhm_i^2)].
Syntax
result = multigaussfit(X, Y, errors, estimate [, chisq])
Arguments
X: A vector containing the x values to be fit
Y: A vector containing the y values to be fit
errors: The errors in Y
estimate: Initial estimate for the fit of the form [A, B, x0, fwhm]
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [A1, B1, x01, fwhm1, A2, ..., d_A1, d_B1, d_x01, d_fwhm1, d_A2, ...]
See also
function_gaussfit
*/

//TODO
/*!
readfitsall
Read a FITS file and all its extensions. The FITS will be stored in a variable to type dpuserTypeList.
Syntax
result = readfitsall(filename)
Arguments
filename: A string pointing to the file to be read
See also
function_readfits
function_readfitsextension
*/

//TODO
/*!
cubequantile
Does a quantile of all images in X.
Syntax
result = cubequantile(X, quantile)
Arguments
X: A matrix or a FITS file name. If a FITS file name is given, the global variable tmpmem determines how many bytes of data are read in at a time.
quantile: The quantile to be returned (0...1).
See also
function_cubemedian
*/

//TODO
/*!
quantile
Compute the quantile of X.
Syntax
result = quantile(X, quantile)
Arguments
X: A matrix.
quantile: The quantile to be returned (0...1).
See also
function_median
*/

//TODO
/*!
sersic2dsmoothfit
Fit a two-dimensional smoothed sersic function to a rectangular grid (i.e. an image).<br>
The 2D sersic function is defined as I(x,y) = c + Ie * exp(-bn * (R/Re)^(1/n) - 1).<br>
R is an elliptical equation: R = sqrt(xp^2 + (yp / q)^2).<br>
xp and yp describe a rotation: xp = (x - x0) * cos(angle) + (y - y0) * sin(angle), yp = - (x - x0) * sin(angle) + (y - y0) * cos(angle) and bn = 1.9992 * n - 0.3271<br>
The resulting 2D sersic function is then smoothed by convolving with a gaussian with a FWHM given by the smooth parameter.
Syntax
result = sersic2dsmoothfit(image, errors, smooth, estimate [, chisq])
Arguments
image: The image to be fit
errors: The errors in the image values
smooth: The FWHM for the gaussian smoothing
estimate: Initial estimate for the fit of the form [c, Ie, Re, x0, y0, angle, q, n]. If n is provided with a negative sign, it won't be fitted and assumed as fixed (with positive sign, of course).
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [c, Ie, Re, x0, y0, angle, q, n] or [c, Ie, Re, x0, y0, angle, q] if n was kept fixed.
See also
function_sersic2dfit
function_sersic2dsmoothsimplefit
*/

//TODO
/*!
sersic2dsmoothsimplefit
Fit a two-dimensional smoothed sersic function to a rectangular grid (i.e. an image).<br>
The 2D sersic function is defined as I(x,y) = c + Ie * exp(-bn * (R/Re)^(1/n) - 1).<br>
R is an elliptical equation: R = sqrt(xp^2 + (yp / q)^2).<br>
xp and yp describe a rotation: xp = (x - x0) * cos(angle) + (y - y0) * sin(angle), yp = - (x - x0) * sin(angle) + (y - y0) * cos(angle) and bn = 1.9992 * n - 0.3271<br>
The resulting 2D sersic function is then smoothed by convolving with a gaussian with a FWHM given by the smooth parameter.
Syntax
result = sersic2dsmoothsimplefit(image, errors, x0, y0, width, smooth [, chisq])
Arguments
image: The image to be fit
errors: The errors in the image values
x0, y0: The center coordinates of the window to fit
width: The width of the window to fit
smooth: The FWHM for the gaussian smoothing
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters in the form [c, Ie, Re, x0, y0, angle, q, n] or [c, Ie, Re, x0, y0, angle, q] if n was kept fixed.
See also
function_sersic2dfit
function_sersic2dsmoothfit
*/

/*!
readfitsbintable
Read a FITS binary table from disk file
Syntax
result = readfitsbintable(file, extension, column)
Arguments
file: The FITS file to be read
extension: Which extension to read, either an integer number >= 1, or a string with the EXTNAME
column: Which column to read, either an integer number >= 1, or a string with the Column Name
Returns
The column of the specified FITS binary table
*/

dpuserType dpuserFunction_readfitsbintable(dpuserType fname, dpuserType ext, dpuserType col) {
    Dpuser2cUtils::checkFuncArgs("readfitsbintable", fname, ext, col);

    dpuserType rv;
    try {
        int extension = 0;
        Fits _tmp;

        // check for valid extension
        if (ext.type == typeStr) {
            extension = _tmp.FindExtensionByName(fname.toString().c_str(), ext.toString().c_str());
        } else {
            extension = ext.toInt();
        }
        if (extension < 1) {
            throw dpuserTypeException("readfitsbintable: Invalid extension");
        }
        if (!_tmp.ReadFITSExtension(fname.svalue->c_str(), extension)) {
            throw dpuserTypeException("readfitsbintable: Could not read extension");
        }

        // check for number of columns and which to load (or all)
        bool readList = false;
        int  count    = 0,
             column   = 0;
        if (col.type == typeStr) {
            column = _tmp.FindColumnByName(col.toString().c_str());
        } else {
            column = col.toInt();
            count = _tmp.ListTableColumns(fname.svalue->c_str(), extension).size();
            if (count == -1) {
                throw dpuserTypeException("readfitsbintable: Unable to open file!");
            } else if (column > count) {
                throw dpuserTypeException("readfitsbintable: The specified extension does not exist!");
            } else if ((column == 0) && (count > 1)) {
                // read FITS table with extensions
                readList = true;
            }
            if ((column == 0) && (count == 1)) {
                // this is needed for reading complete GRAV-files containing single bintables in an extension
                column++;
            }
        }
        if (column < 0) {
            throw dpuserTypeException("readfitsbintable: Invalid column");
        }


        dpString name;
        name.sprintf("%s, ext #%d, col #%d", fname.svalue->c_str(), extension, column);

        if (!readList) {
            // create single Fits or dpStringList
            nodeEnum type = _tmp.GetBintableType(column);
            if (type == typeFits) {
                // column contains values --> Fits
                rv.type = typeFits;
                rv.fvalue = CreateFits();
                rv.fvalue->setFileName(name.c_str());
                rv.fvalue->setExtensionNumber(extension);
                rv.fvalue->setColumnNumber(column);
                if (!_tmp.GetBintableColumn(column, *rv.fvalue)) {
                    throw dpuserTypeException("readfitsbintable: Could not read column!");
                }
            } else if (type == typeStrarr) {
                // column contains characters --> String array
                rv.type = typeStrarr;
                rv.arrvalue = CreateStringArray();
                rv.arrvalue->setFileName(name.c_str());
                if (!_tmp.GetBintableColumn(column, *rv.arrvalue)) {
                    throw dpuserTypeException("readfitsbintable: Could not read column!");
                }
            }
        } else {
            // create dpuserTypeList
            rv.type = typeDpArr;
            rv.dparrvalue = CreateDpList();

            for (int i = 1; i <= count; i++) {
                dpuserType *dpt = new dpuserType;
                dpt->type = _tmp.GetBintableType(i);
                if (dpt->type == typeFits) {
                    // column contains values --> Fits

                    dpt->fvalue = new Fits;
                    dpt->fvalue->setFileName(name.c_str());
                    dpt->fvalue->setExtensionNumber(extension);
                    dpt->fvalue->setColumnNumber(column);
                    if (i == 1) {
                        // doesn't work since gfd has to be rewinded first
                        // the same if has to be applied for tmpStrarr....
//                        dpt->fvalue->ReadFitsExtensionHeader(_tmp.gfd, extension);
                        // this should work if header wasn't private...
//                        dpt->fvalue->header = (char *)malloc(_tmp.HeaderLength * sizeof(char));
//                        dpt->fvalue->HeaderLength = _tmp.HeaderLength;
//                        strcpy(dpt->fvalue->header, _tmp.header);
                    }
                    if (_tmp.GetBintableColumn(i, *dpt->fvalue)) {
//                        dpt->fvalue->extensionType = BINTABLE;
                        rv.dparrvalue->push_back(dpt);
                    } else {
                        delete dpt;
                    }
                } else if (dpt->type == typeStrarr) {
                    // column contains characters --> String array
                    dpt->type = typeStrarr;
                    dpt->arrvalue = new dpStringList;
                    dpt->arrvalue->setFileName(name.c_str());

                    if (_tmp.GetBintableColumn(i, *dpt->arrvalue)) {
                        rv.dparrvalue->push_back(dpt);
                    } else {
                        delete dpt;
                    }
                } else {
                    delete dpt;
                }
            }
        }
    } catch (std::exception &e) {

    }

    return rv;
}

/*!
listfitsextensions
List extension names of all extensions in a FITS file
Syntax
result = listfitsextensions(fname)
Arguments
fname: A string containing the FITS file name
Returns
A string array with all FITS extension names
*/

dpuserType dpuserFunction_listfitsextensions(dpuserType fname) {
    Dpuser2cUtils::checkFuncArgs("listfitsextensions", fname);

    dpuserType rv;
    Fits _tmp;
    rv.type = typeStrarr;
    rv.arrvalue = CreateStringArray(_tmp.ListFitsExtensions(fname.toString().c_str()));

    return rv;
}

/*!
listtablecolumns
List FITS binary table column names of specified extension in a FITS file
Syntax
result = listtablecolumns(fname, extension)
Arguments
fname: A string containing the FITS file name
extension: Which extension to read, either an integer number >= 1, or a string with the EXTNAME
Returns
A string array with all FITS binary table column names
*/

dpuserType dpuserFunction_listtablecolumns(dpuserType fname, dpuserType ext) {
    Dpuser2cUtils::checkFuncArgs("listtablecolumns", fname, ext);

    int column;
    dpuserType rv;
    Fits _tmp;

    rv.type = typeStrarr;
    if (ext.type == typeStr) {
        column = _tmp.FindExtensionByName(fname.toString().c_str(), ext.toString().c_str());
    } else {
        column = ext.toInt();
    }
    rv.arrvalue = CreateStringArray(_tmp.ListTableColumns(fname.toString().c_str(), column));

    return rv;
}

//TODO
/*!
interpol
1D cubic spline interpolation.
Syntax
result = interpol(y, x, x_new)
Arguments
y: The given y-values as array.
x: The given x-values as array. The arrays x and y must be of same length.
x_new: The new x-values as array.
Returns
The new y-values at given points in x_new.
See also
function_rebin1d
*/

///////////////////////////////////////////////////////////////////////////////////
//	not yet in dpuser
///////////////////////////////////////////////////////////////////////////////////
/*!
where
Create a one-dimensional array of indices which satisfy a given expression.
Syntax
result = where(array comparison scalar)
Arguments
array: An array of any dimension that is to be evaluated
comparison: Any of the known comparison operators (==, !=, >, >=, <, <=)
scalar: A scalar value
Returns
A one-dimensional vector of the indices in array which satisfied the comparison
Examples
Create a sine curve and set all negative values to 0:
<code>sine = sin([0:1000]/100)
<code>sine[where(sine < 0)] = 0
*/

//TODO
/*!
list
Create a FITS list
Syntax
result = list(arg1, ..., arg10)
Arguments
arg1...arg10: Up to 10 FITS or StringArray to be included in the list
Returns
A FITS list with copies of the given arguments
*/

/*!
straightlinefit
Fit a straight line to data with errors in both coordinates
Syntax
result = straightlinefit(x, y, dx, dy [, chisq])
Arguments
x: the x values of the data to fit
y: the y values of the data to fit
dx: the errors in x
dy: the errors in y
chisq: If set to a named variable, the variable is overwritten with the chi-squared of the fit.
Returns
The returned vector contains the best-fit parameters
*/

/*!
mpfit
Fit a given function to data using MPFIT
Syntax
result = mpfit(x, y, err, estimate, function)
Arguments
x: The x-Values of the data
y: the y-Values of the data
err: The errors in y
estimate: Initial estimate of the fit paramters
function: A string describing the function to be fit
Returns
The best-fit parameters
*/

/*!
evalmpfit
Evaluate a function at specified x values
Syntax
result = evalmpfit(x, parameters, function)
Arguments
x: The x-Values where to evaluate
parameters: The parameters of the function to be evaluated
function: A string describing the function to be evaluated
Returns
A vector of same length as x with the function evaluated
Examples
y = evalmpfit(([1:100]-50)/10, [2.3,.2,.1], "a+b*x+c*x*x")
*/

/*!
question
Output a string (or pop up a dialog box on QFitsView) and expect Yes/No answer.
Syntax
result = question(s)
Arguments
s: The string to output
Returns
If the answer starts with n or N, zero is returned. One otherwise.
*/

/*!
nparams
Return the number of parameters passed to a user defined function or procedure
Syntax
result = nparams()
Examples
Define a user written function that adds up a variable amount of values:
<code>function add,a,b,c {
add = a
if (nparams() > 1) add += b
if (nparams() > 2) add += c
}
</code>
*/

/*!
double
Return a value cast to double
Syntax
result = double(x)
Arguments
x: If of any numeric type, cast to double. If a complex number, the real part. If a string, interpreted as number. If a matrix, set the type to R8.
*/

/*!
pyvar
Copy a PYTHON variable to DPUSER
Syntax
result = pyvar(variable)
Arguments
variable: A string naming the PYTHON variable
Returns
A deep copy of the PYTHON variable
Examples
Create a variable within the PYTHON module, and then pass this back to DPUSER:
<code>python "a=1"
<code>hhh = pyvar("a")
*/

/*!
mjd
Returns the Modified Julian Date of given date. Hour, minute, and second default to 0.
Syntax
result = mjd(day, month, year [, hour [, minute [,second]]])
Arguments
day: An integer number.
month: An integer number. 
year: An integer number.
hour: An integer number.
minute: An integer number.
second: A real number.
See also
function_jd
*/


dpuserType dpuserFunction_where(dpuserType arg, char *cmp, dpuserType what) {
    if (arg.type != typeFits)
        throw dpuserTypeException("where: Wrong argument type.");

    if (!what.isReal())
        throw dpuserTypeException("where: Wrong argument type.");

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->where(*arg.fvalue, cmp, what.toDouble());

// since the FITS class where method returns C-based index, we have to increment
    if (rv.fvalue->Nelements() > 0) {
        for (dpint64 i = 0; i < rv.fvalue->Nelements(); i++) {
            rv.fvalue->i4data[i]++;
        }
    }
    return rv;
}

dpuserType dpuserFunction_where(dpuserType arg, char *cmp, dpuserType what, dpuserType elements) {
    dpuserType rv = dpuserFunction_where(arg, cmp, what);
    elements = dpuserFunction_nelements(rv);

    return rv;
}

dpuserType dpuserFunction_pow(dpuserType arg, dpuserType arg1) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon: switch (arg1.type) {
            case typeCon:
                rv.type = typeDbl;
                rv.dvalue = pow((double)arg.lvalue, (double)arg1.lvalue);
                break;
            case typeDbl:
                rv.type = typeDbl;
                rv.dvalue = pow((double)arg.lvalue, arg1.dvalue);
                break;
            default:
                throw dpuserTypeException("Wrong arguments to power operator\n");
                break;
        }
            break;
        case typeDbl: switch (arg1.type) {
            case typeCon:
                rv.type = typeDbl;
                rv.dvalue = pow(arg.dvalue, (double)arg1.lvalue);
                break;
            case typeDbl:
                rv.type = typeDbl;
                rv.dvalue = pow(arg.dvalue, arg1.dvalue);
                break;
            default:
                throw dpuserTypeException("Wrong arguments to pow operator\n");
                break;
            }
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->setType(R4);
            switch (arg1.type) {
            case typeCon:
                rv.fvalue->power((double)arg1.lvalue);
                break;
            case typeDbl:
                rv.fvalue->power(arg1.dvalue);
                break;
            default:
                throw dpuserTypeException("Wrong arguments to power operator\n");
                break;
            }
            break;
        default:
            throw dpuserTypeException("Wrong arguments to power operator\n");
            break;
    }

    return rv;
}

dpuserType dpuserFunction_modulo(dpuserType arg, dpuserType arg1) {
    dpuserType rv;

    switch (arg.type) {
        case typeCon: switch (arg1.type) {
            case typeCon:
                rv.type = typeCon;
                if (arg1.lvalue != 0) rv.lvalue = arg.lvalue % arg1.lvalue;
                else rv.lvalue = 0;
                break;
            case typeDbl:
                rv.type = typeDbl;
                if (arg1.dvalue != 0.0) {
                    rv.dvalue = (double)(int)(arg.lvalue / arg1.dvalue);
                    rv.dvalue = arg.lvalue - arg1.dvalue * rv.dvalue;
                } else {
                    rv.dvalue = 0.0;
                }
                break;
            default:
                throw dpuserTypeException("Wrong arguments to modulo operator\n");
                break;
        }
            break;
        case typeDbl: switch (arg1.type) {
            case typeCon:
                rv.type = typeDbl;
                if (arg1.lvalue != 0) {
                    rv.dvalue = (double)(int)(arg.dvalue / ((double)arg1.lvalue));
                    rv.dvalue = arg.dvalue - arg1.lvalue * rv.dvalue;
                } else {
                    rv.dvalue = 0.0;
                }
                break;
            case typeDbl:
                rv.type = typeDbl;
                if (arg1.dvalue != 0.0) {
                    rv.dvalue = (double)(int)(arg.dvalue / arg1.dvalue);
                    rv.dvalue = arg.dvalue - arg1.dvalue * rv.dvalue;
                } else {
                    rv.dvalue = 0.0;
                }
                break;
            default:
                throw dpuserTypeException("Wrong arguments to modulo operator\n");
                break;
            }
            break;
        case typeFits:
            rv.type = typeFits;
            rv.clone(arg);
            rv.fvalue->setType(R4);
            switch (arg1.type) {
            case typeCon:
                if (arg1.lvalue != 0) {
                    double value;
                    for (unsigned long n = 0; n < rv.fvalue->Nelements(); n++) {
                        value = (double)(int)(arg.fvalue->ValueAt(n) / ((double)arg1.lvalue));
                        rv.fvalue->r4data[n] = arg.fvalue->ValueAt(n) - arg1.lvalue * value;
                    }
                } else {
                    *rv.fvalue = 0.0;
                }
                break;
            case typeDbl:
                if (arg1.dvalue != 0.0) {
                    double value;
                    for (unsigned long n = 0; n < rv.fvalue->Nelements(); n++) {
                        value = (double)(int)(arg.fvalue->ValueAt(n) / arg1.dvalue);
                        rv.fvalue->r4data[n] = arg.fvalue->ValueAt(n) - arg1.dvalue * value;
                    }
                } else {
                    *rv.fvalue = 0.0;
                }
                break;
            default:
                throw dpuserTypeException("Wrong arguments to modulo operator\n");
                break;
            }
            break;
        default:
            throw dpuserTypeException("Wrong arguments to modulo operator\n");
            break;
    }

    return rv;
}

dpuserType dpuserFunction_matrixmul(dpuserType arg1, dpuserType arg2) {
    dpuserType rv;

    switch (arg1.type) {
        case typeFits: switch(arg2.type) {
            case typeFits:
                rv.clone(arg1);
                rv.fvalue->matrix_mul(*arg2.fvalue);
            break;
            case typeFitsFile: {
                Fits a;

                if (!a.ReadFITS(arg2.ffvalue->c_str())) rv.type = typeUnknown;
                else {
                    rv.clone(arg1);
                    rv.fvalue->matrix_mul(a);
                }
            }
            break;
            case typeCon:
                rv.clone(arg1);
                rv.fvalue->mul((double)(arg2.lvalue));
            break;
            case typeDbl:
                rv.clone(arg1);
                rv.fvalue->mul(arg2.dvalue);
            break;
            default:
                throw dpuserTypeException("Cannot do matrix multiplication with this type\n");
                break;
        }
    }

    return rv;
}

dpuserType execCompiledFunction(dpString which, int nargs, dpuserType *args) {
    bool found = FALSE;
    int i, w;
    dpuserType rv;

    for (i = 0; i < compiledfunctions.size(); i++) {
        if (which == compiledfunctions[i].name) {
            w = i;
            if (nargs == compiledfunctions[i].nargs) found = TRUE;
        }
    }
    if (found) switch (nargs) {
        case 0: rv = compiledfunctions[w].arg0(); break;
        case 1: rv = compiledfunctions[w].arg1(args[0]); break;
        case 2: rv = compiledfunctions[w].arg2(args[0],args[1]); break;
        case 3: rv = compiledfunctions[w].arg3(args[0],args[1],args[2]); break;
        case 4: rv = compiledfunctions[w].arg4(args[0],args[1],args[2],args[3]); break;
        case 5: rv = compiledfunctions[w].arg5(args[0],args[1],args[2],args[3],args[4]); break;
        case 6: rv = compiledfunctions[w].arg6(args[0],args[1],args[2],args[3],args[4],args[5]); break;
        case 7: rv = compiledfunctions[w].arg7(args[0],args[1],args[2],args[3],args[4],args[5],args[6]); break;
        case 8: rv = compiledfunctions[w].arg8(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7]); break;
        case 9: rv = compiledfunctions[w].arg9(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8]); break;
        case 10: rv = compiledfunctions[w].arg10(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9]); break;
        case 11: rv = compiledfunctions[w].arg11(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10]); break;
        case 12: rv = compiledfunctions[w].arg12(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11]); break;
        case 13: rv = compiledfunctions[w].arg13(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12]); break;
        case 14: rv = compiledfunctions[w].arg14(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13]); break;
        case 15: rv = compiledfunctions[w].arg15(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14]); break;
        case 16: rv = compiledfunctions[w].arg16(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14],args[15]); break;
        case 17: rv = compiledfunctions[w].arg17(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14],args[15],args[16]); break;
        case 18: rv = compiledfunctions[w].arg18(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14],args[15],args[16],args[17]); break;
        case 19: rv = compiledfunctions[w].arg19(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14],args[15],args[16],args[17],args[18]); break;
        case 20: rv = compiledfunctions[w].arg20(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14],args[15],args[16],args[17],args[18],args[19]); break;
        default: break;
    }
    switch (rv.type) {
        case typeFits:
            addToListOfFits(rv.fvalue);
            break;
        default:
            break;
    }

    return rv;
}


///////////////////////////////////////////////////////////////////////////////////
//	helper functions
///////////////////////////////////////////////////////////////////////////////////
//Alex
CJulianDay convertToJulianDay(dpuserType day, dpuserType month, dpuserType year, dpuserType hour, dpuserType minute, dpuserType second)
{
    CJulianDay jd;
    jd.SetJD(day.lvalue, month.lvalue, year.lvalue, hour.lvalue, minute.lvalue, second.toDouble());
    return jd;
}

dpuserType createarray(int x, int y, int z, int b) {//FitsBitpix b) {
    dpuserType rv;

    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->create(x, y, z, (FitsBitpix)b);

    return rv;
}

//Alex
double calcpolyfit(dpuserType &rv, dpuserType X, dpuserType N) {
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    Fits x, err;

    x.create(X.fvalue->Nelements(), 1, I4);
    err.create(X.fvalue->Nelements(), 1, I1);
    for (int i = 0; i < X.fvalue->Nelements(); i++) {
        x.i4data[i] = i;
        err.i1data[i] = 1;
    }
    return polyfit1d(*rv.fvalue, x, *X.fvalue, err, N.lvalue);
}

//Alex
// rv, xerr, yerr are used as return values
bool calctranscoords(dpuserType &rv, double &xerr, double &yerr, const dpuserType &masterlist, const dpuserType &refstars, const dpuserType &imstars, const dpString &option1, const dpString &option2)
{
    double *xref = NULL, *yref = NULL, *xim = NULL, *yim = NULL;
    double SY[10] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
    double SX[10] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
    double x, y;
    int i, llen, ncoords;
    bool success = true;

    rv.type = typeFits;
    rv.clone(masterlist);

    llen = refstars.fvalue->Naxis(2);
    if (llen < 1)
        throw dpuserTypeException("transcoords: Must have at least 3 reference sources\n");
    if ((refstars.fvalue->Naxis(1) != 2) || (imstars.fvalue->Naxis(1) != 2))
        throw dpuserTypeException("transcoords: Input matrices are not of correct size: Must be 2xn\n");
    if (refstars.fvalue->Naxis(2) != imstars.fvalue->Naxis(2))
        throw dpuserTypeException("transcoords: The two reference lists do not contain the same number of sources\n");
    if (masterlist.fvalue->Naxis(1) != 2)
        throw dpuserTypeException("transcoords: The master coordinate list does not contain 2 coordinates per star\n");

    if (rv.fvalue->setType(R8)) {

        xref = (double *)malloc(llen * sizeof(double));
        yref = (double *)malloc(llen * sizeof(double));
        xim = (double *)malloc(llen * sizeof(double));
        yim = (double *)malloc(llen * sizeof(double));

        if ((xref == NULL) || (yref == NULL) || (xim == NULL) || (yim == NULL)) {
            if (xref != NULL) free(xref);
            if (yref != NULL) free(yref);
            if (xim != NULL) free(xim);
            if (yim != NULL) free(yim);
            throw dpuserTypeException("transcoords: Allocation failed.\n");
        } else {
            ncoords = 0;
            for (i = 0; i < llen; i++) {
                xref[ncoords] = refstars.fvalue->ValueAt(refstars.fvalue->C_I(0, i));
                yref[ncoords] = refstars.fvalue->ValueAt(refstars.fvalue->C_I(1, i));
                xim[ncoords] = imstars.fvalue->ValueAt(imstars.fvalue->C_I(0, i));
                yim[ncoords] = imstars.fvalue->ValueAt(imstars.fvalue->C_I(1, i));
                if ((xim[ncoords] == -1.) || (yim[ncoords] == -1.) || (xref[ncoords] == -1.) || (yref[ncoords] == -1.)) ncoords--;
                ncoords++;
            }
            if (((option1 == "linear") || (option2 == "linear")) && (ncoords > 2)) {
                success = trans_matrix(ncoords, 3, xref, yref, xim, yim, SX, SY, &xerr, &yerr);
            } else if (((option1 == "cubic") || (option2 == "cubic")) && (ncoords > 10)) {
                success = trans_matrix(ncoords, 10, xref, yref, xim, yim, SX, SY, &xerr, &yerr);
            } else {
//				success = trans_matrix(ncoords, 6, xref, yref, xim, yim, SX, SY, &xerr, &yerr);

                int nused = 6;
                if (ncoords < nused) nused = ncoords;
                success = trans_matrix(ncoords, nused, xref, yref, xim, yim, SX, SY, &xerr, &yerr);
            }
        }

        if (success) {
            if (!((option1 == "silent") || (option2 == "silent"))) {
                if ((option1 == "cubic") || (option2 == "cubic"))
                    dp_output("transformation matrix is:\nx = %5g + %5gx + %5gy + %5gxy + %5gx^2 + %5gy^2 + %5gx^2y + %5gxy^2 + %5gx^3 + %5gy^3\ny = %5g + %5gx + %5gy + %5gxy + %5gx^2 + %5gy^2 + %5gx^2y + %5gxy^2 + %5gx^3 + %5gy^3\n", SX[9], SX[0], SX[1], SX[4], SX[2], SX[3], SX[7], SX[8], SX[5], SX[6], SY[9], SY[0], SY[1], SY[4], SY[2], SY[3], SY[7], SY[8], SY[5], SY[6]);
                else
                    dp_output("transformation matrix is:\nx = %5g + %5gx + %5gy + %5gxy + %5gx^2 + %5gy^2\ny = %5g + %5gx + %5gy + %5gxy + %5gx^2 + %5gy^2\n", SX[5], SX[0], SX[1], SX[4], SX[2], SX[3], SY[5], SY[0], SY[1], SY[4], SY[2], SY[3]);
            }
            for (i = 0; i < rv.fvalue->Naxis(2); i++) {
                x = masterlist.fvalue->ValueAt(masterlist.fvalue->C_I(0, i));
                y = masterlist.fvalue->ValueAt(masterlist.fvalue->C_I(1, i));
                if ((option1 == "cubic") || (option2 == "cubic")) {
                    rv.fvalue->r8data[rv.fvalue->C_I(0, i)] = SX[0]*x+SX[1]*y+SX[2]*x*x+SX[3]*y*y+SX[4]*x*y+SX[5]*x*x*x+SX[6]*y*y*y+SX[7]*x*x*y+SX[8]*x*y*y+SX[9];
                    rv.fvalue->r8data[rv.fvalue->C_I(1, i)] =  SY[0]*x+SY[1]*y+SY[2]*x*x+SY[3]*y*y+SY[4]*x*y+SY[5]*x*x*x+SY[6]*y*y*y+SY[7]*x*x*y+SY[8]*x*y*y+SY[9];
                } else {
                    rv.fvalue->r8data[rv.fvalue->C_I(0, i)] = SX[0]*x+SX[1]*y+SX[2]*x*x+SX[3]*y*y+SX[4]*x*y+SX[5];
                    rv.fvalue->r8data[rv.fvalue->C_I(1, i)] =  SY[0]*x+SY[1]*y+SY[2]*x*x+SY[3]*y*y+SY[4]*x*y+SY[5];
                }
            }
        }
    }
    free(xref);
    free(yref);
    free(xim);
    free(yim);

    return success;
}

//Alex
double calcpolyfitxy(dpuserType &rv, const dpuserType &X, const dpuserType &Y, const dpuserType &N)
{
    rv.type = typeFits;
    rv.fvalue = CreateFits();

    Fits err;
    int i;

    err.create(X.fvalue->Nelements(), 1, I1);
    for (i = 0; i < X.fvalue->Nelements(); i++) {
        err.i1data[i] = 1;
    }
    return polyfit1d(*rv.fvalue, *X.fvalue, *Y.fvalue, err, N.lvalue);
}

//Alex
double calcgaussfit(dpuserType &rv, const dpuserType &X, const dpuserType &Y, const dpuserType &errors, const dpuserType &estimate)
{
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->copy(*estimate.fvalue);
    return gaussfit(*rv.fvalue, *X.fvalue, *Y.fvalue, *errors.fvalue);
}

//Alex
double calcgauss2dfit(dpuserType &rv, const dpuserType &image, const dpuserType &errors, const dpuserType &estimate)
{
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->copy(*estimate.fvalue);
    return gauss2dfit(*rv.fvalue, *image.fvalue, *image.fvalue, *errors.fvalue);
}

//Alex
double calcpolyfitxyerr(dpuserType &rv, const dpuserType &X, const dpuserType &Y, const dpuserType &N, const dpuserType &error)
{
    Fits err;

    rv.type = typeFits;
    rv.fvalue = CreateFits();
    err.create(X.fvalue->Nelements(), 1, R8);
    if (error.type == typeFits) {
        for (int i = 0; i < error.fvalue->Nelements(); i++) {
            err.r8data[i] = error.fvalue->ValueAt(i);
        }
    } else {
        for (int i = 0; i < error.fvalue->Nelements(); i++) {
            err.r8data[i] = error.dvalue;
        }
    }
    return polyfit1d(*rv.fvalue, *X.fvalue, *Y.fvalue, err, N.lvalue);
}

//Alex
dpuserType calctransmatrix(double &xerror, double &yerror, const dpuserType &refstars, const dpuserType &imstars, dpString option)
{
    double *xref = NULL, *yref = NULL, *xim = NULL, *yim = NULL;
    double SY[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
    double SX[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
    int i, llen, ncoords;

    llen = refstars.fvalue->Naxis(2);
    if (llen < 1)
        throw dpuserTypeException("transmatrix: Must have at least 3 reference sources\n");
    if ((refstars.fvalue->Naxis(1) != 2) || (imstars.fvalue->Naxis(1) != 2))
        throw dpuserTypeException("transmatrix: Input matrices are not of correct size: Must be 2xn\n");
    if (refstars.fvalue->Naxis(2) != imstars.fvalue->Naxis(2))
        throw dpuserTypeException("transmatrix: The two reference lists do not contain the same number of sources\n");

    dpuserType rv;
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    if (rv.fvalue->create(2, 6, R8)) {
        xref = (double *)malloc(llen * sizeof(double));
        yref = (double *)malloc(llen * sizeof(double));
        xim = (double *)malloc(llen * sizeof(double));
        yim = (double *)malloc(llen * sizeof(double));

        bool success = false;
        if ((xref == NULL) || (yref == NULL) || (xim == NULL) || (yim == NULL)) {
            if (xref != NULL) free(xref);
            if (yref != NULL) free(yref);
            if (xim != NULL) free(xim);
            if (yim != NULL) free(yim);
            throw dpuserTypeException("transmatrix: Memory allocation failed\n");
        } else {
            ncoords = 0;
            for (i = 0; i < llen; i++) {
                xref[ncoords] = refstars.fvalue->ValueAt(refstars.fvalue->C_I(0, i));
                yref[ncoords] = refstars.fvalue->ValueAt(refstars.fvalue->C_I(1, i));
                xim[ncoords] = imstars.fvalue->ValueAt(imstars.fvalue->C_I(0, i));
                yim[ncoords] = imstars.fvalue->ValueAt(imstars.fvalue->C_I(1, i));
                if ((xim[ncoords] == -1.) || (yim[ncoords] == -1.) || (xref[ncoords] == -1.) || (yref[ncoords] == -1.)) ncoords--;
                ncoords++;
            }
            if ((option == "linear") && (ncoords > 2)) {
                success = trans_matrix(ncoords, 3, xref, yref, xim, yim, SX, SY, &xerror, &yerror);
            } else {
                success = trans_matrix(ncoords, ncoords, xref, yref, xim, yim, SX, SY, &xerror, &yerror);
            }
        }
        if (success) {
            dp_output("transformation matrix is:\nx = %5g + %5gx + %5gy + %5gxy + %5gx^2 + %5gy^2\ny = %5g + %5gx + %5gy + %5gxy + %5gx^2 + %5gy^2\n", SX[5], SX[0], SX[1], SX[4], SX[2], SX[3], SY[5], SY[0], SY[1], SY[4], SY[2], SY[3]);
            rv.fvalue->r8data[0] = SX[5];
            rv.fvalue->r8data[1] = SX[0];
            rv.fvalue->r8data[2] = SX[1];
            rv.fvalue->r8data[3] = SX[4];
            rv.fvalue->r8data[4] = SX[2];
            rv.fvalue->r8data[5] = SX[3];
            rv.fvalue->r8data[6] = SY[5];
            rv.fvalue->r8data[7] = SY[0];
            rv.fvalue->r8data[8] = SY[1];
            rv.fvalue->r8data[9] = SY[4];
            rv.fvalue->r8data[10] = SY[2];
            rv.fvalue->r8data[11] = SY[3];
        }
    }
    free(xref);
    free(yref);
    free(xim);
    free(yim);

    return rv;
}

//Alex
dpuserType calcreform(const int &nargs, const dpuserType &X, const dpuserType &n1, const dpuserType &n2, const dpuserType &n3)
{
    dpuserType rv;

    if (X.type != typeFits) {
        rv.clone(X);
    }
    else {
        if (nargs == 1) {
            rv.type = typeFits;
            rv.clone(X);
            rv.fvalue->deflate();
        } else {
            long n = n1.lvalue;

            if (nargs > 2)
                n *= n2.lvalue;
            if (nargs > 3)
                n *= n3.lvalue;
            if (n != (long)X.fvalue->Nelements())
                throw dpuserTypeException("reform: must have the same number of elements as the original arrayn\n");
            else {
                rv.type = typeFits;
                rv.clone(X);
                rv.fvalue->setNaxis(0, nargs - 1);
                if (nargs > 1)
                    rv.fvalue->setNaxis(1, n1.lvalue);
                if (nargs > 2)
                    rv.fvalue->setNaxis(2, n2.lvalue);
                if (nargs > 3)
                    rv.fvalue->setNaxis(3, n3.lvalue);
                for (n = nargs; n < 9; n++) {
                    rv.fvalue->setNaxis(n, 1);
                }
            }
        }
    }
    return rv;
}

//Alex
double calcmultigauss2dfit(dpuserType &rv, const dpuserType &image, const dpuserType &errors, const dpuserType &estimate)
{
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->copy(*estimate.fvalue);

    return multigauss2dfit(*rv.fvalue, *image.fvalue, *errors.fvalue, (rv.fvalue->Nelements() - 2) / 3);
}

//Alex
double calcsincfit(dpuserType &rv, const dpuserType &X, const dpuserType &Y, const dpuserType &errors, const dpuserType &estimate)
{
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->copy(*estimate.fvalue);

    return sincfit(*rv.fvalue, *X.fvalue, *Y.fvalue, *errors.fvalue);
}

//Alex
double calcsinfit(dpuserType &rv, const dpuserType &X, const dpuserType &Y, const dpuserType &errors, const dpuserType &estimate) {
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    rv.fvalue->copy(*estimate.fvalue);

    if (rv.fvalue->Nelements() == 3) {
        return sinfit(*rv.fvalue, *X.fvalue, *Y.fvalue, *errors.fvalue);
    } else if (rv.fvalue->Nelements() == 2) {
        return sinfit2(*rv.fvalue, *X.fvalue, *Y.fvalue, *errors.fvalue);
    }
    return 0.0;
}

//Alex
double calcgauss2dsimplefit(dpuserType &rv, const dpuserType &image, const dpuserType &x, const dpuserType &y, const dpuserType &width)
{
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    return gauss2dsimplefit(*rv.fvalue, *image.fvalue, (int)(x.dvalue + .5), (int)(y.dvalue + .5), (int)(width.dvalue + .5));
}

