/* * Licensed to the Apache Software Foundation (ASF) Under one or more * contributor license agreements. See the NOTICE file distributed with * this work for Additional information regarding copyright ownership. * The ASF licenses this file to You Under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed Under the License is distributed on an "AS Is" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations Under the License. */ namespace HH.WMS.Utils.NPOI.SS.Formula.Functions { using System; using HH.WMS.Utils.NPOI.SS.Formula.Eval; using HH.WMS.Utils.NPOI.SS.Formula; public interface Accumulator { double Accumulate(double x, double y); } /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ public abstract class XYNumericFunction : Fixed2ArgFunction { protected static int X = 0; protected static int Y = 1; private abstract class ValueArray : ValueVector { private int _size; protected ValueArray(int size) { _size = size; } public ValueEval GetItem(int index) { if (index < 0 || index > _size) { throw new ArgumentException("Specified index " + index + " is outside range (0.." + (_size - 1) + ")"); } return GetItemInternal(index); } protected abstract ValueEval GetItemInternal(int index); public int Size { get { return _size; } } } private class SingleCellValueArray : ValueArray { private ValueEval _value; public SingleCellValueArray(ValueEval value) : base(1) { _value = value; } protected override ValueEval GetItemInternal(int index) { return _value; } } private class RefValueArray : ValueArray { private RefEval _ref; public RefValueArray(RefEval ref1) : base(1) { _ref = ref1; } protected override ValueEval GetItemInternal(int index) { return _ref.InnerValueEval; } } private class AreaValueArray : ValueArray { private TwoDEval _ae; private int _width; public AreaValueArray(TwoDEval ae) : base(ae.Width * ae.Height) { _ae = ae; _width = ae.Width; } protected override ValueEval GetItemInternal(int index) { int rowIx = index / _width; int colIx = index % _width; return _ae.GetValue(rowIx, colIx); } } protected class DoubleArrayPair { private double[] _xArray; private double[] _yArray; public DoubleArrayPair(double[] xArray, double[] yArray) { _xArray = xArray; _yArray = yArray; } public double[] GetXArray() { return _xArray; } public double[] GetYArray() { return _yArray; } } public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { double result; try { ValueVector vvX = CreateValueVector(arg0); ValueVector vvY = CreateValueVector(arg1); int size = vvX.Size; if (size == 0 || vvY.Size != size) { return ErrorEval.NA; } result = EvaluateInternal(vvX, vvY, size); } catch (EvaluationException e) { return e.GetErrorEval(); } if (Double.IsNaN(result) || Double.IsInfinity(result)) { return ErrorEval.NUM_ERROR; } return new NumberEval(result); } /** * Constructs a new instance of the Accumulator used to calculated this function */ public abstract Accumulator CreateAccumulator(); private double EvaluateInternal(ValueVector x, ValueVector y, int size) { Accumulator acc = CreateAccumulator(); // error handling is as if the x is fully evaluated before y ErrorEval firstXerr = null; ErrorEval firstYerr = null; bool accumlatedSome = false; double result = 0.0; for (int i = 0; i < size; i++) { ValueEval vx = x.GetItem(i); ValueEval vy = y.GetItem(i); if (vx is ErrorEval) { if (firstXerr == null) { firstXerr = (ErrorEval)vx; continue; } } if (vy is ErrorEval) { if (firstYerr == null) { firstYerr = (ErrorEval)vy; continue; } } // only count pairs if both elements are numbers if (vx is NumberEval && vy is NumberEval) { accumlatedSome = true; NumberEval nx = (NumberEval)vx; NumberEval ny = (NumberEval)vy; result += acc.Accumulate(nx.NumberValue, ny.NumberValue); } else { // all other combinations of value types are silently ignored } } if (firstXerr != null) { throw new EvaluationException(firstXerr); } if (firstYerr != null) { throw new EvaluationException(firstYerr); } if (!accumlatedSome) { throw new EvaluationException(ErrorEval.DIV_ZERO); } return result; } private static double[] TrimToSize(double[] arr, int len) { double[] tarr = arr; if (arr.Length > len) { tarr = new double[len]; Array.Copy(arr, 0, tarr, 0, len); } return tarr; } private static bool IsNumberEval(Eval eval) { bool retval = false; if (eval is NumberEval) { retval = true; } else if (eval is RefEval) { RefEval re = (RefEval)eval; ValueEval ve = re.InnerValueEval; retval = (ve is NumberEval); } return retval; } private static double GetDoubleValue(Eval eval) { double retval = 0; if (eval is NumberEval) { NumberEval ne = (NumberEval)eval; retval = ne.NumberValue; } else if (eval is RefEval) { RefEval re = (RefEval)eval; ValueEval ve = re.InnerValueEval; retval = (ve is NumberEval) ? ((NumberEval)ve).NumberValue : double.NaN; } else if (eval is ErrorEval) { retval = double.NaN; } return retval; } private static ValueVector CreateValueVector(ValueEval arg) { if (arg is ErrorEval) { throw new EvaluationException((ErrorEval)arg); } if (arg is TwoDEval) { return new AreaValueArray((TwoDEval)arg); } if (arg is RefEval) { return new RefValueArray((RefEval)arg); } return new SingleCellValueArray(arg); } } }