diff --git a/poi/src/main/java/org/apache/poi/ss/formula/functions/LookupUtils.java b/poi/src/main/java/org/apache/poi/ss/formula/functions/LookupUtils.java index 7ba7b18a11..43ad217a5b 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/functions/LookupUtils.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/functions/LookupUtils.java @@ -625,8 +625,10 @@ public final class LookupUtils { public static int xlookupIndexOfValue(ValueEval lookupValue, ValueVector vector, MatchMode matchMode, SearchMode searchMode) throws EvaluationException { LookupValueComparer lookupComparer = createTolerantLookupComparer(lookupValue, matchMode != MatchMode.WildcardMatch, true); int result; - if (searchMode == SearchMode.BinarySearchForward || searchMode == SearchMode.BinarySearchBackward) { - result = binarySearchIndexOfValue(lookupComparer, vector, matchMode); + if (searchMode == SearchMode.BinarySearchForward) { + result = binarySearchIndexOfValue(lookupComparer, vector, matchMode, false); + } else if (searchMode == SearchMode.BinarySearchBackward) { + result = binarySearchIndexOfValue(lookupComparer, vector, matchMode, true); } else if (searchMode == SearchMode.IterateBackward) { result = lookupLastIndexOfValue(lookupComparer, vector, matchMode); } else { @@ -711,7 +713,7 @@ public final class LookupUtils { } private static int binarySearchIndexOfValue(LookupValueComparer lookupComparer, ValueVector vector, - MatchMode matchMode) { + MatchMode matchMode, boolean reverse) { int bestMatchIdx = -1; ValueEval bestMatchEval = null; HashSet alreadySearched = new HashSet<>(); @@ -758,7 +760,9 @@ public final class LookupUtils { break; } if (result.isTypeMismatch()) { - handleMidValueTypeMismatch(lookupComparer, vector, bsi, i); + handleMidValueTypeMismatch(lookupComparer, vector, bsi, i, reverse); + } else if (reverse) { + bsi.narrowSearch(i, result.isGreaterThan()); } else { bsi.narrowSearch(i, result.isLessThan()); } @@ -820,7 +824,7 @@ public final class LookupUtils { } CompareResult cr = lookupComparer.compareTo(vector.getItem(midIx)); if(cr.isTypeMismatch()) { - int newMidIx = handleMidValueTypeMismatch(lookupComparer, vector, bsi, midIx); + int newMidIx = handleMidValueTypeMismatch(lookupComparer, vector, bsi, midIx, false); if(newMidIx < 0) { continue; } @@ -837,11 +841,12 @@ public final class LookupUtils { * 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) + * @param reverse the data is sorted in reverse order * @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) { + BinarySearchIndexes bsi, int midIx, boolean reverse) { int newMid = midIx; int highIx = bsi.getHighIx(); @@ -854,7 +859,13 @@ public final class LookupUtils { return -1; } CompareResult cr = lookupComparer.compareTo(vector.getItem(newMid)); - if(cr.isLessThan() && newMid == highIx-1) { + if(cr.isLessThan() && !reverse && 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. + } else if(cr.isGreaterThan() && reverse && newMid == highIx-1) { // move highIx down to the low end of the mid values bsi.narrowSearch(midIx, true); return -1; @@ -871,7 +882,11 @@ public final class LookupUtils { // Note - if moving highIx down (due to lookup