#include <math.h>
#include "dpuserAST.h"
#include "math_utils.h"
#include "dpuser.procs.h"
#include "dpuser_utils.h"
#include "dpuser.yacchelper.h"
#include "dpuserType.h"
#include "fits.h"
#include <iostream>

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

std::map<std::string, dpuserType> dpuser_vars;
std::map<std::string, ASTNode *>userprocedures;
std::map<std::string, std::vector<std::string> >userprocedure_arguments;
std::map<std::string, ASTNode *>userfunctions;
std::map<std::string, std::vector<std::string> >userfunction_arguments;
std::map<std::string, bool>loopVariables;
std::map<std::string, dpuserType> new_dpuser_vars;

int indexBase = 1;

// some globals to control user defined functions and procedures
bool returnPassed = 0;
dpuserType returnedValue;
int nparams;

dpuserType resolveFunction(int id, std::vector<ASTNode *>args, std::vector<std::string>options);
void resolveProcedure(int id, std::vector<ASTNode *>args, std::vector<std::string>options);

int call_gdl(std::string func_or_pro, std::vector<dpuserType> *args, std::vector<std::string> variables, std::map<std::string, std::pair<std::string, dpuserType> > *keyw, bool isFunction, dpuserType &rv);
int call_python(std::string func_or_pro, std::vector<dpuserType> *args, std::vector<std::string> variables, std::map<std::string, std::pair<std::string, dpuserType> > *keyw, bool isFunction, dpuserType &rv);

int looplock;

dpuserType assignmentNode::evaluate() {
    dpuserType intermittent = exp->evaluate();
    if (intermittent.type == typeUnknown) throw dpuserTypeException("Cannot assign this to a variable\n");
    dpuserType result;
    result.clone(intermittent);
    if (result.type == typeFitsFile) {
        result.type = typeFits;
        result.fvalue = CreateFits();
        if (!result.fvalue->ReadFITS(result.ffvalue->c_str())) {
            result.type = typeUnknown;
            deleteFromListOfFits(result.fvalue);
        }
    }
    if (result.type == typeUnknown) throw dpuserTypeException("Cannot assign this to a variable\n");

/* check type of our global dpuser variables:
    - naxis1, naxis2, tmpmem, method are integer
    - plotdevice, title, xtitle, ytitle are strings
*/
    if (id == "tmpmem" || id == "naxis1" || id == "naxis2" || id == "method") {
        if (result.type == typeDbl) {
            result.type = typeCon;
            result.lvalue = (dpint64)result.dvalue;
        }
        if (result.type != typeCon) throw dpuserTypeException("variable must be an integer\n");
        if (id == "tmpmem" || id == "naxis1" || id == "naxis2") {
            if (result.toInt() < 1) throw dpuserTypeException("variable must be >= 1\n");
        }
    }
    if (id == "plotdevice" || id == "title" || id == "xtitle" || id == "ytitle") {
        if (result.type != typeStr) throw dpuserTypeException("variable must be a string\n");
    }
    result.variable = true;
#ifdef DPQT
// Lock QFitsView buffer
    QWriteLocker locker(&buffersLock);
    if (result.type == typeFits) {
        if (dpuser_vars[id].type == typeFits) {
            resetGUIsettings =  (result.fvalue->matches(*(dpuser_vars[id].fvalue)));
        } else {
            resetGUIsettings = false;
        }
    }
#endif
    dpuser_vars[id] = result;
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return result;
}


variableNode::variableNode(std::string value) : id(value) {
}

dpuserType variableNode::evaluate() {
    if (dpuser_vars.count(id)) return dpuser_vars[id];
    else {
        dp_output("%s: ", id.c_str());
        throw dpuserTypeException("No such variable\n");
    }
}

dpuserType writefitsNode::evaluate() {
    dpuserType rv = arg->evaluate();
    if (rv.type == typeFitsFile) {
        rv.type = typeFits;
        rv.fvalue = CreateFits();
        rv.fvalue->ReadFITS(rv.ffvalue->c_str());
    }
    if (rv.type != typeFits) {
        yyerror("Don't know how to write this to a fits file");
        return rv;
    }
    rv.fvalue->WriteFITS(fname.c_str());
    return rv;
}

std::map<std::string, std::string> idlFunctionNames = {
    { "fltarr", "floatarray" },
    { "intarr", "longarray" },
    { "dblarr", "doublearray" },
    { "n_params", "nparams" },
    { "keyword_set", "isvariable" },
    { "n_elements", "nelements" }
};

functionNode::functionNode(std::string func) : id(func) {
/* find built-in function */
    builtin_id = 0;
    if (idlFunctionNames.count(func)) id = idlFunctionNames[func];
    for (auto function:funcs) if (!(function.name == id)) builtin_id++; else break;
    if (builtin_id >= funcs.size()) builtin_id = -1;
}

