#include #include "../shared/TclUtil.h" #include "../shared/SimpleLogFile.h" #include "GridFiller.h" ///////////////////////////////////////////////////////////////////// // GridFiller ///////////////////////////////////////////////////////////////////// int GridFiller::commonArgs(int objc, Tcl_Obj *const objv[], int &firstRow, int &lastRow, int &column, bool &skipBadValues) { if ((objc < 4) || (objc > 5)) { Tcl_WrongNumArgs(_interp, 1, objv, "firstRow lastRow column ?skipBadValues?"); return TCL_ERROR; } if (Tcl_GetIntFromObj(_interp, objv[1], &firstRow) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(_interp, objv[2], &lastRow) != TCL_OK) return TCL_ERROR; const std::string columnName = getString(objv[3]); column = _grid->getPrototype()->findColumn(columnName); if (column < 0) { Tcl_SetObjResult(_interp, makeTclString("Unknown column: " + columnName)); return TCL_ERROR; } int skip = 0; if (objc == 5) { if (Tcl_GetBooleanFromObj(_interp, objv[4], &skip) != TCL_OK) return TCL_ERROR; } skipBadValues = skip; return TCL_OK; } int GridFiller::sumCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->sumCmd(objc, objv); } int GridFiller::sumCmd(int objc, Tcl_Obj *const objv[]) { int firstRow; int lastRow; int column; bool skipBadValues; if (commonArgs(objc, objv, firstRow, lastRow, column, skipBadValues) != TCL_OK) return TCL_ERROR; const double result = _grid->sum(_grid->nextRow() - firstRow, _grid->nextRow() - lastRow, column, skipBadValues); Tcl_SetObjResult(_interp, Tcl_NewDoubleObj(result)); return TCL_OK; } int GridFiller::countCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->countCmd(objc, objv); } int GridFiller::countCmd(int objc, Tcl_Obj *const objv[]) { int firstRow; int lastRow; int column; { // This is just like the sum, average, etc., except that the optional // argument does not exist in this command. if (objc != 4) { Tcl_WrongNumArgs(_interp, 1, objv, "firstRow lastRow column"); return TCL_ERROR; } bool unused; if (commonArgs(objc, objv, firstRow, lastRow, column, unused) != TCL_OK) return TCL_ERROR; } const int result = _grid->count(_grid->nextRow() - firstRow, _grid->nextRow() - lastRow, column); Tcl_SetObjResult(_interp, Tcl_NewDoubleObj(result)); return TCL_OK; } int GridFiller::averageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->averageCmd(objc, objv); } int GridFiller::averageCmd(int objc, Tcl_Obj *const objv[]) { int firstRow; int lastRow; int column; bool skipBadValues; if (commonArgs(objc, objv, firstRow, lastRow, column, skipBadValues) != TCL_OK) return TCL_ERROR; const double result = _grid->average(_grid->nextRow() - firstRow, _grid->nextRow() - lastRow, column, skipBadValues); Tcl_SetObjResult(_interp, Tcl_NewDoubleObj(result)); return TCL_OK; } int GridFiller::maxCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->maxCmd(objc, objv); } int GridFiller::maxCmd(int objc, Tcl_Obj *const objv[]) { int firstRow; int lastRow; int column; bool skipBadValues; if (commonArgs(objc, objv, firstRow, lastRow, column, skipBadValues) != TCL_OK) return TCL_ERROR; const double result = _grid->max(_grid->nextRow() - firstRow, _grid->nextRow() - lastRow, column, skipBadValues); Tcl_SetObjResult(_interp, Tcl_NewDoubleObj(result)); return TCL_OK; } int GridFiller::minCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->minCmd(objc, objv); } int GridFiller::minCmd(int objc, Tcl_Obj *const objv[]) { int firstRow; int lastRow; int column; bool skipBadValues; if (commonArgs(objc, objv, firstRow, lastRow, column, skipBadValues) != TCL_OK) return TCL_ERROR; const double result = _grid->min(_grid->nextRow() - firstRow, _grid->nextRow() - lastRow, column, skipBadValues); Tcl_SetObjResult(_interp, Tcl_NewDoubleObj(result)); return TCL_OK; } int GridFiller::refCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->refCmd(objc, objv); } int GridFiller::refCmd(int objc, Tcl_Obj *const objv[]) { if ((objc < 2) || (objc > 3)) { Tcl_WrongNumArgs(_interp, 1, objv, "row ?column?"); return TCL_ERROR; } int row; if (Tcl_GetIntFromObj(_interp, objv[1], &row) != TCL_OK) return TCL_ERROR; int columnIndex; if (objc >= 3) { const std::string columnName = getString(objv[2]); columnIndex = _grid->getPrototype()->findColumn(columnName); if (columnIndex < 0) { Tcl_SetObjResult(_interp, makeTclString("Unknown column: " + columnName)); return TCL_ERROR; } } else columnIndex = _grid->getCalculationCol(); // TODO we should really check to make sure that we are not looking forward. // The current implementation of GridInstance doesn't allow that. You can // only fill in a value at the very end of the list. However, we've looked // at other implementations. const double result = _grid->referencePacked(_grid->nextRow() - row, columnIndex); Tcl_SetObjResult(_interp, Tcl_NewDoubleObj(result)); return TCL_OK; } int GridFiller::symbolCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->symbolCmd(objc, objv); } int GridFiller::symbolCmd(int objc, Tcl_Obj *const objv[]) { if (objc != 1) { Tcl_WrongNumArgs(_interp, 1, objv, ""); return TCL_ERROR; } if (!_grid) { Tcl_SetObjResult(_interp, makeTclString("Grid is not currently available.")); return TCL_ERROR; } Tcl_SetObjResult(_interp, makeTclString(_grid->getDataProvider()->getSymbol())); return TCL_OK; } int GridFiller::currentCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->currentCmd(objc, objv); } int GridFiller::currentCmd(int objc, Tcl_Obj *const objv[]) { if ((objc < 2) || (objc > 3)) { Tcl_WrongNumArgs(_interp, 1, objv, "data_type ?offset?"); return TCL_ERROR; } const std::string dataType = getString(objv[1]); int offset = 0; if (objc >= 3) if (Tcl_GetIntFromObj(_interp, objv[2], &offset) != TCL_OK) return TCL_ERROR; if (!_grid) { Tcl_SetObjResult(_interp, makeTclString("Grid is not currently available.")); return TCL_ERROR; } std::string errorMsg; const double result = _grid->getDataProvider()->getValue(dataType, _grid, offset, errorMsg); if (errorMsg.empty()) { Tcl_SetObjResult(_interp, Tcl_NewDoubleObj(result)); return TCL_OK; } else { Tcl_SetObjResult(_interp, makeTclString(errorMsg)); return TCL_ERROR; } } int GridFiller::ifEmptyCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->ifEmptyCmd(objc, objv); } int GridFiller::ifEmptyCmd(int objc, Tcl_Obj *const objv[]) { for (int i = 1; i < objc; i++) { double result; if (Tcl_GetDoubleFromObj(_interp, objv[i], &result) == TCL_OK) if (isfinite(result)) { Tcl_SetObjResult(_interp, objv[i]); return TCL_OK; } } Tcl_SetObjResult(_interp, Tcl_NewObj()); return TCL_OK; } int GridFiller::isEmptyCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->isEmptyCmd(objc, objv); } int GridFiller::isEmptyCmd(int objc, Tcl_Obj *const objv[]) { if (objc != 2) { Tcl_WrongNumArgs(_interp, 1, objv, "value"); return TCL_ERROR; } bool result; double value; if (Tcl_GetDoubleFromObj(NULL, objv[1], &value) != TCL_OK) result = true; else if (isfinite(value)) result = false; else result = true; Tcl_SetObjResult(_interp, Tcl_NewBooleanObj(result)); return TCL_OK; } int GridFiller::getValuesAllCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { return ((GridFiller *)clientData)->getValuesAllCmd(objc, objv); } int GridFiller::getValuesAllCmd(int objc, Tcl_Obj *const objv[]) { int firstRow; int lastRow; int column; if (objc != 4) { Tcl_WrongNumArgs(_interp, 1, objv, "firstRow lastRow column"); return TCL_ERROR; } if (Tcl_GetIntFromObj(_interp, objv[1], &firstRow) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(_interp, objv[2], &lastRow) != TCL_OK) return TCL_ERROR; const std::string columnName = getString(objv[3]); if (!_grid) { Tcl_SetObjResult (_interp, makeTclString("Grid not found. " + getString(objv[0]) + " can only be called when updating a grid.")); return TCL_ERROR; } column = _grid->getPrototype()->findColumn(columnName); if (column < 0) { Tcl_SetObjResult(_interp, makeTclString("Unknown column: " + columnName)); return TCL_ERROR; } std::vector< double > values; _grid->getValuesAll(values, _grid->getCalculationRowPacked() - firstRow, _grid->getCalculationRowPacked() - lastRow, column); Tcl_Obj *result = Tcl_NewObj(); for (std::vector< double >::const_iterator it = values.begin(); it != values.end(); it++) { Tcl_ListObjAppendElement(NULL, result, Tcl_NewDoubleObj(*it)); } Tcl_SetObjResult(_interp, result); return TCL_OK; } void GridFiller::initInterp() { if (_interp) return; _interp = Tcl_CreateInterp(); Tcl_Init(_interp); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::sum", sumCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::count", countCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::average", averageCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::min_val", minCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::max_val", maxCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::ref", refCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::current", currentCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::symbol", symbolCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::if_empty", ifEmptyCmd, this, NULL); Tcl_CreateObjCommand(_interp, "::tcl::mathfunc::is_empty", isEmptyCmd, this, NULL); Tcl_CreateObjCommand(_interp, "get_values_all", getValuesAllCmd, this, NULL); } /* I explicitly initialize _interp to NULL here, and create the interpreter * later. * * It causes problems if you create the interpreter in one thread, and use * it in a different thread. I'm not 100% sure why. Some operations worked * but others returned an error for no good reason. (Out of stack space.) * * It was convenient for a thread object to create a GridFiller in its own * constructor, but to use the GridFiller in its own thread. It seemed * easier to fix the problem within this object, rather than making more * rules for accessing the GridFiller object. * * I'm not sure about destruction. In this case it's likely that the * interpreter will be disposed of in a different thread, the same thread * which calls the GridFiller constructor. I don't know if that's a problem * or not. */ GridFiller::GridFiller() : _interp(NULL), _grid(NULL), _errorCollector(NULL) { } GridFiller::~GridFiller() { sendToLogFile(TclList()<nextCol() == 0) && grid->isPacked() && !grid->getDataProvider()->getValid(grid)) { grid->skipRow(); return; } initInterp(); _grid = grid; const std::string formula = grid->getNextFormula(); double output; if (formula.empty()) { output = GridInstance::invalid(); } else { TclObjHolder code = TclObjCache::getInstance().find(formula); int result = Tcl_ExprDoubleObj(_interp, code.getObj(), &output); if (result != TCL_OK) { //std::string errorMessage = Tcl_GetStringResult(_interp); output = GridInstance::invalid(); if (_errorCollector) _errorCollector->add(_grid, _interp); } } grid->store(output); _grid = NULL; } void GridFiller::fillRow(GridInstance *grid) { do { fillCell(grid); } while (grid->nextCol()); }