#include <math.h>
#include "gsl/gsl_math.h"
#include "fits.h"
#include "dpuser_utils.h"
#include "dpuserAST.h"

#ifdef DPQT
#include <QWriteLocker>
//#include "events.h"
//#include "QFitsMainWindow.h"
#include "QFitsGlobal.h"
#endif /* DPQT */

dpuserType unaryMinusNode::evaluate() {
  dpuserType value = exp->evaluate();
	return -value;
}

dpuserType plusNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeCon: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.lvalue + rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeDbl;
                result.dvalue = leftvalue.lvalue + rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(rightvalue);
                *result.cvalue += leftvalue.lvalue;
                break;
            case typeStr:
                result.type = typeStr;
                result.svalue = CreateString(leftvalue.lvalue);
                *result.svalue += *rightvalue.svalue;
                break;
            case typeFits:
                result.clone(rightvalue);
                *result.fvalue += (double)leftvalue.lvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue += (double)leftvalue.lvalue;
                break;
            case typeStrarr:
                dp_output("Cannot add a string array to an integer number\n");
                break;
            default:
                dp_output("Cannot add this type to an integer number\n");
                break;
        }
            break;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeDbl;
                result.dvalue = leftvalue.dvalue + rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeDbl;
                result.dvalue = leftvalue.dvalue + rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(rightvalue);
                *result.cvalue += leftvalue.dvalue;
                break;
            case typeStr:
                result.type = typeStr;
                result.svalue = CreateString(leftvalue.dvalue);
                *result.svalue += *rightvalue.svalue;
                break;
            case typeFits:
                result.clone(rightvalue);
                *result.fvalue += leftvalue.dvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue += leftvalue.dvalue;
                break;
            case typeStrarr:
                dp_output("Cannot add a string array to a real number\n");
                break;
            default:
                dp_output("Cannot add this type to a real number\n");
                break;
        }
            break;
        case typeCom: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.cvalue += rightvalue.lvalue;
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.cvalue += rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(leftvalue);
                *result.cvalue += *rightvalue.cvalue;
                break;
            case typeStr:
                result.type = typeStr;
                result.svalue = CreateString(*leftvalue.cvalue);
                *result.svalue += *rightvalue.svalue;
                break;
            case typeFits:
        result.clone(rightvalue);
        *result.fvalue += *leftvalue.cvalue;
                break;
            case typeFitsFile:
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    result.fvalue->ReadFITS(rightvalue.ffvalue->c_str());
//                    *result.fvalue += *leftvalue.cvalue;
                dp_output("Cannot add a FITS file to a complex number\n");
                break;
            case typeStrarr:
                dp_output("Cannot add a string array to a complex number\n");
                break;
            default:
                dp_output("Cannot add this type to a complex number\n");
                break;
        }
            break;
        case typeStr: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.svalue += *CreateString(rightvalue.lvalue);
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.svalue += *CreateString(rightvalue.dvalue);
                break;
            case typeCom:
                result.clone(leftvalue);
                *result.svalue += FormatComplexdpString(*rightvalue.cvalue);
                break;
            case typeStr:
                result.clone(leftvalue);
                *result.svalue += *rightvalue.svalue;
                break;
            case typeFits:
//                    CloneValue(*arg2, result);
//                    *result.fvalue += *leftvalue.cvalue;
                dp_output("Cannot add a matrix to a string\n");
                break;
            case typeFitsFile:
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    result.fvalue->ReadFITS(rightvalue.ffvalue->c_str());
//                    *result.fvalue += *leftvalue.cvalue;
                dp_output("Cannot add a FITS file to a string\n");
                break;
            case typeStrarr:
                result.clone(rightvalue);
                result.arrvalue->prepend(*leftvalue.svalue);
                break;
            default:
                dp_output("Cannot add this type to a string\n");
                break;
        }
            break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.fvalue += (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.fvalue += rightvalue.dvalue;
                break;
            case typeCom:
        result.clone(leftvalue);
        *result.fvalue += *rightvalue.cvalue;
        break;
            case typeStr:
                dp_output("Cannot add a string to a matrix\n");
                break;
            case typeFits:
                if (leftvalue.fvalue->matches(*rightvalue.fvalue)) {
                    if (isVariable(leftvalue.fvalue) == 0) {
                        result.clone(leftvalue);
                        *result.fvalue += *rightvalue.fvalue;
                    } else if (isVariable(rightvalue.fvalue) == 0) {
                        result.clone(rightvalue);
                        *result.fvalue += *leftvalue.fvalue;
                    } else {
                        result.clone(leftvalue);
                        *result.fvalue += *rightvalue.fvalue;
                    }
                } else {
                    result.clone(leftvalue);
                    *result.fvalue += *rightvalue.fvalue;
                }
                break;
            case typeFitsFile: {
                Fits a;
                result.type = typeFits;
                result.clone(leftvalue);
                result.fvalue = CreateFits();
                if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue += a;
            }
                break;
            case typeStrarr:
                dp_output("Cannot add a string array to a matrix\n");
                break;
            default:
                dp_output("Cannot add this type to a matrix\n");
                break;
        }
            break;
        case typeFitsFile: switch(rightvalue.type) {
            case typeCon:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue += (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue += rightvalue.dvalue;
                break;
            case typeCom:
                dp_output("Cannot add a complex number to a FITS file\n");
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
//                    else *result.fvalue += *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot add a string to a FITS file\n");
                break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue += *rightvalue.fvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    Fits a;
                    if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                    else *result.fvalue += a;
                }
                break;
            case typeStrarr:
                dp_output("Cannot add a string array to a FITS file\n");
                break;
            default:
                dp_output("Cannot add this type to a FITS file\n");
                break;
        }
            break;
        case typeStrarr: switch(rightvalue.type) {
            case typeCon:
                dp_output("Cannot add an integer number to a string array\n");
                break;
            case typeDbl:
                dp_output("Cannot add a real number to a string array\n");
                break;
            case typeCom:
                dp_output("Cannot add a complex number to a string array\n");
                break;
            case typeStr:
                result.clone(leftvalue);
                result.arrvalue->append(*rightvalue.svalue);
                break;
            case typeFits:
                dp_output("Cannot add a matrix to a string array\n");
                break;
            case typeFitsFile:
                dp_output("Cannot add a FITS file to a string array\n");
                break;
            case typeStrarr:
                result.clone(leftvalue);
                for (dpint64 n = 0; n < rightvalue.arrvalue->count(); n++) result.arrvalue->append((*rightvalue.arrvalue)[n]);
                break;
            default:
                dp_output("Cannot add this type to a string array\n");
                break;
        }
            break;
        default:
            dp_output("Invalid arguments to operator '+'\n");
            break;
    }
    return result;
}