dpuserType functionNode::evaluate() {
/* is this a built-in function */
    if (builtin_id != -1) {
        return resolveFunction(builtin_id, args, options);
    }

/* see if this is a user defined function */
    if (userfunctions.count(id) != 0) {
        dpuserType rv;
        int old_nparams = nparams;
        int old_indexBase = indexBase;
        std::map<std::string, dpuserType> old_new_dpuser_vars;
        std::map<std::string, dpuserType> old_dpuser_vars;
        bool needswap = new_dpuser_vars.size() > 0;
        try {
            nparams = args.size();
            if (needswap) {
                for (auto var:new_dpuser_vars) {
                    if (var.second.type == typeCom) protect(var.second.cvalue);
                    else if (var.second.type == typeStr) protect(var.second.svalue);
                    else if (var.second.type == typeStrarr) protect(var.second.arrvalue);
                    else if (var.second.type == typeFits) protect(var.second.fvalue);
                    else if (var.second.type == typeDpArr) protect(var.second.dparrvalue);
                }
                old_new_dpuser_vars.swap(new_dpuser_vars);
            }
            for (int i = 1; i < userfunction_arguments[id].size(); i++) {
                if (i <= nparams) new_dpuser_vars[userfunction_arguments[id][i]] = args.at(i-1)->evaluate();
            }
            if ((nparams > 0) && (userfunction_arguments[id].size() <= nparams))
                dp_output("WARNING: Function %s called with too many arguments (%i expected, %i provided).", id.c_str(), userfunction_arguments[id].size()-1, nparams);
/* backup copy variables */
            looplock++;
            for (auto var:dpuser_vars) {
                if (var.second.type == typeCom) protect(var.second.cvalue);
                else if (var.second.type == typeStr) protect(var.second.svalue);
                else if (var.second.type == typeStrarr) protect(var.second.arrvalue);
                else if (var.second.type == typeFits) protect(var.second.fvalue);
                else if (var.second.type == typeDpArr) protect(var.second.dparrvalue);
            }
            dpuser_vars.swap(old_dpuser_vars);
            dpuser_vars.clear();
            dpuser_vars.swap(new_dpuser_vars);
            createGlobalVariables();
            userfunctions[id]->evaluate();
            rv = dpuser_vars[id];
            rv.variable = false;
        } catch (dpuserTypeException e) {
            dp_output(e.reason());
        }
        nparams = old_nparams;
        indexBase = old_indexBase;
        if (needswap) {
            new_dpuser_vars.clear();
            new_dpuser_vars.swap(old_new_dpuser_vars);
        }
        dpuser_vars.clear();
        dpuser_vars.swap(old_dpuser_vars);
        for (auto var:dpuser_vars) {
            if (var.second.type == typeCom) unprotect(var.second.cvalue);
            else if (var.second.type == typeStr) unprotect(var.second.svalue);
            else if (var.second.type == typeStrarr) unprotect(var.second.arrvalue);
            else if (var.second.type == typeFits) unprotect(var.second.fvalue);
            else if (var.second.type == typeDpArr) unprotect(var.second.dparrvalue);
        }

        looplock--;
        return rv;
    } else {
// see if we can call this via GDL or PYTHON
        std::vector<dpuserType> arguments;
        std::map<std::string, std::pair<std::string, dpuserType> > keywords;
        std::vector<std::string> passed_variables;
        for (int i = 0; i < args.size(); i++) {
            assignmentNode *assign = dynamic_cast<assignmentNode *>(args.at(i));
            if (assign != NULL) {
                variableNode *var = dynamic_cast<variableNode *>(assign->exp);
                if (var != NULL) {
                    keywords[assign->id] = std::pair<std::string, dpuserType>(var->id, assign->evaluate());
                } else {
                    keywords[assign->id] = std::pair<std::string, dpuserType>("", assign->evaluate());
                }
            } else {
                arguments.push_back(args.at(i)->evaluate());
            }
        }
        for (auto option:options) {
            dp_output("%s\n", option.c_str());
            dpuserType dummy = 1;
            keywords[option] = std::pair<std::string, dpuserType>("", dummy);
        }
        dpuserType rv;
        int gdl_success = call_gdl(id, &arguments, passed_variables, &keywords, true, rv);
        if (gdl_success == 1) {
            if (rv.type != typeUnknown) {
                // copy back keywords
                for (auto keyw:keywords) {
                    dp_debug("passed_keywords is %s\n", keyw.first.c_str());
                    if (keyw.second.first != "" && keyw.second.second.type != typeUnknown) {
                        dp_debug("dpuser receives variable %s\n", keyw.second.first.c_str());
#ifdef DPQT
                        QWriteLocker locker(&buffersLock);
#endif
                        dpuser_vars[keyw.second.first] = keyw.second.second;
#ifdef DPQT
                        locker.unlock();
                        dpUpdateVar(keyw.second.first);
#endif
                    }
                }
            }
            return rv;
        } else {
            int python_success = call_python(id, &arguments, passed_variables, &keywords, true, rv);
            if (python_success == 1) {
                if (rv.type != typeUnknown) {
                    return rv;
                }
            }
        }

        dp_output("%s: no such function\n", id.c_str());
        return 0;
    }
}

//procedureNode::procedureNode(ASTNode *fromFunction) {
//	functionNode *originalFunction = dynamic_cast<functionNode *>(fromFunction);
//	if (originalFunction != NULL) {
//		id = originalFunction->id;
//		args = originalFunction->args;
//		originalFunction->args.clear();
		
//		delete fromFunction;
//	}
//}

procedureNode::procedureNode(std::string proc) : id(proc) {
/* find built-in procedure */
    builtin_id = 0;
    for (auto proc:procs) if (!(proc.name == id)) builtin_id++; else break;
    if (builtin_id >= procs.size()) builtin_id = -1;
}

