dojo.provide("parus.excelFormulas");


dojo.declare("parus.excelFormulas", [], {

  trueValue: "ИСТИНА",

  falseValue: "ЛОЖЬ",

  getCellValue: function(Row, Col, Ident, asString) { /* virtual */
    return 0;
  },

  getCellNode: function(Row, Col, Ident) { /* virtual */
    return null;
  },

  __R: null,

  __C: null,

  __currentFormatCondition: function(Row, Col) { /* virtual */
    return {
      value: new parus.formulaValue({
        numval: 0
      }),
      node: null
    };
  },

  __applyFormatCondition: function(node, className) { /* abstract. Do nothing. */
  },

  __exportBoolean: function(result) {
    if (!result) return false;
    return (result.asString() != "") && (result.asString() != this.falseValue);
  },

  __export: function(result, Row, Col) {
    var hasFormat = false;
    var r = "";
    if (result) {
      if (result.isNumber() && Row && Col) {
        var node = this.getCell("R" + Row + "C" + Col);
        hasFormat = (node && node.getAttribute("fmt"))
      };

      if (hasFormat) {
        r = result.asNumber();
        r = (isNaN(r) || Math.abs(r) == Infinity) ? result.asString() : r;
      }
      else r = result.asString();
    };
    return r;

  },

  __exception: function(error, Row, Col) {
    console.log("Formula exception", {
      exception: error,
      context: this,
      row: Row,
      col: Col
    });
  },

  __fromNumber: function(numval) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return numval
      }
    });
  },

  __fromString: function(strval) {
    return new parus.excelFormulas.value({
      asString: function() {
        return strval
      }
    });
  },

  __fromValue: function(val) {

    return new parus.excelFormulas.value({

      asString: function() {
        return parus.parus8.valueToExternalString(val);
      },

      asNumber: function() {
        var value = val;
        var vtype = typeof value;
        var v = (vtype == "number") ? value : dojo.number.parse(value);
        return (isNaN(v) || Math.abs(v) == Infinity) ? 0 : v
      }

    });

  },

  __fromReference: function(Row, Col, Ident) {
    return new parus.excelFormulas.cell({
      Row: Row,
      Col: Col,
      Ident: Ident,
      formulas: this
    });
  },

  __fromRange: function(cell1, cell2) {
    return new parus.excelFormulas.range({
      Row1: cell1.Row,
      Col1: cell1.Col,
      Row2: cell2.Row,
      Col2: cell2.Col,
      formulas: this
    });

  },

  __operatorMinusUnary: function(operand) {

    return new parus.excelFormulas.value({
      asNumber: function() {
        return -operand.asNumber()
      }
    });

  },

  __operatorPercent: function(operand) {

    return new parus.excelFormulas.value({
      asNumber: function() {
        return (operand.asNumber() / 100);
      }
    });

  },

  __operatorMinusUnary: function(operand) {

    return new parus.excelFormulas.value({
      asNumber: function() {
        return -operand.asNumber()
      }
    });

  },

  __operatiorMinusUnary: function(operand) {
    //misprint. Remove in next versions
    return new parus.excelFormulas.value({
      asNumber: function() {
        return -operand.asNumber()
      }
    });

  },

  __operatorAsteric: function(left, right) {

    return new parus.excelFormulas.value({
      asNumber: dojo.hitch(this, function() {
        var result = left.asNumber() * right.asNumber();
        return (Math.abs(result) <= this.__epsilon()) ? 0 : this.__fixAccuracy(result);
      })
    });

  },

  __operatorSlash: function(left, right) {
    return new parus.excelFormulas.value({
      asNumber: dojo.hitch(this, function() {
        var result = left.asNumber() / right.asNumber();
        return (Math.abs(result) <= this.__epsilon()) ? 0 : this.__fixAccuracy(result);
      })
    });
  },


  __operatorPlus: function(left, right) {

    return new parus.excelFormulas.value({
      asNumber: dojo.hitch(this, function() {
        var result = left.asNumber() + right.asNumber();
        return (Math.abs(result) <= this.__epsilon()) ? 0 : this.__fixAccuracy(result);
      })
    });

  },

  __epsilon: function() {
    return 5e-7;
  },

  __fixAccuracy: function(n) {
    return parseFloat(n.toFixed(7));
  },

  __operatorMinus: function(left, right) {
    return new parus.excelFormulas.value({
      asNumber: dojo.hitch(this, function() {
        var result = left.asNumber() - right.asNumber();
        return (Math.abs(result) <= this.__epsilon()) ? 0 : this.__fixAccuracy(result);
      })
    });

  },

  __operatorAmpersand: function(left, right) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return left.asString() + right.asString()
      }
    });

  },


  __operatorEqual: function(left, right) {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        if (left.isNumber() && right.isNumber()) {
          return (Math.abs(left.asNumber() - right.asNumber()) <= _this.__epsilon()) ? _this.trueValue : _this.falseValue;
          /*        } if ((left.asString() == "") && right.isNumber()) {
           return (Math.abs(right.asNumber()) <= _this.__epsilon()) ? _this.trueValue : _this.falseValue;
           } if ((left.isNumber()) && (right.asString() == "")) {
           return (Math.abs(left.asNumber()) <= _this.__epsilon()) ? _this.trueValue : _this.falseValue; */
        }
        else {
          return (left.asString() == right.asString()) ? _this.trueValue : _this.falseValue;
        }
      }
    });
  },

  __operatorNotEqual: function(left, right) {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        if (left.isNumber() && right.isNumber()) {
          return (Math.abs(left.asNumber() - right.asNumber()) > _this.__epsilon()) ? _this.trueValue : _this.falseValue;
        }
        else {
          return (left.asString() != right.asString()) ? _this.trueValue : _this.falseValue;
        }
      }
    });
  },


  __operatorMore: function(left, right) {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return (left.asNumber() > right.asNumber()) ? _this.trueValue : _this.falseValue
      }
    });
  },


  __operatorMoreEqual: function(left, right) {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return (left.asNumber() >= right.asNumber()) ? _this.trueValue : _this.falseValue
      }
    });
  },


  __operatorLess: function(left, right) {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return (left.asNumber() < right.asNumber()) ? _this.trueValue : _this.falseValue
      }
    });
  },


  __operatorLessEqual: function(left, right) {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return (left.asNumber() <= right.asNumber()) ? _this.trueValue : _this.falseValue
      }
    });
  },

  __operatorDesignator: function(left, right) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.pow(left.asNumber(), right.asNumber());
      }
    });
  },

  __functionSUM: function() {
    var _arguments = arguments;
    var _this = this;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            result += arg.asNumber();
          })
        });

        return (Math.abs(result) <= _this.__epsilon()) ? 0 : _this.__fixAccuracy(result);
      }
    });

  },

  __functionPI: function() {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.PI
      }
    });
  },

  __functionTRUE: function() {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return _this.trueValue;
      }
    });
  },
  __functionFALSE: function() {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return _this.falseValue;
      }
    });
  },

  __functionISBLANK: function(arg) {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return (arg.asString() == "") ? _this.trueValue : _this.falseValue
      }
    });
  },

  __functionIF: function(condition, ifTrue, ifFalse) {
    var _this = this;
    return new parus.excelFormulas.value({
      asString: function() {
        return _this.__exportBoolean(condition) ? ifTrue.asString() : (ifFalse ? ifFalse.asString() : "");
      },
      asNumber: function() {
        return _this.__exportBoolean(condition) ? ifTrue.asNumber() : (ifFalse ? ifFalse.asNumber() : "");
      }
    });
  },

  __functionAND: function() {
    var _this = this;
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var result = true;
        dojo.forEach(_arguments, function(arg) {
          if (result) if (!_this.__exportBoolean(arg)) result = false;
        });
        return result ? _this.trueValue : _this.falseValue;
      }
    });
  },

  __functionOR: function() {
    var _this = this;
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var result = false;
        dojo.forEach(_arguments, function(arg) {
          if (!result) if (_this.__exportBoolean(arg)) result = true;
        });
        return result ? _this.trueValue : _this.falseValue;
      }
    });
  },

  __functionNOT: function(arg) {
    var _this = this;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return _this.__exportBoolean(arg) ? _this.falseValue : _this.trueValue;
      }
    });
  },

  __functionCOUNT: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            if (arg.asNumber() || (arg.asString() == "0")) result++;
          })
        });
        return result;
      }
    });
  },


  __functionCOUNTA: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            if (arg.asString()) result++;
          })
        });
        return result;
      }
    });
  },

  __functionCOUNTBLANK: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            if (!arg.asString()) result++;
          })
        });
        return result;
      }
    });
  },

  __functionAVERAGE: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        var counter = 0;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            var inc = arg.asNumber();
            if (inc) {
              result += inc;
              counter++;
            }
          })
        });
        return counter ? result / counter : result;
      }
    });
  },

  __functionAVERAGEA: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        var counter = 0;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            var inc = arg.asNumber();
            if (inc) {
              result += inc;
              counter++;
            }
          })
        });
        return counter ? result / counter : result;
      }
    });
  },

  __functionFIXED: function(value, decimals, mode) {
    var _this = this;
    return new parus.excelFormulas.value({
      asString: function() {
        var pattern = "#0";
        if (decimals) {
          if (decimals.asNumber()) pattern += ".";
          for (var i = 0; i < decimals.asNumber(); i++)
          pattern += "0";
        };
        if (!_this.__exportBoolean(mode)) pattern = "#,#" + pattern;
        return parus.parus8.formatExcelValue(value.asNumber(), pattern, true);
      }
    });
  },

  __functionROUND: function(value, decimals) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var d = (decimals) ? decimals.asNumber() : 0;
        return dojo.number.round((value.asNumber() * Math.pow(10, d)).toPrecision(13 + d)) / Math.pow(10, d);
      }
    });
  },

  __functionROUNDUP: function(value, decimals) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var d = (decimals) ? decimals.asNumber() : 0;
        return Math.ceil(value.asNumber() * Math.pow(10, d)) / Math.pow(10, d);
      }
    });
  },

  __functionROUNDDOWN: function(value, decimals) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var d = (decimals) ? decimals.asNumber() : 0;
        return Math.floor(value.asNumber() * Math.pow(10, d)) / Math.pow(10, d);
      }
    });
  },

  __functionABS: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.abs(value.asNumber());
      }
    });
  },

  __functionSIN: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.sin(value.asNumber());
      }
    });
  },

  __functionCOS: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.cos(value.asNumber());
      }
    });
  },

  __functionTAN: function(value) {

    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.tan(value.asNumber());
      }
    });

  },

  __functionACOS: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.acos(value.asNumber());
      }
    });
  },

  __functionASIN: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.asin(value.asNumber());
      }
    });
  },

  __functionATAN: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.atan(value.asNumber());
      }
    });
  },

  __functionATAN2: function(y, x) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.atan2(y.asNumber(), x.asNumber());
      }
    });
  },

  __functionEXP: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.exp(value.asNumber());
      }
    });
  },

  __functionLN: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.log(value.asNumber());
      }
    });
  },


  __functionLOG: function(value, base) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.log(value.asNumber() / Math.log(base.asNumber()));
      }
    });
  },

  __functionLOG10: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.log(value.asNumber() / Math.log(10));
      }
    });
  },

  __functionSQRT: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.sqrt(value.asNumber());
      }
    });
  },

  __functionACOSH: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.log(value.asNumber() + Math.sqrt(value.asNumber() * value.asNumber() - 1));
      }
    });
  },

  __functionASC: function(value) {
    return new parus.excelFormulas.value({
      asString: function() {
        return value.asString();
      }
    });
  },

  __functionASINH: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.log(value.asNumber() + Math.sqrt(value.asNumber() * value.asNumber() + 1));
      }
    });
  },

  __functionATANH: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.log((1 + value.asNumber()) / (1 - value.asNumber())) / 2;
      }
    });
  },

  __functionCOSH: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return (Math.exp(value.asNumber()) + Math.exp(-value.asNumber())) / 2;
      }
    });
  },

  __functionMAXA: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var result = "";
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            var a = arg.asString();
            if (a > result) result = a;
          })
        });
        return result;
      }
    });
  },

  __functionMINA: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var result = "";
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            var a = arg.asString();
            if (!result || a < result) result = a;
          })
        });
        return result;
      }
    });
  },

  __functionMAX: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = null;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            var a = arg.asNumber();
            var s = arg.asString();
            if (s && (!result || (a > result))) result = a;
          })
        });
        return result;
      }
    });
  },

  __functionMIN: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = null;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            var a = arg.asNumber();
            var s = arg.asString();
            if (s && (!result || (a < result))) result = a;
          })
        });
        return result;
      }
    });
  },

  __functionSINH: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return (Math.exp(value.asNumber()) - Math.exp(-value.asNumber())) / 2;
      }
    });
  },

  __functionTANH: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return (Math.exp(2 * value.asNumber()) - 1) / (Math.exp(2 * value.asNumber()) + 1);
      }
    });
  },

  __functionADDRESS: function(Row, Col, type) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var t = type ? type.asNumber() : 1;
        if (t == 1) return "R" + Row.asNumber() + "C" + Col.asNumber();
        else if (t == 2) return "R" + Row.asNumber() + "C[" + (Col.asNumber() - C) + "]";
        else if (t == 3) return "R[" + (Row.asNumber() - R) + "]C" + Col.asNumber();
        else if (t == 4) return "R[" + (Row.asNumber() - R) + "]C[" + (Col.asNumber() - C) + "]";
      }
    });
  },

  __functionHLOOKUP: function(criteria, range, rowNum) {
    if (range.Row1 && range.Row2 && range.Col1 && range.Col2) for (var j = range.Col1; j <= range.Col2; j++) {
      if (this.__exportBoolean(
      this.__operatorEqual(criteria, this.__fromReference(range.Row1, j, null)))) return this.__fromReference(range.Row1 - 1 + rowNum.asNumber(), j, null)
    }
    else throw new Error("Недопустимый тип аргумета.");
  },

  __functionVLOOKUP: function(criteria, range, colNum) {
    if (range.Row1 && range.Row2 && range.Col1 && range.Col2) for (var j = range.Row1; j <= range.Row2; j++) {
      if (this.__exportBoolean(
      this.__operatorEqual(criteria, this.__fromReference(j, range.Col1, null)))) return this.__fromReference(j, range.Col1 - 1 + colNum.asNumber(), null)
    }
    else throw new Error("Недопустимый тип аргумета.");
  },

  __functionTIMEVALUE: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = str.asString();
        var d = null;
        if (s.length == 8) d = dojo.date.locale.parse(
        s, {
          selector: "time",
          timePattern: "HH:mm:ss"
        })
        else if (s.length == 5) d = dojo.date.locale.parse(
        s, {
          selector: "time",
          timePattern: "HH:mm"
        });

        if (d) return (d.getHours() / 24) + (d.getMinutes() / 24 / 60) + (d.getSeconds() / 24 / 60 / 60)
        else return null;
      }
    });
  },

  __functionTIME: function(h, m, s) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return (h.asNumber() / 24) + (m.asNumber() / 24 / 60) + (s.asNumber() / 24 / 60 / 60)
      }
    });
  },

  __functionCHOOSE: function() {
    return arguments[arguments[0].toNumber()];
  },

  __functionPOWER: function(base, exp) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.pow(base.asNumber(), exp.asNumber());
      }
    });
  },

  __functionPRODUCT: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 1;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            result = arg.asNumber() * result;
          })
        });
        return result;
      }
    });
  },

  __functionMOD: function(left, right) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return left.asNumber() % right.asNumber()
      }
    });
  },

  __functionLEN: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = value.asString();
        return s ? s.length : 0;
      }
    });
  },

  __functionLEFT: function(value, len) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return value.asString().substr(0, len.asNumber());
      }
    });
  },

  __functionRIGHT: function(value, len) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var s = value.asString();
        return s.substr(s.length - len.asNumber(), len.asNumber());
      }
    });
  },

  __functionMID: function(value, start, len) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var s = value.asString();
        var l = len ? len.asNumber() : 10000;
        return s.substr(start.asNumber() - 1, l);
      }
    });
  },

  __functionSEARCH: function(template, value, start) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return (start ? value.asString().toUpperCase().indexOf(template.asString().toUpperCase(), start.asNumber() - 1) : value.asString().toUpperCase().indexOf(template.asString().toUpperCase())) + 1;
      }
    });
  },

  __functionFIND: function(template, value, start) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return (start ? value.asString().indexOf(template.asString(), start.asNumber() - 1) : value.asString().indexOf(template.asString())) + 1;
      }
    });
  },

  __functionSUBSTITUTE: function(value, o, n, num) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var os = o.asString();
        var ns = n.asString();
        var s = value.asString().split(os);
        if (!s.length) return value.asString();

        var result = "";
        var idx = num ? num.asNumber() : 1;
        for (var i = 0; i < s.length; i++) {
          result = result + s[i];
          if (i == idx - 1) result = result + ns
          else if (i < s.length - 1) result = result + os;
        }
        return result;
      }
    });
  },

  __functionTRIM: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return dojo.trim(value.asString());
      }
    });
  },


  __functionEXACT: function(left, right) {
    return this.__operatorEqual(left, right);
  },

  __functionLOWER: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return value.asString().toLowerCase();
      }
    });
  },

  __functionUPPER: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return value.asString().toUpperCase();
      }
    });
  },

  __functionCONCATENATE: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var result = "";
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            result = result + arg.asString();
          })
        });
        return result;
      }
    });
  },

  __functionYEAR: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = str.asString();
        var d = dojo.date.locale.parse(s, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
        return d ? d.getFullYear() : null
      }
    });
  },

  __functionMONTH: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = str.asString();
        var d = dojo.date.locale.parse(s, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
        return d ? d.getMonth() + 1 : null
      }
    });
  },

  __functionMINUTE: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = str.asString();
        var d = dojo.date.locale.parse(s, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
        return d ? d.getMinutes() : null
      }
    });
  },

  __functionDAY: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = str.asString();
        var d = dojo.date.locale.parse(s, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
        return d ? d.getDate() : null
      }
    });
  },

  __functionWEEKDAY: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = str.asString();
        var d = dojo.date.locale.parse(s, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
        return d ? d.getDay() + 1 : null
      }
    });
  },

  __functionHOUR: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = str.asString();
        var d = dojo.date.locale.parse(s, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
        return d ? d.getHours() : null
      }
    });
  },

  __functionSECOND: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s = str.asString();
        var d = dojo.date.locale.parse(s, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
        return d ? d.getSeconds() : null
      }
    });
  },

  __functionDEGREES: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return value.asNumber() * 180 / Math.PI;
      }
    });
  },

  __functionRADIANS: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return value.asNumber() * Math.PI / 180;
      }
    });
  },

  __functionDATE: function(y, m, d) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return dojo.date.locale.format(new Date(y.asNumber(), m.asNumber() - 1, d.asNumber()), {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
      }
    });
  },

  __functionDATEVALUE: function(value) {
    return value;
  },

  __functionINDIRECT: function(ref) {
    var addr = ref.asString().match(/^R(\d+)C(\d+)$/);
    if (addr) return this.__fromReference(addr[1], addr[2])
    else throw new Error("Используйте RC-стиль ссылок.");
  },

  __functionDAYS360: function(argd1, argd2) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var s1 = argd1.asString();
        var d1 = dojo.date.locale.parse(s1, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s1, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;

        var s2 = argd2.asString();
        var d2 = dojo.date.locale.parse(s2, {
          selector: "date",
          datePattern: "dd.MM.yyyy HH:mm:ss"
        }) || dojo.date.locale.parse(
        s2, {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        }) || null;
        return (d1 && d2) ? ((d2 - d1) / 1000 / 24 / 3600) : null;
      }
    });
  },

  __functionISLOGICAL: function(arg) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        if ((arg.asString() == this.trueValue) || (arg.asString() == this.falseValue)) return this.trueValue
        else return this.falseValue;
      })
    });
  },

  __functionISNA: function(arg) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        if (arg && arg.asString()) return this.trueValue
        else return this.falseValue;
      })
    });
  },

  __functionISNONTEXT: function(arg) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        if (arg.asNumber()) return this.trueValue
        else return this.falseValue;
      })
    });
  },

  __functionISERR: function(arg) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        if (!arg) return this.falseValue;
        try {
          var s = arg.asString();
          if ((s == "Infinity") || (s == "NaN")) return this.trueValue
          else return this.falseValue;
        }
        catch (e) {
          return this.trueValue;
        };
      })
    });
  },

  __functionISERROR: function(arg) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        if (!arg) return this.falseValue;
        try {
          var s = arg.asString();
          if ((s == "Infinity") || (s == "NaN")) return this.trueValue
          else return this.falseValue;
        }
        catch (e) {
          return this.trueValue;
        };
      })
    });

  },

  __functionISREF: function(arg) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        if (arg && (arg.Col || arg.Row)) return this.trueValue
        else return this.falseValue;
      })
    });
  },

  __functionISTEXT: function(arg) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        if (arg.asNumber()) return this.falseValue
        else return this.trueValue;
      })
    });
  },

  __functionISNUMBER: function(arg) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        if (arg.asNumber()) return this.trueValue
        else return this.falseValue;
      })
    });
  },


  __functionSIGN: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        var v = value.asNumber();
        if (v < 0) return -1
        else if (v > 0) return 1
        else return 0;
      }
    });
  },

  __functionVALUE: function(value) {
    return value;
  },

  __functionINFO: function(t) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var infoType = t.asString();
        if (infoType = "версия") return "parusBalanceOnLine";
        else if (infoType = "версияос") return navigator.userAgent;
        else if (infoType = "доступнаяпамять") return 0;
        else if (infoType = "используемаяпамять") return 0;
        else if (infoType = "источник") return "R" + R + "C" + C
        else if (infoType = "каталог") return document.URL;
        else if (infoType = "памятьвсего") return 0;
        else if (infoType = "перевычислить") return "Автоматически"
        else if (infoType = "система") return "pcdos"
        else if (infoType = "числофайлов") return 1;
        else return null;
      }
    });
  },

  __functionCODE: function(str) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return str.charCodeAt(0);
      }
    });
  },

  __functionCHAR: function(code) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return String.fromCharCode(code.asNumber)
      }
    });
  },

  __functionNA: function() {
    return new parus.excelFormulas.value({
      asString: function() {
        return null;
      },
      asNumber: function() {
        return null;
      }
    });
  },

  __functionODD: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        return ((value.asNumber() % 2) == 1) ? this.trueValue : this.falseValue
      })
    });
  },

  __functionEVEN: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: dojo.hitch(this, function() {
        return ((value.asNumber() % 2) == 0) ? this.trueValue : this.falseValue
      })
    });
  },


  __functionCEILING: function(value, decimals) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        d = (decimals) ? decimals.asNumber() : 0;
        return Math.ceil(value.asNumber() * Math.pow(10, d)) / Math.pow(10, d);
      }
    });
  },

  __functionFLOOR: function(value, decimals) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        d = (decimals) ? decimals.asNumber() : 0;
        return Math.floor(value.asNumber() * Math.pow(10, d)) / Math.pow(10, d);
      }
    });
  },

  __functionTRUNC: function(value, decimals) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        d = (decimals) ? decimals.asNumber() : 0;
        return Math.floor(value.asNumber() * Math.pow(10, d)) / Math.pow(10, d);
      }
    });
  },

  __functionCLEAN: function(value) {
    return value;
  },

  __functionREPT: function(text, num) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        var s = text.asString();
        var r = [];
        for (var i = 0; i < (num.asNumber() || 0); i++)
        r.push(s);
        return r.join("");
      }
    });
  },

  __functionMATCH: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        var idx = null;
        var cntr = 0;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            if (!idx) idx = arg.asNumber()
            else if (!result && (arg.asNumber() == idx)) result = cntr;
            cntr++;
          })
        });
        return result;
      }
    });
  },


  __functionPROPER: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return dojo.map((text.asString() || "").split(" "), function(v) {
          return (v || "").substring(1, 1).toUpperCase() + (v || "").substring(2).toLowerCase();
        }).join(" ");
      }
    });
  },

  __functionLOOKUP: function() {
    var _arguments = arguments;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        var idx = null;
        var cntr = 0;
        dojo.forEach(_arguments, function(arg) {
          arg.forEach(function(arg) {
            if (!idx) idx = arg.asNumber()
            else if (!result && (arg.asNumber() == idx)) result = cntr;
            cntr++;
          })
        });
        return result;
      }
    });
  },

  __functionDOLLAR: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return dojo.currency.format(value.asNumber());
      }
    })
  },

  __functionTODAY: function() {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return dojo.date.locale.format(new Date(), {
          selector: "date",
          datePattern: "dd.MM.yyyy"
        });
      }
    })
  },

  __functionRAND: function() {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.random();
      }
    })
  },

  __functionCOLUMN: function(ref) {
    var _this = this;
    return new parus.excelFormulas.value({

      asNumber: dojo.hitch(this, function() {
        var addr = null;
        if (ref) {
          if ((ref.Row && ref.Col) || ref.Ident) addr = _this.pane.cellShiftedAddress(_this.getCellNode(ref.Row, ref.Col, ref.Ident));
          else if (ref.Row1 && ref.Col1) addr = _this.pane.cellShiftedAddress(_this.getCellNode(ref.Row1, ref.Col1, null));
        }
        else addr = _this.pane.cellShiftedAddress(_this.getCellNode(_this.__R, _this.__C, null));

        return addr ? addr.c : 0;
      })

    })
  },

  __functionROW: function(ref) {
    var _this = this;
    return new parus.excelFormulas.value({

      asNumber: dojo.hitch(this, function() {
        var addr = null;

        if (ref) {
          if ((ref.Row && ref.Col) || ref.Ident) addr = _this.pane.cellShiftedAddress(_this.getCellNode(ref.Row, ref.Col, ref.Ident));
          else if (ref.Row1 && ref.Col1) addr = _this.pane.cellShiftedAddress(_this.getCellNode(ref.Row1, ref.Col1, null));
        }
        else addr = _this.pane.cellShiftedAddress(_this.getCellNode(_this.__R, _this.__C, null));

        return addr ? addr.r : 0;
      })

    })
  },

  __functionT: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return value.asString();
      }
    })
  },

  __functionNOW: function() {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return dojo.date.locale.format(new Date(), {
          formatLength: "full",
          timePattern: "HH:mm:ss"
        });
      }
    })
  },

  __functionTEXT: function(value) {
    return new parus.excelFormulas.value({
      isNumber: function() {
        return false;
      },
      asString: function() {
        return value.asString();
      }
    })
  },

  __functionINT: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return Math.floor(value.asNumber());
      }
    });
  },

  __functionN: function(value) {
    return new parus.excelFormulas.value({
      asNumber: function() {
        return value.asNumber();
      }
    });
  },


  __functionRANK: function(value, range, ord) {
    var _this = this;
    var v = value.asNumber();
    var o = ord ? ord.asNumber() : 0;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        range.forEach(function(arg) {
          if (arg.isNumber()) {
            var a = arg.asNumber();
            if (o) {
              if (a < v) result++;
            }
            else {
              if (a > v) result++;
            }
          }
        });
        return (result + 1);
      }
    });
  },

  __functionRANKAVG: function(value, range, ord) {

    var _this = this;
    var v = value.asNumber();
    var o = ord ? ord.asNumber() : 0;
    return new parus.excelFormulas.value({
      asNumber: function() {
        var result = 0;
        var eqcnt = 0;
        var s = 0;
        range.forEach(function(arg) {
          if (arg.isNumber()) {
            var a = arg.asNumber();
            if (a == v) {
              eqcnt++;
              s += eqcnt;
            };

            if (o) {
              if (a < v) result++;
            }
            else {
              if (a > v) result++;
            }
          }
        });
        return eqcnt ? (result + (s / eqcnt)) : 0;
      }
    });
  },

  __functionRANKEQ: function(value, range, ord) {
    return this.__functionRANK(value, range, ord);
  },

  /* остальные не обязательно */

  __functionFDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionFINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionZTEST: function() {
    throw new Error("функция пока не реализована.");
  },

  __functionSYD: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSLN: function() {
    throw new Error("функция пока не реализована.");
  },


  __functionBETAINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionBETADIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionFV: function() {
    throw new Error("функция пока не реализована.");
  },

  __functionBINOMDIST: function() {
    throw new Error("функция пока не реализована.");
  },


  __functionWEIBULL: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionPROB: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionIRR: function() {
    throw new Error("функция пока не реализована.");
  },

  __functionGAMMALN: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionGAMMAINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionGAMMADIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionHYPGEOMDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionHYPERLINK: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionDDB: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionVAR: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionVARA: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionVARP: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionVARPA: function() {
    throw new Error("функция пока не реализована.");
  },


  __functionDB: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCONFIDENCE: function() {
    throw new Error("функция пока не реализована.");
  },

  __functionINDEX: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionDEVSQ: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionQUARTILE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionRSQ: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCOVAR: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCORREL: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionNPER: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCRITBINOM: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionLOGEST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionLINEST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionLOGINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionLOGNORMDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionMIRR: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionMEDIAN: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionMINVERSE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionMODE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionMDETERM: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionMMULT: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionLARGE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSMALL: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSLOPE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionRATE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSTANDARDIZE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionNORMINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionNORMDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionNORMSINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionNORMSDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionNPV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionAREAS: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionPPMT: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionNEGBINOMDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionINTERCEPT: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionVDB: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionPERMUT: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionPERCENTILE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionPV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionPEARSON: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionIPMT: function() {
    throw new Error("функция пока не реализована.");
  },

  __functionPMT: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionFORECAST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionPERCENTRANK: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionPOISSON: function() {
    throw new Error("функция пока не реализована.");
  },

  __functionROMAN: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionGROWTH: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSKEW: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionOFFSET: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionHARMEAN: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionGEOMEAN: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionAVEDEV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSTDEV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSTDEVA: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSTDEVP: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSTDEVPA: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSTEYX: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionTDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionTINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSUMIF: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSUMSQ: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSUMXMY2: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSUMPRODUCT: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSUMX2MY2: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionSUMX2PY2: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCOUNTIF: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionTREND: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionTYPE: function() {
    throw new Error("функция пока не реализована.");
  },

  __functionTRANSPOSE: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionTTEST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionTRIMMEAN: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionFACT: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionFISHER: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionFISHERINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionFTEST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCHIINV: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCHIDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCHITEST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionFREQUENCY: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCOMBIN: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCOLUMNS: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionROWS: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionEXPONDIST: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionKURT: function() {
    throw new Error("функция пока не реализована.");
  },
  __functionCELL: function() {
    throw new Error("функция пока не реализована.");
  }


});