dpuserType minusNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
	dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeCon: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.lvalue - rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeDbl;
                result.dvalue = leftvalue.lvalue - rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(rightvalue);
                *result.cvalue *= -1.;
                *result.cvalue += leftvalue.lvalue;
                break;
            case typeStr:
                dp_output("Cannot subtract a string from an integer number\n");
                break;
            case typeFits:
                result.clone(rightvalue);
                *result.fvalue *= -1.;
                *result.fvalue += (double)leftvalue.lvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    *result.fvalue *= -1.;
                    *result.fvalue += (double)leftvalue.lvalue;
                }
                break;
            case typeStrarr:
                dp_output("Cannot subtract a string array from an integer number\n");
                break;
            default:
                dp_output("Cannot subtract this type from an integer number\n");
                break;
        }
            break;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeDbl;
                result.dvalue = leftvalue.dvalue - rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeDbl;
                result.dvalue = leftvalue.dvalue - rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(rightvalue);
                *result.cvalue *= -1.;
                *result.cvalue += leftvalue.dvalue;
                break;
            case typeStr:
                dp_output("Cannot subtract a string from a real number\n");
                break;
            case typeFits:
                result.clone(rightvalue);
                *result.fvalue *= -1.;
                *result.fvalue += leftvalue.dvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    *result.fvalue *= -1.;
                    *result.fvalue += leftvalue.dvalue;
                }
                break;
            case typeStrarr:
                dp_output("Cannot subtract a string array from a real number\n");
                break;
            default:
                dp_output("Cannot subtract this type from a real number\n");
                break;
        }
            break;
        case typeCom: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.cvalue -= rightvalue.lvalue;
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.cvalue -= rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(leftvalue);
                *result.cvalue -= *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot subtract a string from a complex number\n");
                break;
            case typeFits:
        result.clone(rightvalue);
        result.fvalue->setType(C16);
        for (dpint64 n = 0; n < result.fvalue->Nelements(); n++) {
            result.fvalue->cdata[n].r = leftvalue.cvalue->real() - result.fvalue->cdata[n].r;
            result.fvalue->cdata[n].i = leftvalue.cvalue->imag() - result.fvalue->cdata[n].i;
        }
                break;
            case typeFitsFile:
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    result.fvalue->ReadFITS(rightvalue.ffvalue->c_str());
//                    *result.fvalue += -*leftvalue.cvalue;
                dp_output("Cannot subtract a FITS file from a complex number\n");
                break;
            case typeStrarr:
                dp_output("Cannot subtract a string array from a complex number\n");
                break;
            default:
                dp_output("Cannot subtract this type from a complex number\n");
                break;
        }
            break;
        case typeStr:
            dp_output("Cannot subtract anything from a string\n");
            break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.fvalue -= (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.fvalue -= rightvalue.dvalue;
                break;
            case typeCom:
        result.clone(leftvalue);
        *result.fvalue -= *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot subtract a string from a matrix\n");
                break;
            case typeFits:
                if (leftvalue.fvalue->matches(*rightvalue.fvalue)) {
                    if (isVariable(leftvalue.fvalue) == 0) {
                        result.clone(leftvalue);
                        *result.fvalue -= *rightvalue.fvalue;
                    } else if (isVariable(rightvalue.fvalue) == 0) {
                        result.clone(rightvalue);
                        *result.fvalue *= -1.;
                        *result.fvalue += *leftvalue.fvalue;
                    } else {
                        result.clone(leftvalue);
                        *result.fvalue -= *rightvalue.fvalue;
                    }
                } else {
                    result.clone(leftvalue);
                    *result.fvalue -= *rightvalue.fvalue;
                }
                break;
            case typeFitsFile: {
                Fits a;
                result.type = typeFits;
                result.clone(leftvalue);
                if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue -= a;
            }
                break;
            case typeStrarr:
                dp_output("Cannot subtract a string array from a matrix\n");
                break;
            default:
                dp_output("Cannot subtract this type from a matrix\n");
                break;
        }
            break;
        case typeFitsFile: switch(rightvalue.type) {
            case typeCon:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue -= (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue -= rightvalue.dvalue;
                break;
            case typeCom:
                dp_output("Cannot subtract a complex number from a FITS file\n");
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
//                    else *result.fvalue -= *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot subtract a string from a FITS file\n");
                break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue -= *rightvalue.fvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    Fits a;
                    if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                    else *result.fvalue -= a;
                }
                break;
            case typeStrarr:
                dp_output("Cannot subtract a string array from a FITS file\n");
                break;
            default:
                dp_output("Cannot subtract this type from a FITS file\n");
                break;
        }
            break;
        case typeStrarr:
            dp_output("Cannot subtract anything from a string array\n");
            break;
        default:
            dp_output("Invalid arguments to operator '-'\n");
            break;
    }


    return result;
}