dpuserType procedureNode::evaluate() {
/* is this a built-in procedure */
    if (builtin_id != -1) {
        resolveProcedure(builtin_id, args, options);
        return 0.0;
    }

/* see if this is a user defined procedure */
	if (userprocedures.count(id) != 0) {
        int old_nparams = nparams;
        int old_indexBase = indexBase;
        std::map<std::string, dpuserType> new_dpuser_vars;
        std::vector<std::string> passed_variables;
        nparams = args.size();
        int argcount = 0;
        if (userprocedure_arguments[id].size() > 1) {
            for (int i = 1; i <= args.size(); i++) {
                assignmentNode *assign = dynamic_cast<assignmentNode *>(args.at(i-1));
                if (assign != NULL) {
                    new_dpuser_vars[assign->id] = assign->evaluate();
                    nparams--;
                } else {
                    variableNode *var = dynamic_cast<variableNode *>(args.at(i-1));
                    if (var != NULL) {
                        passed_variables.push_back(var->id);
                    } else {
                        passed_variables.push_back("");
                    }
                    if (i < userprocedure_arguments[id].size()) new_dpuser_vars[userprocedure_arguments[id][i]] = args.at(i-1)->evaluate();
                }
            }
        }
        if ((nparams > 0) && (userprocedure_arguments[id].size() <= nparams))
            dp_output("WARNING: Procedure %s called with too many arguments (%i expected, %i provided).", id.c_str(), userprocedure_arguments[id].size()-1, nparams);
        for (int i = 0; i < options.size(); i++) {
            dpuserType opt = 1;
            new_dpuser_vars[options.at(i)] = opt;
        }
/* backup copy variables */
        looplock++;
        for (auto var:dpuser_vars) {
            if (var.second.type == typeCom) protect(var.second.cvalue);
            else if (var.second.type == typeStr) protect(var.second.svalue);
            else if (var.second.type == typeStrarr) protect(var.second.arrvalue);
            else if (var.second.type == typeFits) protect(var.second.fvalue);
            else if (var.second.type == typeDpArr) protect(var.second.dparrvalue);
        }
        std::map<std::string, dpuserType> old_dpuser_vars;
		dpuser_vars.swap(old_dpuser_vars);
		dpuser_vars.swap(new_dpuser_vars);
        returnPassed = false;
        createGlobalVariables();
		userprocedures[id]->evaluate();
        if (userprocedure_arguments[id].size() > 1) {
            for (int i = 1; i < userprocedure_arguments[id].size(); i++) {
                if (i - 1 < nparams) {
                    if (passed_variables.at(i-1) != "") {
                        old_dpuser_vars[passed_variables.at(i-1)] = dpuser_vars[userprocedure_arguments[id][i]];
                    }
                }
            }
        }
        nparams = old_nparams;
        indexBase = old_indexBase;
		dpuser_vars.clear();
		dpuser_vars.swap(old_dpuser_vars);
        for (auto var:dpuser_vars) {
            if (var.second.type == typeCom) unprotect(var.second.cvalue);
            else if (var.second.type == typeStr) unprotect(var.second.svalue);
            else if (var.second.type == typeStrarr) unprotect(var.second.arrvalue);
            else if (var.second.type == typeFits) unprotect(var.second.fvalue);
            else if (var.second.type == typeDpArr) unprotect(var.second.dparrvalue);
        }
        looplock--;
    } else {
        std::vector<dpuserType> arguments;
        std::vector<std::string> passed_variables;
        std::map<std::string, std::pair<std::string, dpuserType> > keywords;
        for (int i = 0; i < args.size(); i++) {
            assignmentNode *assign = dynamic_cast<assignmentNode *>(args.at(i));
            if (assign != NULL) {
                variableNode *var = dynamic_cast<variableNode *>(assign->exp);
                if (var != NULL) {
                    keywords[assign->id] = std::pair<std::string, dpuserType>(var->id, assign->evaluate());
                } else {
                    keywords[assign->id] = std::pair<std::string, dpuserType>("", assign->evaluate());
                }
            } else {
                variableNode *var = dynamic_cast<variableNode *>(args.at(i));
                if (var != NULL) {
                    passed_variables.push_back(var->id);
                } else {
                    passed_variables.push_back("");
                }
                arguments.push_back(args.at(i)->evaluate());
            }
        }
        for (auto option:options) {
            dpuserType dummy = 1;
            keywords[option] = std::pair<std::string, dpuserType>("", dummy);
        }
        dpuserType rv;
        int gdl_success = call_gdl(id, &arguments, passed_variables, &keywords, false, rv);
        if (gdl_success == 1) {
            if (rv.type != typeUnknown) {
//                std::cout << "calling GDL pro successful!" << std::endl;
                // copy back variables
                for (int i = 0; i < arguments.size(); i++) {
                    dp_debug("passed_variables(%i) is %s\n", i, passed_variables.at(i).c_str());
                    if (passed_variables.at(i) != "" && arguments.at(i).type != typeUnknown) {
                        dp_debug("dpuser receives variable %s\n", passed_variables.at(i).c_str());
#ifdef DPQT
                        QWriteLocker locker(&buffersLock);
#endif
                        dpuser_vars[passed_variables.at(i)] = arguments[i];
#ifdef DPQT
                        locker.unlock();
                      dpUpdateVar(passed_variables.at(i));
#endif
                    }
                }
                // copy back keywords
                for (auto keyw:keywords) {
                    dp_debug("passed_keywords is %s\n", keyw.first.c_str());
                    if (keyw.second.first != "" && keyw.second.second.type != typeUnknown) {
                        dp_debug("dpuser receives variable %s\n", keyw.second.first.c_str());
#ifdef DPQT
                        QWriteLocker locker(&buffersLock);
#endif
                        dpuser_vars[keyw.second.first] = keyw.second.second;
#ifdef DPQT
                        locker.unlock();
                        dpUpdateVar(keyw.second.first);
#endif
                    }
                }
            }
            return rv;
        }
        dp_output("%s: no such procedure\n", id.c_str());
        return 0;
	}
}

dpuserType helpNode::evaluate() {
	help((char *)id.c_str());

	return 0.0;
}

statementNode::statementNode(std::string proc, ASTNode *arg1) : stmt(arg1) {
	id = 1;
//    std::string f = func.substr(0, func.size() - 1);
//		std::cout << f << std::endl;
//    if (dpuser_functions1.count(f) != 1) {
//        asterror(("function " + func + " does not take 1 parameter").c_str());
//    } else {
//        id = dpuser_functions1.at(f);
//    }
}

dpuserType statementNode::evaluate() {
	dpuserType value = 0.0;
	if (id == 0) {
		if (stmt) value = stmt->evaluate();
/*		std::cout << value << "id == 0" << std::endl; */
	} else {
		if (stmt) value = stmt->evaluate();
/*		std::cout << value << std::endl; */
	}
	return value;
}

dpuserType listNode::evaluate() {
    for (int i = 0; i < list.size(); i++) {
        if (ScriptInterrupt || returnPassed) {
            return 0;
        } else {
            list.at(i)->evaluate();
        }
    }
    return 0;
}

dpuserType forloopNode::evaluate() {
    looplock++;
	if (change == NULL) {
		assignmentNode *var = dynamic_cast<assignmentNode *>(start);
		if (var != NULL) {
			start->evaluate();
			dpuserType eee = limit->evaluate();
			while (dpuser_vars[var->id] <= eee) {
				commands->evaluate();
				dpuser_vars[var->id]++;
                freePointerlist();
                if (ScriptInterrupt) {
                    looplock = 0;
                    break;
                }
            }
		}
	} else {
		start->evaluate();
		check->evaluate();
		while (check->evaluate() == 1.0) {
			commands->evaluate();
			change->evaluate();
            freePointerlist();
            if (ScriptInterrupt) {
                looplock = 0;
                break;
            }
        }
	}
    looplock--;
    if (looplock < 0) {
        looplock = 0;
    }
#ifdef DPQT
    if (looplock == 0) {
        for (auto var:loopVariables) dpUpdateVar(var.first);
        loopVariables.clear();
        dpUpdateVar(fitsMainWindow->getCurrentBufferIndex());
    }
#endif
}

dpuserType whileNode::evaluate() {
    looplock++;
    while (check->evaluate() == 1.0) {
        commands->evaluate();
        freePointerlist();
        if (ScriptInterrupt) {
            looplock = 0;
            break;
        }
    }
    looplock--;
    if (looplock < 0) {
        looplock = 0;
    }
#ifdef DPQT
    if (looplock == 0) {
        for (auto var:loopVariables) dpUpdateVar(var.first);
        loopVariables.clear();
        dpUpdateVar(fitsMainWindow->getCurrentBufferIndex());
    }
#endif
}

dpuserType ifNode::evaluate() {
	if (expression->evaluate() != 0) {
        return commands->evaluate();
	} else {
        if (elsecommands != NULL) return elsecommands->evaluate();
	}
}

dpuserType rangeNode::evaluate() {
    dpuserType rv;

    return rv;
}