dojo.declare("parus.excelFormulas.value", [], {

  postscript: function(mixin) {
    if (mixin) dojo.mixin(this, mixin);
  },

  /* attention: unleashed recursion: */

  asString: function() {
    return parus.parus8.valueToExternalString(this.asNumber());
  },

  asNumber: function() {
    var value = this.asString();
    var vtype = typeof value;
    var v = (vtype == "number") ? value : dojo.number.parse(value);
    return (isNaN(v) || Math.abs(v) == Infinity) ? 0 : v
  },

  isNumber: function() {
    var value = this.asString();
    var vtype = typeof value;
    var v = (vtype == "number") ? value : dojo.number.parse(value);
    return (isNaN(v) || Math.abs(v) == Infinity) ? false : true
  },

  forEach: function(func) {
    func(this);
  }

});

dojo.declare("parus.excelFormulas.cell", [], {

  Col: null,

  Row: null,

  Ident: null,

  formulas: null,

  postscript: function(mixin) {
    if (mixin) dojo.mixin(this, mixin);
  },

  asString: function() {
    return this.formulas.getCellValue(this.Row, this.Col, this.Ident, true);
  },

  asNumber: function() {
    return this.formulas.getCellValue(this.Row, this.Col, this.Ident, false);
  },

  isNumber: function() {
    var value = this.asString();
    var vtype = typeof value;
    var v = (vtype == "number") ? value : dojo.number.parse(value);
    return (isNaN(v) || Math.abs(v) == Infinity) ? false : true
  },

  forEach: function(func) {
    func(this);
  }
});

dojo.declare("parus.excelFormulas.range", [], {

  Row1: null,

  Row2: null,

  Col1: null,

  Col2: null,

  formulas: null,

  postscript: function(mixin) {
    if (mixin) dojo.mixin(this, mixin);
  },

  forEach: function(func) {
    var _this = this;
    for (var i = this.Row1; i <= this.Row2; i++)
    for (var j = this.Col1; j <= this.Col2; j++) {
      var key = "R" + i + "C" + j;
      if (_this.formulas.getCell(key)) func(_this.formulas.__fromReference(i, j, null));
      else dojo.forEach(_this.formulas.pane._tables, function(table) {
        dojo.forEach(table.clones, function(clone) {
          if (clone.formulas.getCell(key)) func(clone.formulas.__fromReference(i, j, null));
        });
      });
    }
  }

});