dpuserType multiplyNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
	dpuserType rightvalue = right->evaluate();
    dpuserType result;
	
    switch (leftvalue.type) {
        case typeCon: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.lvalue * rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeDbl;
                result.dvalue = leftvalue.lvalue * rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(rightvalue);
                *result.cvalue *= leftvalue.lvalue;
                break;
            case typeStr:
                dp_output("Cannot multiply a string to an integer number\n");
                break;
            case typeFits:
                result.clone(rightvalue);
                *result.fvalue *= (double)leftvalue.lvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue *= (double)leftvalue.lvalue;
                break;
            case typeStrarr:
                dp_output("Cannot multiply a string array to an integer number\n");
                break;
            default:
                dp_output("Cannot multiply this type to an integer number\n");
                break;
        }
            break;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeDbl;
                result.dvalue = leftvalue.dvalue * rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeDbl;
                result.dvalue = leftvalue.dvalue * rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(rightvalue);
                *result.cvalue *= leftvalue.dvalue;
                break;
            case typeStr:
                dp_output("Cannot multiply a string to a real number\n");
                break;
            case typeFits:
                result.clone(rightvalue);
                *result.fvalue *= leftvalue.dvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue *= leftvalue.dvalue;
                break;
            case typeStrarr:
                dp_output("Cannot multiply a string array to a real number\n");
                break;
            default:
                dp_output("Cannot multiply this type to a real number\n");
                break;
        }
            break;
        case typeCom: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.cvalue *= rightvalue.lvalue;
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.cvalue *= rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(leftvalue);
                *result.cvalue *= *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot multiply a string to a complex number\n");
                break;
            case typeFits:
        result.clone(rightvalue);
        *result.fvalue *= *leftvalue.cvalue;
                break;
            case typeFitsFile:
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    result.fvalue->ReadFITS(rightvalue.ffvalue->c_str());
//                    *result.fvalue *= *leftvalue.cvalue;
                dp_output("Cannot multiply a FITS file to a complex number\n");
                break;
            case typeStrarr:
                dp_output("Cannot multiply a string array to a complex number\n");
                break;
            default:
                dp_output("Cannot multiply this type to a complex number\n");
                break;
        }
            break;
        case typeStr:
            dp_output("Cannot multiply anything to a string\n");
            break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.fvalue *= (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.fvalue *= rightvalue.dvalue;
                break;
            case typeCom:
        result.clone(leftvalue);
        *result.fvalue *= *rightvalue.cvalue;
        break;
            case typeStr:
                dp_output("Cannot multiply a string to a matrix\n");
                break;
            case typeFits:
                if (leftvalue.fvalue->matches(*rightvalue.fvalue)) {
                    if (isVariable(leftvalue.fvalue) == 0) {
                        result.clone(leftvalue);
                        *result.fvalue *= *rightvalue.fvalue;
                    } else if (isVariable(rightvalue.fvalue) == 0) {
                        result.clone(rightvalue);
                        *result.fvalue *= *leftvalue.fvalue;
                    } else {
                        result.clone(leftvalue);
                        *result.fvalue *= *rightvalue.fvalue;
                    }
                } else {
                    result.clone(leftvalue);
                    *result.fvalue *= *rightvalue.fvalue;
                }
                break;
            case typeFitsFile: {
                Fits a;
                result.type = typeFits;
                result.clone(leftvalue);
                if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue *= a;
            }
                break;
            case typeStrarr:
                dp_output("Cannot multiply a string array to a matrix\n");
                break;
            default:
                dp_output("Cannot multiply this type to a matrix\n");
                break;
        }
            break;
        case typeFitsFile: switch(rightvalue.type) {
            case typeCon:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue *= (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue *= rightvalue.dvalue;
                break;
            case typeCom:
                dp_output("Cannot multiply a complex number to a FITS file\n");
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
//                    else *result.fvalue *= *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot multiply a string to a FITS file\n");
                break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue *= *rightvalue.fvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    Fits a;
                    if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                    else *result.fvalue *= a;
                }
                break;
            case typeStrarr:
                dp_output("Cannot multiply a string array to a FITS file\n");
                break;
            default:
                dp_output("Cannot multiply this type to a FITS file\n");
                break;
        }
            break;
        case typeStrarr:
            dp_output("Cannot multiply anything to a string array\n");
            break;
        default:
            dp_output("Invalid arguments to operator '*'\n");
            break;
    }


    return result;
}