dpuserType createrangeNode::evaluate() {
    int i;
    long dim, j, inc;
    dpuserType l, rv;
    int success = 1;

    rangeNode *newrange = dynamic_cast<rangeNode *>(range);

    if (newrange == NULL) {
        dp_output("CreateRange called with wrong arguments.\n");
        return rv;
    }

    if (newrange->list.at(0) != NULL) {
        l = newrange->list.at(0)->evaluate();
        if ((l.type == typeStr) || (l.type == typeStrarr)) {
            rv.type = typeStrarr;
            rv.arrvalue = CreateStringArray();
            for (i = 0; i < newrange->list.size(); i++) {
                if (newrange->list.at(i) != NULL) {
                    l = newrange->list.at(i)->evaluate();
                    if (l.type == typeStr) {
                        rv.arrvalue->append(*l.svalue);
                    } else if (l.type == typeStrarr) {
                        for (dpint64 n = 0; n < l.arrvalue->count(); n++) rv.arrvalue->append((*l.arrvalue)[n]);
                    } else {
                        dp_output("Wrong argument to insert into a string array\n");
                    }
                }
            }
        } else if (l.type == typeFits) {
            if (newrange->list.size() == 1) {
                rv.clone(l);
            } else {
                dim = l.fvalue->Naxis(1);
                rv.type = typeFits;
                rv.fvalue = CreateFits();
                rv.fvalue->create(dim, newrange->list.size());
                rv.fvalue->setRange(*l.fvalue, 1, dim, 1, 1, 1, 1);
                for (i = 1; i < newrange->list.size(); i++) {
                    if (newrange->list.at(i) != NULL) {
                        l = newrange->list.at(i)->evaluate();
                        if (l.type == typeFits) {
                            if (l.fvalue->Naxis(1) > dim) {
                                dim = l.fvalue->Naxis(1);
                                rv.fvalue->resize(dim, newrange->list.size());
                            }
                            rv.fvalue->setRange(*l.fvalue, 1, l.fvalue->Naxis(1), i+1, i+1, 1, 1);
                        } else dp_output("WARNING - matrix element %i is not a vector\n", i+1);
                    }
                }
            }
        } else {
            rv.type = typeFits;
            rv.fvalue = CreateFits();

            if (!rv.fvalue->create(newrange->list.size(), 1, R8)) {
                success = 0;
                return rv;
            }
            dim = 0;
            for (i = 0; i < newrange->list.size(); i++) {
                if (newrange->list.at(i) != NULL) {
                    if (i > 0) l = newrange->list.at(i)->evaluate();
                    if (l.type == typeCon) {
                        l.type = typeDbl;
                        l.dvalue = (double)l.lvalue;
                    }
                    if (l.type == typeDbl) {
                        rv.fvalue->r8data[dim] = l.dvalue;
                        if (newrange->type[i] == ':') {
                            inc = (long)fabs(l.dvalue - rv.fvalue->ValueAt(dim - 1)) - 1;
                            rv.fvalue->resize(rv.fvalue->Nelements() + inc, 1);
                            if (l.dvalue > rv.fvalue->ValueAt(dim - 1)) {
                                for (j = dim; j < dim + inc + 1; j++) {
                                    rv.fvalue->r8data[j] = rv.fvalue->r8data[j - 1] + 1.0;
                                }
                            } else {
                                for (j = dim; j < dim + inc + 1; j++) {
                                    rv.fvalue->r8data[j] = rv.fvalue->r8data[j - 1] - 1.0;
                                }
                            }
                            dim += inc + 1;
                        } else {
                            dim++;
                        }
                    } else if (l.type == typeFits && l.fvalue->Naxis(0) == 1) {
                        inc = (long)l.fvalue->Naxis(1);
                        rv.fvalue->resize(rv.fvalue->Nelements() + inc, 1);
                        for (int n = 0, j = dim; j < dim + inc + 1; j++, n++) {
                            rv.fvalue->r8data[j] = l.fvalue->ValueAt(n);
                        }
                        dim += inc;
                    } else {
                        dp_output("Wrong argument type to set an array");
                        success = 0;
                        break;
                    }
                }
            }
            if (success) {
                rv.fvalue->resize(dim, 1, 1);
            }
        }
    }

    return rv;
}

