/** * Excel has funny behaviour when the some elements in the search vector are the wrong type. * */ private static int PerformBinarySearch(ValueVector vector, LookupValueComparer lookupComparer) { // both low and high indexes point to values assumed too low and too high. BinarySearchIndexes bsi = new BinarySearchIndexes(vector.Size); while (true) { int midIx = bsi.GetMidIx(); if (midIx < 0) { return(bsi.GetLowIx()); } CompareResult cr = lookupComparer.CompareTo(vector.GetItem(midIx)); if (cr.IsTypeMismatch) { int newMidIx = HandleMidValueTypeMismatch(lookupComparer, vector, bsi, midIx); if (newMidIx < 0) { continue; } midIx = newMidIx; cr = lookupComparer.CompareTo(vector.GetItem(midIx)); } if (cr.IsEqual) { return(FindLastIndexInRunOfEqualValues(lookupComparer, vector, midIx, bsi.GetHighIx())); } bsi.narrowSearch(midIx, cr.IsLessThan); } }
/** * @return zero based index */ private static int FindIndexOfValue(ValueEval lookupValue, ValueVector lookupRange, bool matchExact, bool FindLargestLessThanOrEqual) { LookupValueComparer lookupComparer = CreateLookupComparer(lookupValue, matchExact); int size = lookupRange.Size; if (matchExact) { for (int i = 0; i < size; i++) { if (lookupComparer.CompareTo(lookupRange.GetItem(i)).IsEqual) { return(i); } } throw new EvaluationException(ErrorEval.NA); } if (FindLargestLessThanOrEqual) { // Note - backward iteration for (int i = size - 1; i >= 0; i--) { CompareResult cmp = lookupComparer.CompareTo(lookupRange.GetItem(i)); if (cmp.IsTypeMismatch) { continue; } if (!cmp.IsLessThan) { return(i); } } throw new EvaluationException(ErrorEval.NA); } // else - Find smallest greater than or equal to // TODO - Is binary search used for (match_type==+1) ? for (int i = 0; i < size; i++) { CompareResult cmp = lookupComparer.CompareTo(lookupRange.GetItem(i)); if (cmp.IsEqual) { return(i); } if (cmp.IsGreaterThan) { if (i < 1) { throw new EvaluationException(ErrorEval.NA); } return(i - 1); } } return(size - 1); }
public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { ValueEval arg3 = null; switch (args.Length) { case 4: arg3 = args[3]; // important: assumed array element Is never null break; case 3: break; default: // wrong number of arguments return(ErrorEval.VALUE_INVALID); } try { // Evaluation order: // arg0 lookup_value, arg1 table_array, arg3 range_lookup, Find lookup value, arg2 row_index, fetch result ValueEval lookupValue = OperandResolver.GetSingleValue(args[0], srcCellRow, srcCellCol); AreaEval tableArray = LookupUtils.ResolveTableArrayArg(args[1]); bool IsRangeLookup = LookupUtils.ResolveRangeLookupArg(arg3, srcCellRow, srcCellCol); int colIndex = LookupUtils.LookupIndexOfValue(lookupValue, LookupUtils.CreateRowVector(tableArray, 0), IsRangeLookup); int rowIndex = LookupUtils.ResolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol); ValueVector resultCol = CreateResultColumnVector(tableArray, rowIndex); return(resultCol.GetItem(colIndex)); } catch (EvaluationException e) { return(e.GetErrorEval()); } }
/** * Finds first (lowest index) exact occurrence of specified value. * @param lookupComparer the value to be found in column or row vector * @param vector the values to be searched. For VLOOKUP this Is the first column of the * tableArray. For HLOOKUP this Is the first row of the tableArray. * @return zero based index into the vector, -1 if value cannot be found */ private static int lookupFirstIndexOfValue(LookupValueComparer lookupComparer, ValueVector vector, MatchMode matchMode) { // Find first occurrence of lookup value int size = vector.Size; int bestMatchIdx = -1; ValueEval bestMatchEval = null; for (int i = 0; i < size; i++) { ValueEval valueEval = vector.GetItem(i); CompareResult result = lookupComparer.CompareTo(valueEval); if (result.IsEqual) { return(i); } switch (matchMode) { case MatchMode.ExactMatchFallbackToLargerValue: if (result.IsLessThan) { if (bestMatchEval == null) { bestMatchIdx = i; bestMatchEval = valueEval; } else { LookupValueComparer matchComparer = CreateTolerantLookupComparer(valueEval, true, true); if (matchComparer.CompareTo(bestMatchEval).IsLessThan) { bestMatchIdx = i; bestMatchEval = valueEval; } } } break; case MatchMode.ExactMatchFallbackToSmallerValue: if (result.IsGreaterThan) { if (bestMatchEval == null) { bestMatchIdx = i; bestMatchEval = valueEval; } else { LookupValueComparer matchComparer = CreateTolerantLookupComparer(valueEval, true, true); if (matchComparer.CompareTo(bestMatchEval).IsGreaterThan) { bestMatchIdx = i; bestMatchEval = valueEval; } } } break; } } return(bestMatchIdx); }
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); }
/** * Once the binary search has found a single match, (V/H)LOOKUP steps one by one over subsequent * values to choose the last matching item. */ private static int FindLastIndexInRunOfEqualValues(LookupValueComparer lookupComparer, ValueVector vector, int firstFoundIndex, int maxIx) { for (int i = firstFoundIndex + 1; i < maxIx; i++) { if (!lookupComparer.CompareTo(vector.GetItem(i)).IsEqual) { return(i - 1); } } return(maxIx - 1); }
/** * Finds first (lowest index) exact occurrence of specified value. * @param lookupValue the value to be found in column or row vector * @param vector the values to be searched. For VLOOKUP this Is the first column of the * tableArray. For HLOOKUP this Is the first row of the tableArray. * @return zero based index into the vector, -1 if value cannot be found */ private static int LookupIndexOfExactValue(LookupValueComparer lookupComparer, ValueVector vector) { // Find first occurrence of lookup value int size = vector.Size; for (int i = 0; i < size; i++) { if (lookupComparer.CompareTo(vector.GetItem(i)).IsEqual) { return(i); } } return(-1); }
public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval lookup_value, ValueEval table_array, ValueEval col_index, ValueEval range_lookup) { try { // Evaluation order: // arg0 lookup_value, arg1 table_array, arg3 range_lookup, find lookup value, arg2 col_index, fetch result ValueEval lookupValue = OperandResolver.GetSingleValue(lookup_value, srcRowIndex, srcColumnIndex); TwoDEval tableArray = LookupUtils.ResolveTableArrayArg(table_array); bool isRangeLookup = LookupUtils.ResolveRangeLookupArg(range_lookup, srcRowIndex, srcColumnIndex); int rowIndex = LookupUtils.LookupIndexOfValue(lookupValue, LookupUtils.CreateColumnVector(tableArray, 0), isRangeLookup); int colIndex = LookupUtils.ResolveRowOrColIndexArg(col_index, srcRowIndex, srcColumnIndex); ValueVector resultCol = CreateResultColumnVector(tableArray, colIndex); return(resultCol.GetItem(rowIndex)); } catch (EvaluationException e) { return(e.GetErrorEval()); } }
/** * Excel seems to handle mismatched types initially by just stepping 'mid' ix forward to the * first compatible value. * @param midIx 'mid' index (value which has the wrong type) * @return usually -1, signifying that the BinarySearchIndex has been narrowed to the new mid * index. Zero or greater signifies that an exact match for the lookup value was found */ private static int HandleMidValueTypeMismatch(LookupValueComparer lookupComparer, ValueVector vector, BinarySearchIndexes bsi, int midIx) { int newMid = midIx; int highIx = bsi.GetHighIx(); while (true) { newMid++; if (newMid == highIx) { // every element from midIx to highIx was the wrong type // move highIx down to the low end of the mid values bsi.narrowSearch(midIx, true); return(-1); } CompareResult cr = lookupComparer.CompareTo(vector.GetItem(newMid)); if (cr.IsLessThan && newMid == highIx - 1) { // move highIx down to the low end of the mid values bsi.narrowSearch(midIx, true); return(-1); // but only when "newMid == highIx-1"? slightly weird. // It would seem more efficient to always do this. } if (cr.IsTypeMismatch) { // keep stepping over values Until the right type Is found continue; } if (cr.IsEqual) { return(newMid); } // Note - if moving highIx down (due to lookup<vector[newMid]), // this execution path only moves highIx it down as far as newMid, not midIx, // which would be more efficient. bsi.narrowSearch(newMid, cr.IsLessThan); return(-1); } }
public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { switch (args.Length) { case 3: break; case 2: // complex rules to choose lookupVector and resultVector from the single area ref throw new Exception("Two arg version of LOOKUP not supported yet"); default: return(ErrorEval.VALUE_INVALID); } try { ValueEval lookupValue = OperandResolver.GetSingleValue(args[0], srcCellRow, srcCellCol); AreaEval aeLookupVector = LookupUtils.ResolveTableArrayArg(args[1]); AreaEval aeResultVector = LookupUtils.ResolveTableArrayArg(args[2]); ValueVector lookupVector = CreateVector(aeLookupVector); ValueVector resultVector = CreateVector(aeResultVector); if (lookupVector.Size > resultVector.Size) { // Excel seems to handle this by accessing past the end of the result vector. throw new Exception("Lookup vector and result vector of differing sizes not supported yet"); } int index = LookupUtils.LookupIndexOfValue(lookupValue, lookupVector, true); return(resultVector.GetItem(index)); } catch (EvaluationException e) { return(e.GetErrorEval()); } }
private double EvaluateInternal(ValueVector x, ValueVector y, int size) { // error handling is as if the x is fully Evaluated before y ErrorEval firstXerr = null; ErrorEval firstYerr = null; bool accumlatedSome = false; // first pass: read in data, compute xbar and ybar double sumx = 0.0, sumy = 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; sumx += nx.NumberValue; sumy += ny.NumberValue; } else { // all other combinations of value types are silently ignored } } double xbar = sumx / size; double ybar = sumy / size; // second pass: compute summary statistics double xxbar = 0.0, xybar = 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) { NumberEval nx = (NumberEval)vx; NumberEval ny = (NumberEval)vy; xxbar += (nx.NumberValue - xbar) * (nx.NumberValue - xbar); xybar += (nx.NumberValue - xbar) * (ny.NumberValue - ybar); } else { // all other combinations of value types are silently ignored } } double beta1 = xybar / xxbar; double beta0 = ybar - beta1 * xbar; if (firstXerr != null) { throw new EvaluationException(firstXerr); } if (firstYerr != null) { throw new EvaluationException(firstYerr); } if (!accumlatedSome) { throw new EvaluationException(ErrorEval.DIV_ZERO); } if (function == FUNCTION.INTERCEPT) { return(beta0); } else { return(beta1); } }
private double EvaluateInternal(ValueVector x, ValueVector y, int size) { // error handling is as if the x is fully Evaluated before y ErrorEval firstXerr = null; ErrorEval firstYerr = null; bool accumlatedSome = false; // first pass: read in data, compute xbar and ybar double sumx = 0.0, sumy = 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; sumx += nx.NumberValue; sumy += ny.NumberValue; } else { // all other combinations of value types are silently ignored } } double xbar = sumx / size; double ybar = sumy / size; // second pass: compute summary statistics double xxbar = 0.0, xybar = 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) { NumberEval nx = (NumberEval)vx; NumberEval ny = (NumberEval)vy; xxbar += (nx.NumberValue - xbar) * (nx.NumberValue - xbar); xybar += (nx.NumberValue - xbar) * (ny.NumberValue - ybar); } else { // all other combinations of value types are silently ignored } } double beta1 = xybar / xxbar; double beta0 = ybar - beta1 * xbar; if (firstXerr != null) { throw new EvaluationException(firstXerr); } if (firstYerr != null) { throw new EvaluationException(firstYerr); } if (!accumlatedSome) { throw new EvaluationException(ErrorEval.DIV_ZERO); } if (function == FUNCTION.INTERCEPT) { return beta0; } else { return beta1; } }
/** * Once the binary search has found a single match, (V/H)LOOKUP steps one by one over subsequent * values to choose the last matching item. */ private static int FindLastIndexInRunOfEqualValues(LookupValueComparer lookupComparer, ValueVector vector, int firstFoundIndex, int maxIx) { for (int i = firstFoundIndex + 1; i < maxIx; i++) { if (!lookupComparer.CompareTo(vector.GetItem(i)).IsEqual) { return i - 1; } } return maxIx - 1; }
/** * Excel seems to handle mismatched types initially by just stepping 'mid' ix forward to the * first compatible value. * @param midIx 'mid' index (value which has the wrong type) * @return usually -1, signifying that the BinarySearchIndex has been narrowed to the new mid * index. Zero or greater signifies that an exact match for the lookup value was found */ private static int HandleMidValueTypeMismatch(LookupValueComparer lookupComparer, ValueVector vector, BinarySearchIndexes bsi, int midIx) { int newMid = midIx; int highIx = bsi.GetHighIx(); while (true) { newMid++; if (newMid == highIx) { // every element from midIx to highIx was the wrong type // move highIx down to the low end of the mid values bsi.narrowSearch(midIx, true); return -1; } CompareResult cr = lookupComparer.CompareTo(vector.GetItem(newMid)); if (cr.IsLessThan && newMid == highIx - 1) { // move highIx down to the low end of the mid values bsi.narrowSearch(midIx, true); return -1; // but only when "newMid == highIx-1"? slightly weird. // It would seem more efficient to always do this. } if (cr.IsTypeMismatch) { // keep stepping over values Until the right type Is found continue; } if (cr.IsEqual) { return newMid; } // Note - if moving highIx down (due to lookup<vector[newMid]), // this execution path only moves highIx it down as far as newMid, not midIx, // which would be more efficient. bsi.narrowSearch(newMid, cr.IsLessThan); return -1; } }
/** * Excel has funny behaviour when the some elements in the search vector are the wrong type. * */ private static int PerformBinarySearch(ValueVector vector, LookupValueComparer lookupComparer) { // both low and high indexes point to values assumed too low and too high. BinarySearchIndexes bsi = new BinarySearchIndexes(vector.Size); while (true) { int midIx = bsi.GetMidIx(); if (midIx < 0) { return bsi.GetLowIx(); } CompareResult cr = lookupComparer.CompareTo(vector.GetItem(midIx)); if (cr.IsTypeMismatch) { int newMidIx = HandleMidValueTypeMismatch(lookupComparer, vector, bsi, midIx); if (newMidIx < 0) { continue; } midIx = newMidIx; cr = lookupComparer.CompareTo(vector.GetItem(midIx)); } if (cr.IsEqual) { return FindLastIndexInRunOfEqualValues(lookupComparer, vector, midIx, bsi.GetHighIx()); } bsi.narrowSearch(midIx, cr.IsLessThan); } }
/** * Finds first (lowest index) exact occurrence of specified value. * @param lookupValue the value to be found in column or row vector * @param vector the values to be searched. For VLOOKUP this Is the first column of the * tableArray. For HLOOKUP this Is the first row of the tableArray. * @return zero based index into the vector, -1 if value cannot be found */ private static int LookupIndexOfExactValue(LookupValueComparer lookupComparer, ValueVector vector) { // Find first occurrence of lookup value int size = vector.Size; for (int i = 0; i < size; i++) { if (lookupComparer.CompareTo(vector.GetItem(i)).IsEqual) { return i; } } return -1; }
/** * @return zero based index */ private static int FindIndexOfValue(ValueEval lookupValue, ValueVector lookupRange, bool matchExact, bool FindLargestLessThanOrEqual) { LookupValueComparer lookupComparer = CreateLookupComparer(lookupValue, matchExact); if (matchExact) { for (int i = 0; i < lookupRange.Size; i++) { if (lookupComparer.CompareTo(lookupRange.GetItem(i)).IsEqual) { return i; } } throw new EvaluationException(ErrorEval.NA); } if (FindLargestLessThanOrEqual) { // Note - backward iteration for (int i = lookupRange.Size - 1; i >= 0; i--) { CompareResult cmp = lookupComparer.CompareTo(lookupRange.GetItem(i)); if (cmp.IsTypeMismatch) { continue; } if (!cmp.IsLessThan) { return i; } } throw new EvaluationException(ErrorEval.NA); } // else - Find smallest greater than or equal to // TODO - Is binary search used for (match_type==+1) ? for (int i = 0; i < lookupRange.Size; i++) { CompareResult cmp = lookupComparer.CompareTo(lookupRange.GetItem(i)); if (cmp.IsEqual) { return i; } if (cmp.IsGreaterThan) { if (i < 1) { throw new EvaluationException(ErrorEval.NA); } return i - 1; } } throw new EvaluationException(ErrorEval.NA); }
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; }
/** * @return zero based index */ private static int FindIndexOfValue(ValueEval lookupValue, ValueVector lookupRange, bool matchExact, bool FindLargestLessThanOrEqual) { LookupValueComparer lookupComparer = CreateLookupComparer(lookupValue, matchExact); int size = lookupRange.Size; if (matchExact) { for (int i = 0; i < size; i++) { if (lookupComparer.CompareTo(lookupRange.GetItem(i)).IsEqual) { return(i); } } throw new EvaluationException(ErrorEval.NA); } if (FindLargestLessThanOrEqual) { //in case the lookupRange area is not sorted, still try to get the right index if (lookupValue is NumericValueEval nve) { Dictionary <int, double> dicDeltas = new Dictionary <int, double>(); for (int i = 0; i < size; i++) { var item = lookupRange.GetItem(i) as NumericValueEval; if (lookupComparer.CompareTo(item).IsEqual) { return(i); } else { dicDeltas.Add(i, item.NumberValue - nve.NumberValue); } } return(dicDeltas.Where(kv => kv.Value < 0).OrderByDescending(kv => kv.Value).First().Key); } // Note - backward iteration for (int i = size - 1; i >= 0; i--) { CompareResult cmp = lookupComparer.CompareTo(lookupRange.GetItem(i)); if (cmp.IsTypeMismatch) { continue; } if (!cmp.IsLessThan) { return(i); } } throw new EvaluationException(ErrorEval.NA); } // else - Find smallest greater than or equal to // TODO - Is binary search used for (match_type==+1) ? for (int i = 0; i < size; i++) { CompareResult cmp = lookupComparer.CompareTo(lookupRange.GetItem(i)); if (cmp.IsEqual) { return(i); } if (cmp.IsGreaterThan) { if (i < 1) { throw new EvaluationException(ErrorEval.NA); } return(i - 1); } } return(size - 1); }