dpuserType divideNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
	dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeCon: switch(rightvalue.type) {
            case typeCon:
                result.type = typeDbl;
                result.dvalue = (double)leftvalue.lvalue / (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeDbl;
                result.dvalue = leftvalue.lvalue / rightvalue.dvalue;
                break;
            case typeCom:
                result.type = typeCom;
                result.cvalue = CreateComplex();
                *result.cvalue = leftvalue.lvalue / *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot divide an integer number by a string\n");
                break;
            case typeFits:
                result.clone(rightvalue);
                result.fvalue->invert();
                *result.fvalue *= (double)leftvalue.lvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    result.fvalue->invert();
                    *result.fvalue *= (double)leftvalue.lvalue;
                }
                break;
            case typeStrarr:
                dp_output("Cannot divide an integer number by a string array\n");
                break;
            default:
                dp_output("Cannot divide an integer number by this type\n");
                break;
        }
            break;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeDbl;
                result.dvalue = leftvalue.dvalue / rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeDbl;
                result.dvalue = leftvalue.dvalue / rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(rightvalue);
                *result.cvalue = leftvalue.dvalue / *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot divide a real number by a string\n");
                break;
            case typeFits:
                result.clone(rightvalue);
                result.fvalue->invert();
                *result.fvalue *= leftvalue.dvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    result.fvalue->invert();
                    *result.fvalue *= leftvalue.dvalue;
                }
                break;
            case typeStrarr:
                dp_output("Cannot divide a real number by a string array\n");
                break;
            default:
                dp_output("Cannot divide a real number by this type\n");
                break;
        }
            break;
        case typeCom: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.cvalue /= rightvalue.lvalue;
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.cvalue /= rightvalue.dvalue;
                break;
            case typeCom:
                result.clone(leftvalue);
                *result.cvalue /= *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot divide a complex number by a string\n");
                break;
            case typeFits:
        result.clone(rightvalue);
        result.fvalue->setType(C16);
        double rr, ii, div;
        for (dpint64 n = 0; n < result.fvalue->Nelements(); n++) {
            rr = result.fvalue->cdata[n].r;
            ii = result.fvalue->cdata[n].i;
            div = pow(rr, 2) + pow(ii,2);

            result.fvalue->cdata[n].r = (rr * leftvalue.cvalue->real() + ii * leftvalue.cvalue->imag()) / div;
            result.fvalue->cdata[n].i = (rr * leftvalue.cvalue->imag() - ii * leftvalue.cvalue->real()) / div;
        }
        break;
            case typeFitsFile:
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    result.fvalue->ReadFITS(rightvalue.ffvalue->c_str());
//                    *result.fvalue += -*leftvalue.cvalue;
                dp_output("Cannot divide a complex number by a FITS file\n");
                break;
            case typeStrarr:
                dp_output("Cannot divide a complex number by a string array\n");
                break;
            default:
                dp_output("Cannot divide a complex number by this type\n");
                break;
        }
            break;
        case typeStr:
            dp_output("Cannot divide a string by anything\n");
            break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                result.clone(leftvalue);
                *result.fvalue /= (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.clone(leftvalue);
                *result.fvalue /= rightvalue.dvalue;
                break;
            case typeCom:
        result.clone(leftvalue);
        *result.fvalue /= *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot divide a matrix by a string\n");
                break;
            case typeFits:
                if (leftvalue.fvalue->matches(*rightvalue.fvalue)) {
                    if (isVariable(leftvalue.fvalue) == 0) {
                        result.clone(leftvalue);
                        *result.fvalue /= *rightvalue.fvalue;
                    } else if (isVariable(rightvalue.fvalue) == 0) {
                        result.clone(rightvalue);
                        result.fvalue->invert();
                        *result.fvalue *= *leftvalue.fvalue;
                    } else {
                        result.clone(leftvalue);
                        *result.fvalue /= *rightvalue.fvalue;
                    }
                } else {
                    result.clone(leftvalue);
                                            *result.fvalue /= *rightvalue.fvalue;
                }
                break;
            case typeFitsFile: {
                Fits a;
                result.type = typeFits;
                result.clone(leftvalue);
                if (a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue /= a;
            }
                break;
            case typeStrarr:
                dp_output("Cannot divide a matrix by a string array\n");
                break;
            default:
                dp_output("Cannot divide a matrix by this type\n");
                break;
        }
            break;
        case typeFitsFile: switch(rightvalue.type) {
            case typeCon:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue /= (double)rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue /= rightvalue.dvalue;
                break;
            case typeCom:
                dp_output("Cannot divide a FITS file by a complex number\n");
//                    result.type = typeFits;
//                    result.fvalue = new Fits();
//                    if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
//                    else *result.fvalue /= *rightvalue.cvalue;
                break;
            case typeStr:
                dp_output("Cannot divide a FITS file by a string\n");
                break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else *result.fvalue /= *rightvalue.fvalue;
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    Fits a;
                    if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                    else *result.fvalue /= a;
                }
                break;
            case typeStrarr:
                dp_output("Cannot divide a FITS file by a string array\n");
                break;
            default:
                dp_output("Cannot divide a FITS file by this type\n");
                break;
        }
            break;
        case typeStrarr:
            dp_output("Cannot divide a string array by anything\n");
            break;
        default:
            dp_output("Invalid arguments to operator '/'\n");
            break;
    }


    return result;
}