dpuserType extractrangeNode::evaluate() {
    dpuserType rv;

    rangeNode *newrange = dynamic_cast<rangeNode *>(range);

    if (newrange == NULL) {
        dp_output("CreateRange called with wrong arguments.\n");
        return rv;
    }

    int i;
    dpuserType arg = argument->evaluate();

//    dpuserType eArgs[10];

// One argument
    if (newrange->list.size() == 1) {

// ...which is a '*'. We just return a copy of the argument
        if (newrange->list.at(0) == NULL) {
            if (arg.type == typeDpArr) {
                yyerror("Wrong number of arguments to access member of FITS list");
            } else if (arg.type == typeFitsFile) {
                rv.type = typeFits;
                rv.fvalue = CreateFits();
                rv.fvalue->ReadFITS(arg.ffvalue->c_str());
            } else {
                rv.clone(arg);
            }
            return rv;
        } else {
            dpuserType range = newrange->list.at(0)->evaluate();

// ...which is a number. We return the single element
            if (range.type == typeDbl || range.type == typeCon) {
                long index = range.toInt() - indexBase;
                switch (arg.type) {
                    case typeCon:
                    case typeDbl:
                    case typeCom:
                        if (index == 0) rv.copy(arg);
                        else yyerror("Index out of range");
                    break;
                    case typeStr:
                        if (index < 0 || index >= arg.svalue->length()) {
                            yyerror("Index out of range");
                        } else {
                            rv.type = typeStr;
                            rv.svalue = CreateString(arg.svalue->mid(index, 1));
                        }
                    break;
                    case typeStrarr:
                        if (index < 0 || index >= arg.arrvalue->size()) {
                            yyerror("Index out of range");
                        } else {
                            rv.type = typeStr;
                            rv.svalue = CreateString(arg.arrvalue->at(index));
                        }
                    break;
                    case typeFits:
                        if ((index >= 0) && (index < (long)arg.fvalue->Nelements())) {
                            if (arg.fvalue->membits == C16) {
                                if (fabs(arg.fvalue->cdata[index].i) < dpTOLERANCE) {
                                    rv.type = typeDbl;
                                    rv.dvalue = arg.fvalue->cdata[index].r;
                                } else {
                                    rv.type = typeCom;
                                    rv.cvalue = CreateComplex(arg.fvalue->cdata[index].r, arg.fvalue->cdata[index].i);
                                }
                            } else {
                                rv.type = typeDbl;
                                rv.dvalue = arg.fvalue->ValueAt(index);
                            }
                        } else {
                            yyerror("Index out of range");
                        }
                    break;
                    case typeDpArr:
                        index += indexBase;
                        if ((index >= 0) && (index < arg.dparrvalue->size())) {
                            dpuserType dpinp;

                            dpinp = *(dpuserType *)(arg.dparrvalue->at(index));
                            rv.deep_copy(dpinp);

                            switch (rv.type) {
                                case typeFits:
                                    addToListOfFits(rv.fvalue);
                                    break;
                                case typeStrarr:
                                    addToListOfdpStringArrays(rv.arrvalue);
                                    break;
                                default:
                                    break;
                            }
                        } else {
                            yyerror("Index out of range");
                        }
                    break;
                    default:
                        yyerror("Wrong argument for selecting range");
                    break;
                }
            } else if (range.type == typeFits) {

// ...which is a vector. We return the range
                switch(arg.type) {
                    case typeStr:
                        rv.type = typeStr;
                        rv.svalue = CreateString(*arg.svalue);
                        reindexString(*rv.svalue, *range.fvalue);
                    break;
                    case typeStrarr:
                        rv.type = typeStrarr;
                        rv.arrvalue = CreateStringArray(*arg.arrvalue);
                        reindexStringArray(*rv.arrvalue, *range.fvalue);
                    break;
                    case typeFits:
                        rv.type = typeFits;
                        rv.fvalue = CreateFits();
                        arg.fvalue->extractRange(*rv.fvalue, *range.fvalue);
                    break;
                    default:
                    break;
                }
            }
        }

// Two arguments, which are separated by a ':'. We return the inclusive range
    } else if ((newrange->list.size() == 2) && (newrange->type[1] == ':')) {
        dpuserType range1 = newrange->list.at(0)->evaluate();
        dpuserType range2 = newrange->list.at(1)->evaluate();
        if ((range1.type == typeDbl || range1.type == typeCon) && (range2.type == typeDbl || range2.type == typeCon)) {
            long index1 = range1.toInt() - indexBase;
            long index2 = range2.toInt() - indexBase;
            switch (arg.type) {
                case typeStr:
                    if ((index1 < 0) || (index1 > index2) || (index2 > arg.svalue->length())) {
                        yyerror("Value out of range");
                    } else {
                        rv.type = typeStr;
                        dpString tmpstr(*arg.svalue);
                        rv.svalue = CreateString();
                        *rv.svalue = tmpstr.mid(index1, index2-index1+1);
                    }
                break;
                case typeStrarr:
                    if ((index1 < 0) || (index1 > index2) || (index1 > (long)arg.arrvalue->count()) || index2 > (long)arg.arrvalue->count()) {
                        yyerror("Value out of range");
                    } else {
                        if (index1 == index2) {
                            rv.type = typeStr;
                            rv.svalue = CreateString(arg.arrvalue->at(index1));
                        } else {
                            rv.type = typeStrarr;
                            rv.arrvalue = CreateStringArray();
                            for (i = index1; i <= index2; i++) {
                                rv.arrvalue->append((*arg.arrvalue)[i]);
                            }
                        }
                    }
                break;
                case typeFits:
                    if (arg.fvalue->Naxis(0) == 1) {
                        rv.type = typeFits;
                        rv.fvalue = CreateFits();
                        if (arg.fvalue->extractRange(*rv.fvalue, index1+1, index2+1, 1, 1, 1, 1) == FALSE) {
                            rv.type = typeUnknown;
                            DeleteFits(rv.fvalue);
                            rv.fvalue = NULL;
                        }
                    } else {
                        yyerror("Wrong number of axes");
                    }
                break;
                default:
                    yyerror("wrong arg type for accessing range");
                break;
            }
        }
    } else {

// the rest only applies to FITS or FITS files, and the arguments are either '*' or numbers
        long indices[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        long index = 0;
        bool hasStar = false;
        for (int i = 0; i < newrange->list.size(); i++) {
            if (newrange->list.at(i) == NULL) {                    // '*'
                indices[index] = indices[index + 1] = -1;
                index += 2;
                hasStar = true;
            } else if (newrange->type[i + 1] == ':') {             // an inclusive range
                dpuserType range1 = newrange->list.at(i)->evaluate();
                i++;
                dpuserType range2 = newrange->list.at(i)->evaluate();
                if ((range1.type == typeDbl || range1.type == typeCon) && (range2.type == typeDbl || range2.type == typeCon)) {
                    indices[index] = range1.toInt() - indexBase;
                    index++;
                    indices[index] = range2.toInt() - indexBase;
                    index++;
                } else {
                    yyerror("wrong arg type for accessing range");
                    return rv;
                }
            } else {                                               // a single number
                dpuserType range1 = newrange->list.at(i)->evaluate();
                if (range1.type == typeDbl || range1.type == typeCon) {
                    indices[index] = range1.toInt() - indexBase;
                    index++;
                    indices[index] = range1.toInt() - indexBase;
                    index++;
                }
            }
        }
        switch (arg.type) {
            case typeFitsFile:
                rv.type = typeFits;
                rv.fvalue = CreateFits();
                for (int i = 0; i < 6; i++) indices[i]++;
                rv.fvalue->ReadFITS(arg.ffvalue->c_str(), indices[0], indices[1], indices[2], indices[3], indices[4], indices[5]);
            break;
            case typeFits:
                if (arg.fvalue->Naxis(0) == index / 2) {
                    dpint64 cindex = -1;
                    if (!hasStar && indices[0] == indices[1] && indices[2] == indices[3] && indices[4] == indices[5]) {
                        switch (arg.fvalue->Naxis(0)) {
                            case 1:
                                if (indices[0] >= 0 && indices[0] < arg.fvalue->Nelements())
                                    cindex = arg.fvalue->C_I(indices[0]);
                            break;
                            case 2:
                                if (indices[0] >= 0 && indices[0] < arg.fvalue->Naxis(1)
                                    && indices[2] >= 0 && indices[2] < arg.fvalue->Naxis(2))
                                    cindex = arg.fvalue->C_I(indices[0], indices[2]);
                            break;
                            case 3:
                            if (indices[0] >= 0 && indices[0] < arg.fvalue->Naxis(1)
                                && indices[2] >= 0 && indices[2] < arg.fvalue->Naxis(2)
                                && indices[4] >= 0 && indices[4] < arg.fvalue->Naxis(3))
                                    cindex = arg.fvalue->C_I(indices[0], indices[2], indices[4]);
                            break;
                            default:
                            break;
                        }
                        if (cindex >= 0) {
                            if (arg.fvalue->membits == C16) {
                                if (fabs(arg.fvalue->cdata[cindex].i) < dpTOLERANCE) {
                                    rv.type = typeDbl;
                                    rv.dvalue = arg.fvalue->cdata[cindex].r;
                                } else {
                                    rv.type = typeCom;
                                    rv.cvalue = CreateComplex(arg.fvalue->cdata[cindex].r, arg.fvalue->cdata[cindex].i);
                                }
                            } else {
                                rv.type = typeDbl;
                                rv.dvalue = arg.fvalue->ValueAt(cindex);
                            }
                        }
                    } else {
                        rv.type = typeFits;
                        rv.fvalue = CreateFits();
                        for (int i = 0; i < 6; i++) if (indices[i] != -1) indices[i]++;
                        if (arg.fvalue->extractRange(*rv.fvalue, indices[0], indices[1], indices[2], indices[3], indices[4], indices[5]) == FALSE) {
                            rv.type = typeUnknown;
                            DeleteFits(rv.fvalue);
                            rv.fvalue = NULL;
                        }
                    }
                } else {
                    yyerror("Wrong number of axes");
                    return rv;
                }
            break;
            default:
            break;
        }
    }
    return rv;
}

long operatorRangeEqualsNode::extractIndices() {
    nodeEnum varType;

/* check argument types */
    varType = dpuser_vars[id].type;
    if ((dpuser_vars[id].type != typeFits)
     && (dpuser_vars[id].type != typeStrarr)
         && (dpuser_vars[id].type != typeDpArr)
         && (dpuser_vars[id].type != typeStr)
     && (dpuser_vars[id].type != typeCon)
     && (dpuser_vars[id].type != typeDbl)
     && (dpuser_vars[id].type != typeCom)) {
        yyerror("Wrong argument to access subset");
        return 0;
    }

    rangeNode *newrange = dynamic_cast<rangeNode *>(range);
    if (newrange == NULL) {
        yyerror("Wrong argument to access subset");
        return 0;
    }

    long index = 0;
    bool hasStar = false;
    for (int i = 0; i < newrange->list.size(); i++) {
        if (newrange->list.at(i) == NULL) {                    // '*'
            indices[index] = 0;
            index++;
            switch (varType) {
                case typeFits:
                    if (newrange->list.size() == 1) indices[index] = dpuser_vars[id].fvalue->Nelements() - indexBase;
                    else indices[index] = dpuser_vars[id].fvalue->Naxis((index+1)/2) - indexBase;
                break;
                    case typeStr:
                    indices[index] = dpuser_vars[id].svalue->length() - 1;
                break;
                    case typeStrarr:
                    indices[index] = dpuser_vars[id].arrvalue->size() - 1;
                break;
                    default:
                    yyerror("wrong arg type for accessing range");
                    return 0;
                break;
            }
            index++;
            hasStar = true;
        } else if (newrange->type[i + 1] == ':') {             // an inclusive range
            dpuserType range1 = newrange->list.at(i)->evaluate();
            i++;
            dpuserType range2 = newrange->list.at(i)->evaluate();
            if ((range1.type == typeDbl || range1.type == typeCon) && (range2.type == typeDbl || range2.type == typeCon)) {
                indices[index] = range1.toInt() - indexBase;
                index++;
                indices[index] = range2.toInt() - indexBase;
                index++;
            } else {
                yyerror("wrong arg type for accessing range");
                return 0;
            }
        } else {                                               // a single number
            dpuserType range1 = newrange->list.at(i)->evaluate();
            if (range1.type == typeDbl || range1.type == typeCon) {
                indices[index] = range1.toInt() - indexBase;
                index++;
                indices[index] = range1.toInt() - indexBase;
                index++;
            } else if (range1.type == typeFits) {
                frange = range1;
                index++;
            }
        }
    }

    return index;
}

dpuserType operatorRangeEqualsNode::evaluate() {
    long nops;
    long i;
    dpuserType rv;
    dpuserType var;
    dpuserType range;

/* check argument types */
    nops = extractIndices();
    if (nops == 0) {
        return rv;
    }

    var = right->evaluate();

#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif

    if (dpuser_vars[id].type == typeCon || dpuser_vars[id].type == typeDbl || dpuser_vars[id].type == typeCom) {
        if (!(var.type == typeCon || var.type == typeDbl || var.type == typeCom || var.type == typeFits)) {
            dp_output("Argument is not a number\n");
            return rv;
        }
        bool singleElement = TRUE;
        if (nops == 1) {
            if (frange.fvalue->Nelements() != 1) singleElement = FALSE;
            if (frange.fvalue->ValueAt(0) != indexBase) singleElement = FALSE;
        } else {
            for (i = 0; i < nops; i++) {
                dp_debug("setRange: Arg %i is %i\n", indices[i]);
                if (indices[i] != 1) singleElement = FALSE;
            }
        }
        if (!singleElement) {
            dp_output("Variable needs to be an array in this context\n");
            return rv;
        }
        dpuser_vars[id].type = var.type;
        if (var.type == typeCon) dpuser_vars[id].lvalue = var.lvalue;
        if (var.type == typeDbl) dpuser_vars[id].dvalue = var.dvalue;
        if (var.type == typeCom) dpuser_vars[id].cvalue = CreateComplex(*var.cvalue);
    }

    if (dpuser_vars[id].type == typeStrarr) {
        if (indices[0] < 0) {
            dp_output("Setting range: Index <= 0 not allowed for string array variable %s\n", id.c_str());
            return rv;
        }
        if (var.type == typeStr) {
            for (long n = indices[0]; n <= indices[1]; n++) (*dpuser_vars[id].arrvalue)[n] = (*var.svalue);
        } else {
            dp_output("Argument is not a string\n");
            return rv;
        }
    } else if (dpuser_vars[id].type == typeStr) {
        if (var.type != typeStr) {
            dp_output("Argument is not a string\n");
            return rv;
        }
        if (indices[1] == 0) indices[1] = indices[0];
        if (indices[0] < 0) {
            dp_output("Setting range: Index <= 0 not allowed for string variable %s\n", id.c_str());
            return rv;
        }
        dpuser_vars[id].svalue->remove(indices[0], indices[1] - indices[0] + 1);
        dpuser_vars[id].svalue->insert(indices[0], *var.svalue);
    } else if (dpuser_vars[id].type == typeFits) {
        if (var.type == typeCon) {
            var.type = typeDbl;
            var.dvalue = (double)var.lvalue;
        }
        if (var.type == typeFitsFile) {
            var.type = typeFits;
            var.fvalue = CreateFits();
            if (!var.fvalue->ReadFITS(var.ffvalue->c_str())) {
                return rv;
            }
        }
        if ((var.type != typeDbl) && (var.type != typeFits)) {
            yyerror("invalid argument for setting range");
            return rv;
        }
        if (nops > dpuser_vars[id].fvalue->Naxis(0) * 2) {
            dp_output("too many range arguments for variable %s\n", id.c_str());
            return rv;
        }
        if (nops == 1) {
            if (var.type == typeDbl) {
                dpuser_vars[id].fvalue->setRawRange(var.dvalue, *frange.fvalue);
            } else if (var.type == typeFits) {
                dpuser_vars[id].fvalue->setRawRange(*var.fvalue, *frange.fvalue);
            } else {
                yyerror("invalid argument for setting range");
            }
        } else if (nops == 2) {
            if (var.type == typeDbl) {
                dpuser_vars[id].fvalue->setRawRange(var.dvalue, indices[0] + 1, indices[1] + 1);
            } else {
                if (var.type != typeFits) {
                    yyerror("invalid argument for setting range");
                    return rv;
                }
                dpuser_vars[id].fvalue->setRange(*var.fvalue, indices[0] + 1, indices[1] + 1, 1, 1, 1, 1);
            }
        } else if (dpuser_vars[id].fvalue->Naxis(0) == 2) {
            if (var.type == typeDbl) {
                dpuser_vars[id].fvalue->setRange(var.dvalue, indices[0] + 1, indices[1] + 1, indices[2] + 1, indices[3] + 1, 1, 1);
            } else {
                if (var.type != typeFits) {
                    yyerror("invalid argument for setting range");
                    return rv;
                }
                dpuser_vars[id].fvalue->setRange(*var.fvalue, indices[0] + 1, indices[1] + 1, indices[2] + 1, indices[3] + 1, 1, 1);
            }
        } else if (dpuser_vars[id].fvalue->Naxis(0) == 3) {
            if (var.type == typeDbl) {
                dpuser_vars[id].fvalue->setRange(var.dvalue, indices[0] + 1, indices[1] + 1, indices[2] + 1, indices[3] + 1, indices[4] + 1, indices[5] + 1);
            } else {
                if (var.type != typeFits) {
                    yyerror("invalid argument for setting range");
                    return rv;
                }
                dpuser_vars[id].fvalue->setRange(*var.fvalue, indices[0] + 1, indices[1] + 1, indices[2] + 1, indices[3] + 1, indices[4] + 1, indices[5] + 1);
            }
        }
    } else if (dpuser_vars[id].type == typeDpArr) {
        if (indices[0] < 0) {
            dp_output("Setting range: Index < 0 not allowed for Fits List variable %s\n", id.c_str());
            return rv;
        } else if (indices[0] > dpuser_vars[id].dparrvalue->size()-1) {
            dp_output("Setting range: Index > %i not allowed for Fits List variable %s\n", indices[0], id.c_str());
            return rv;
        }
        if ((var.type != typeFits) && (var.type != typeStrarr)) {
            dp_output("Setting range: Argument must be of type FITS or String Array");
            return rv;
        }
        switch ((*dpuser_vars[id].dparrvalue).at(indices[0]+1)->type) {
            case typeFits:
                addToListOfFits((*dpuser_vars[id].dparrvalue).at(indices[0]+1)->fvalue);
                break;
            case typeStrarr:
                addToListOfdpStringArrays((*dpuser_vars[id].dparrvalue).at(indices[0]+1)->arrvalue);
                break;
            default:
                break;
        }

        dpuserType *toAppend_tmp, *toAppend;
        toAppend_tmp = new dpuserType(var);
        toAppend = new dpuserType;
        switch (var.type) {
            case typeFits:
                if (isVariable(toAppend_tmp->fvalue)) {
                    toAppend->deep_copy(*toAppend_tmp);
                    ((*dpuser_vars[id].dparrvalue)[indices[0]+1])->copy(*toAppend);
                } else {
                    deleteFromListOfFits(toAppend_tmp->fvalue);
                    ((*dpuser_vars[id].dparrvalue)[indices[0]+1])->copy(*toAppend_tmp);
                }
                break;
            case typeStrarr:
                if (isVariable(toAppend_tmp->arrvalue)) {
                    toAppend->deep_copy(*toAppend_tmp);
                    ((*dpuser_vars[id].dparrvalue)[indices[0]+1])->copy(*toAppend);
                } else {
                    deleteFromListOfdpStringArrays(toAppend_tmp->arrvalue);
                    ((*dpuser_vars[id].dparrvalue)[indices[0]+1])->copy(*toAppend_tmp);
                }
                break;
            default: break;
        }
    }
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */

}

// TODO: allow vector as range
dpuserType operatorRangePlusPlusNode::evaluate() {
    long nops;
    dpuserType rv;
    bool success = true;

    nops = extractIndices();
    if (nops == 0) {
        return rv;
    }

#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif

    for (int i = 0; i < nops; i++) indices[i]++;
    success = dpuser_vars[id].fvalue->incRange(nops, (long *)indices);
    if (!success) yyerror("Error incrementing range");
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return rv;
}

// TODO: allow vector as range
dpuserType operatorRangeMinusMinusNode::evaluate() {
    long nops;
    dpuserType rv;
    bool success = true;

    nops = extractIndices();
    if (nops == 0) {
        return rv;
    }

#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif

    for (int i = 0; i < nops; i++) indices[i]++;
    success = dpuser_vars[id].fvalue->decRange(nops, indices);
    if (!success) yyerror("Error decrementing range");
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return rv;
}

dpuserType operatorRangePlusEqualNode::evaluate() {
    dpuserType rv;
    long nops;
    dpuserType var;

    nops = extractIndices();
    if (nops == 0) {
        return rv;
    }

    var = right->evaluate();

#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif

    if (dpuser_vars[id].type == typeStrarr) {
        if (nops != 2) {
            yyerror("Wrong number of arguments to access subset of stringarray");
            return rv;
        }
        if ((indices[0] < 0) || (indices[0] > (long)dpuser_vars[id].arrvalue->count()) ||
                (indices[1] < 0) || (indices[1] > (long)dpuser_vars[id].arrvalue->count()) ||
                (indices[1] < indices[0])) {
            yyerror("Value out of bounds");
            return rv;
        }
        if (var.type != typeStr) {
            yyerror("invalid argument for setting range");
            return rv;
        }
        for (long n = indices[0]; n <= indices[1]; n++) {
            (*dpuser_vars[id].arrvalue)[n] += *var.svalue;
        }
    } else {
        if (var.type == typeCon) {
            var.type = typeDbl;
            var.dvalue = (double)var.lvalue;
        }

        if (nops == 1) {
            if (var.type == typeDbl) {
                dpuser_vars[id].fvalue->addRawRange(var.dvalue, *frange.fvalue);
            } else {
                yyerror("invalid argument for adding range");
                return rv;
            }
        } else {

            if ((var.type != typeDbl) && (var.type != typeFits)) {
                yyerror("invalid argument for setting range");
                return rv;
            }

            for (int i = 0; i < nops; i++) indices[i]++;
            if (var.type == typeDbl) {
                if (!dpuser_vars[id].fvalue->addRange(var.dvalue, nops, indices)) {
                    yyerror("Error adding constant to range");
                    return rv;
                }
            } else if (var.type == typeFits) {
                if (!dpuser_vars[id].fvalue->addRange(*var.fvalue, nops, indices)) {
                    yyerror("Error adding array to range");
                    return rv;
                }
            }
        }
    }
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return rv;
}

dpuserType operatorRangeMinusEqualNode::evaluate() {
    dpuserType rv;
    long nops;
    dpuserType var;

    nops = extractIndices();
    if (nops == 0) {
        return rv;
    }
    if (dpuser_vars[id].type != typeFits) {
        yyerror("Wrong type to subtract range");
        return rv;
    }

    var = right->evaluate();

#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif

    if (var.type == typeCon) {
        var.type = typeDbl;
        var.dvalue = (double)var.lvalue;
    }

    if (nops == 1) {
        if (var.type == typeDbl) {
            dpuser_vars[id].fvalue->subRawRange(var.dvalue, *frange.fvalue);
        } else {
            yyerror("invalid argument for subtracting range");
            return rv;
        }
    } else {

        if ((var.type != typeDbl) && (var.type != typeFits)) {
            yyerror("invalid argument for setting range");
            return rv;
        }

        for (int i = 0; i < nops; i++) indices[i]++;
        if (var.type == typeDbl) {
            if (!dpuser_vars[id].fvalue->subRange(var.dvalue, nops, indices)) {
                yyerror("Error subtracting constant to range");
                return rv;
            }
        } else if (var.type == typeFits) {
            if (!dpuser_vars[id].fvalue->subRange(*var.fvalue, nops, indices)) {
                yyerror("Error subtracting array to range");
                return rv;
            }
        }
    }
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return rv;
}

dpuserType operatorRangeMultiplyEqualNode::evaluate() {
    dpuserType rv;
    long nops;
    dpuserType var;

    if (dpuser_vars[id].type != typeFits) {
        yyerror("Wrong type to multiply range");
        return rv;
    }
    nops = extractIndices();
    if (nops == 0) {
        return rv;
    }

    var = right->evaluate();

#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif

    if (var.type == typeCon) {
        var.type = typeDbl;
        var.dvalue = (double)var.lvalue;
    }

    if (nops == 1) {
        if (var.type == typeDbl) {
            dpuser_vars[id].fvalue->mulRawRange(var.dvalue, *frange.fvalue);
        } else {
            yyerror("invalid argument for multiplying range");
            return rv;
        }
    } else {

        if ((var.type != typeDbl) && (var.type != typeFits)) {
            yyerror("invalid argument for setting range");
            return rv;
        }

        for (int i = 0; i < nops; i++) indices[i]++;
        if (var.type == typeDbl) {
            if (!dpuser_vars[id].fvalue->mulRange(var.dvalue, nops, indices)) {
                yyerror("Error multiplying constant to range");
                return rv;
            }
        } else if (var.type == typeFits) {
            if (!dpuser_vars[id].fvalue->mulRange(*var.fvalue, nops, indices)) {
                yyerror("Error multiplying array to range");
                return rv;
            }
        }
    }
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return rv;
}

dpuserType operatorRangeDivideEqualNode::evaluate() {
    dpuserType rv;
    long nops;
    dpuserType var;

    if (dpuser_vars[id].type != typeFits) {
        yyerror("Wrong type to divide range");
        return rv;
    }

    nops = extractIndices();
    if (nops == 0) {
        return rv;
    }

    var = right->evaluate();

#ifdef DPQT
    QWriteLocker locker(&buffersLock);
#endif

    if (var.type == typeCon) {
        var.type = typeDbl;
        var.dvalue = (double)var.lvalue;
    }

    if (nops == 1) {
        if (var.type == typeDbl) {
            dpuser_vars[id].fvalue->divRawRange(var.dvalue, *frange.fvalue);
        } else {
            yyerror("invalid argument for dividing range");
            return rv;
        }
    } else {

        if ((var.type != typeDbl) && (var.type != typeFits)) {
            yyerror("invalid argument for setting range");
            return rv;
        }

        for (int i = 0; i < nops; i++) indices[i]++;
        if (var.type == typeDbl) {
            if (!dpuser_vars[id].fvalue->divRange(var.dvalue, nops, indices)) {
                yyerror("Error dividing constant to range");
                return rv;
            }
        } else if (var.type == typeFits) {
            if (!dpuser_vars[id].fvalue->divRange(*var.fvalue, nops, indices)) {
                yyerror("Error dividing array to range");
                return rv;
            }
        }
    }
#ifdef DPQT
    // Notify QFitsView of variable change
    locker.unlock();
    dpUpdateVar(id);
#endif /* DPQT */
    return rv;
}

dpuserType whereNode::evaluate() {
    dpuserType rv, arg;
    dpint64 c = 0;

    arg = comparison->evaluate();
    rv.type = typeFits;
    rv.fvalue = CreateFits();
    switch (arg.type) {
        case typeFits:
            rv.fvalue->create((long)(arg.fvalue->get_flux()), 1, I4);

            for (dpint64 i = 0; i < arg.fvalue->Nelements(); i++) {
                if (arg.fvalue->ValueAt(i) > 0) {
                    rv.fvalue->i4data[c] = i + 1;
                    c++;
                }
            }
        break;
         default:
            rv.fvalue->create(1, 1, I4);
            rv.fvalue->i4data[0] = arg.lvalue;
            if (arg.lvalue) c++;
        break;
    }
    if (id != "") {
        dpuser_vars[id].type = typeCon;
        dpuser_vars[id].lvalue = c;
    }
    return rv;
}