dpuserType powerNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
	dpuserType rightvalue = right->evaluate();

    if (leftvalue.type == typeCon) leftvalue = leftvalue.toDouble();
    if (rightvalue.type == typeCon) rightvalue = rightvalue.toDouble();

    if (leftvalue.type == typeFitsFile) {
        leftvalue.type = typeFits;
        leftvalue.fvalue = CreateFits();
        if (!leftvalue.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) leftvalue.type = typeUnknown;
    }
    if (rightvalue.type == typeFitsFile) {
        rightvalue.type = typeFits;
        rightvalue.fvalue = CreateFits();
        if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) rightvalue.type = typeUnknown;
    }

    dpuserType result;

    switch (leftvalue.type) {
        case typeDbl: switch(rightvalue.type) {
            case typeDbl:
                result = pow(leftvalue.toDouble(), rightvalue.toDouble());
                if (!gsl_finite(result.dvalue)) {
                    result.type = typeCom;
                    result.cvalue = CreateComplex(complex_pow(dpComplex(leftvalue.dvalue), dpComplex(rightvalue.dvalue)));
                }
                break;
            case typeCom:
                result.type = typeCom;
                result.cvalue = CreateComplex(complex_pow(dpComplex(leftvalue.dvalue), *rightvalue.cvalue));
                break;
            case typeFits:
                result.clone(rightvalue);
                if (result.fvalue->getType() == C16) {
                    result.fvalue->setType(C16);
                    dpComplex tmp;
                    for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                        tmp = complex_pow(dpComplex(leftvalue.dvalue),
                                          dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i));
                        result.fvalue->cdata[i].r = tmp.real();
                        result.fvalue->cdata[i].i = tmp.imag();
                    }
                } else {
                    result.fvalue->setType(R8);
                    for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                        result.fvalue->r8data[i] = pow(leftvalue.dvalue, result.fvalue->r8data[i]);
                    }
                }
                break;
            default:
                dp_output("Invalid arguments to operator '^'\n");
                break;
        }
            break;
        case typeCom: switch(rightvalue.type) {
            case typeDbl:
                result.type = typeCom;
                result.cvalue = CreateComplex(complex_pow(*leftvalue.cvalue, dpComplex(rightvalue.dvalue)));
                break;
            case typeCom:
                result.type = typeCom;
                result.cvalue = CreateComplex(complex_pow(*leftvalue.cvalue, *rightvalue.cvalue));
                break;
            case typeFits: {
                result.clone(rightvalue);
                result.fvalue->setType(C16);
                dpComplex tmp;
                for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                    tmp = complex_pow(*leftvalue.cvalue,
                                      dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i));
                    result.fvalue->cdata[i].r = tmp.real();
                    result.fvalue->cdata[i].i = tmp.imag();
                }
            }
                break;
            default:
                dp_output("Invalid arguments to operator '^'\n");
                break;
        }
            break;
        case typeFits: switch(rightvalue.type) {
            case typeDbl:
                result.clone(leftvalue);
                *result.fvalue ^= rightvalue.dvalue;
                break;
            case typeCom: {
                result.clone(leftvalue);
                result.fvalue->setType(C16);

                dpComplex tmp;
                for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                        tmp = complex_pow(dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i),
                                          dpComplex(rightvalue.cvalue->real(), rightvalue.cvalue->imag()));
                    result.fvalue->cdata[i].r = tmp.real();
                    result.fvalue->cdata[i].i = tmp.imag();
                }
            }
                break;
            case typeFits:
                if (isVariable(leftvalue.fvalue) == 0) {
                    result.fvalue = leftvalue.fvalue;

                    if ((result.fvalue->getType() == C16) || (rightvalue.fvalue->getType() == C16)) {
                        result.fvalue->setType(C16);
                        dpComplex tmp;
                        for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                            if (rightvalue.fvalue->getType() == C16) {
                                tmp = complex_pow(dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i),
                                                  dpComplex(rightvalue.fvalue->cdata[i].r, rightvalue.fvalue->cdata[i].i));
                            } else {
                                tmp = complex_pow(dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i),
                                                  dpComplex(rightvalue.fvalue->ValueAt(i)));
                            }
                            result.fvalue->cdata[i].r = tmp.real();
                            result.fvalue->cdata[i].i = tmp.imag();
                        }
                    } else {
                        result.fvalue->setType(R8);
                        for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                            result.fvalue->r8data[i] = pow(result.fvalue->r8data[i], rightvalue.fvalue->ValueAt(i));
                        }
                    }
                } else if (isVariable(rightvalue.fvalue) == 0) {
                    result.fvalue = rightvalue.fvalue;
                    if ((result.fvalue->getType() == C16) || (leftvalue.fvalue->getType() == C16)) {
                        result.fvalue->setType(C16);
                        dpComplex tmp;
                        for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                            if (leftvalue.fvalue->getType() == C16) {
                                tmp = complex_pow(dpComplex(leftvalue.fvalue->cdata[i].r, leftvalue.fvalue->cdata[i].i),
                                                  dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i));
                            } else {
                                tmp = complex_pow(dpComplex(leftvalue.fvalue->ValueAt(i)),
                                                  dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i));
                            }
                            result.fvalue->cdata[i].r = tmp.real();
                            result.fvalue->cdata[i].i = tmp.imag();
                        }
                    } else {
                        result.fvalue->setType(R8);
                        for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                            result.fvalue->r8data[i] = pow(leftvalue.fvalue->ValueAt(i), result.fvalue->r8data[i]);
                        }
                    }
                } else {
                    result.fvalue = CreateFits();
                    result.fvalue->copy(*leftvalue.fvalue);
                    if ((result.fvalue->getType() == C16) || (rightvalue.fvalue->getType() == C16)) {
                        result.fvalue->setType(C16);
                        dpComplex tmp;
                        for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                            if (rightvalue.fvalue->getType() == C16) {
                                tmp = complex_pow(dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i),
                                                  dpComplex(rightvalue.fvalue->cdata[i].r, rightvalue.fvalue->cdata[i].i));
                            } else {
                                tmp = complex_pow(dpComplex(result.fvalue->cdata[i].r, result.fvalue->cdata[i].i),
                                                  dpComplex(rightvalue.fvalue->ValueAt(i)));
                            }
                            result.fvalue->cdata[i].r = tmp.real();
                            result.fvalue->cdata[i].i = tmp.imag();
                        }
                    } else {
                        result.fvalue->setType(R8);
                        for (unsigned long i = 0; i < result.fvalue->Nelements(); i++) {
                            result.fvalue->r8data[i] = pow(result.fvalue->ValueAt(i), rightvalue.fvalue->ValueAt(i));
                        }
                    }
                }
                break;
            default:
                dp_output("Invalid arguments to operator '^'\n");
                break;
        }
            break;
        default:
            dp_output("Invalid arguments to operator '^'\n");
            break;
    }
    return result;
}

dpuserType matrixmulNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeFits: switch(rightvalue.type) {
            case typeFits:
                result.clone(leftvalue);
                result.fvalue->matrix_mul(*rightvalue.fvalue);
                break;
            case typeFitsFile: {
                Fits a;

                if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    result.clone(leftvalue);
                    result.fvalue->matrix_mul(a);
                }
            }
                break;
            case typeCon:
                result.clone(leftvalue);
                result.fvalue->mul((double)(rightvalue.lvalue));
                break;
            case typeDbl:
                result.clone(leftvalue);
                result.fvalue->mul(rightvalue.dvalue);
                break;
            default:
                dp_output("Cannot do matrix multiplication with this type\n");
                break;
        }
            break;
        case typeFitsFile: switch(rightvalue.type) {
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else result.fvalue->matrix_mul(*rightvalue.fvalue);
                break;
            case typeCon:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else result.fvalue->mul((double)(rightvalue.lvalue));
                break;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else result.fvalue->mul(rightvalue.dvalue);
                break;
            case typeFitsFile:
                result.type = typeFits;
                result.fvalue = CreateFits();
                if (!result.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) result.type = typeUnknown;
                else {
                    Fits a;
                    if (!a.ReadFITS(rightvalue.ffvalue->c_str())) result.type = typeUnknown;
                    else result.fvalue->matrix_mul(a);
                }
                break;
            default:
                dp_output("Cannot do matrix multiplication with this type\n");
                break;
        }
            break;
        default:
            dp_output("Invalid arguments to operator '#'\n");
            break;
    }
    return result;
}

dpuserType moduloNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();

    return modulo(leftvalue, rightvalue);
}

dpuserType incrementNode::evaluate() {
#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif
    dpuser_vars[id]++;

#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return dpuser_vars[id];
}

dpuserType decrementNode::evaluate() {
#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif
    dpuser_vars[id]--;

#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return dpuser_vars[id];
}

dpuserType plusEqualsNode::evaluate() {
	dpuserType rightvalue = right->evaluate();
#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif
    dpuser_vars[id] += rightvalue;
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return dpuser_vars[id];
}

dpuserType minusEqualsNode::evaluate() {
	dpuserType rightvalue = right->evaluate();
#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif
    dpuser_vars[id] -= rightvalue;
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return dpuser_vars[id];
}

dpuserType multiplyEqualsNode::evaluate() {
	dpuserType rightvalue = right->evaluate();
#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif
    dpuser_vars[id] *= rightvalue;
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return dpuser_vars[id];
}

dpuserType divideEqualsNode::evaluate() {
	dpuserType rightvalue = right->evaluate();
#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif
    dpuser_vars[id] /= rightvalue;
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return dpuser_vars[id];
}

