MATCH Problems with Int/Float Compare and Wildcards (#3142)

* MATCH Problems with Int/Float Compare and Wildcards

Fix #3141. Function matchSmallestValue did not recognize that an integer could match a float. Adding test cases, it seems that matchFirstValue had the same problem. However, matchLargestValue seemed to handle things correctly - but see below.

In addition, the wildcard logic in matchFirstValue is faulty. It ignored tilde as a wildcard character. Although it would have been easy to just add that, I think it was wrong to determine on its own if a wildcard was in use. Just using the already available wildcard functions whenever comparing two strings is sufficient.

I note that Excel doesn't seem to follow its own rules for MATCH (https://support.microsoft.com/en-us/office/match-function-e8dffd45-c762-47d6-bf89-533f4a37673a?ns=excel&version=90&syslcid=1033&uilcid=1033&appver=zxl900&helpid=xlmain11.chm60112&ui=en-us&rs=en-us&ad=us). PhpSpreadsheet's results match Excel's, so no problem. However, when match_type is not zero, the match array is supposed to be sorted, so I would expect `#N/A` when it isn't; but that's not how Excel operates. I have no idea what Excel is doing. If `MATCH(2,{2,0,4,3},1)` isn't `#N/A` because of the unsorted array, then surely it should be `1` (item 1 of the array is the largest number less than or equal to the lookup value); but Excel and PhpSpreadsheet (before and after changes) return `2`. I have moved this example to be the first of the test cases.

One would think strings would behave similarly. But, no - see the second test case. This time Excel does look for an exact match. But the existing logic doesn't get the matching result in PhpSpreadsheet. It requires a whole new block of code, one which doesn't work correctly for numeric lookup value. Ugh.

LibreOffice doesn't always agree with Excel. It seems that it will use wildcard matching even when the match type is not zero (Excel documentation says wildcards are only for type zero, which is just as well because I don't really know what greater/less mean when wildcards are involved). I have not attempted to duplicate this behavior. For the record, Gnumeric agrees with Excel here.

* More Changes - LibreOffice

Add support for LibreOffice matching wildcard strings when type is not zero. Add support for type to be specified as integer other than 0/1/-1, or as float, or as numeric string; non-numeric string should case `#VALUE!` error.

I have found an example of undefined behavior (unsorted array where type is non-zero) where PhpSpreadsheet does not produce the same result as Excel. It is present as a new `incomplete` test case. I can fix it, but not without breaking other tests where the proper behavior is undefined. IMO, this is not a problem we should be concerned about.

Many test cases are added. Chances are I will add some more before merging this change.
This commit is contained in:
oleibman
2022-11-04 15:51:00 -07:00
committed by GitHub
parent 47c00d0599
commit fac0e46c91
4 changed files with 354 additions and 126 deletions
-64
View File
@@ -312,70 +312,6 @@ parameters:
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Address\\:\\:sheetName\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchFirstValue\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchFirstValue\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchFirstValue\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchLargestValue\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchLargestValue\\(\\) has parameter \\$keySet with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchLargestValue\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchLargestValue\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchSmallestValue\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchSmallestValue\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchSmallestValue\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:prepareLookupArray\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:prepareLookupArray\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:prepareLookupArray\\(\\) has parameter \\$matchType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:validateLookupArray\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:validateLookupValue\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:validateMatchType\\(\\) has parameter \\$matchType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Lookup\\:\\:verifyResultVector\\(\\) has no return type specified\\.$#"
count: 1
@@ -39,12 +39,11 @@ class ExcelMatch
}
$lookupArray = Functions::flattenArray($lookupArray);
$matchType = (int) ($matchType ?? self::MATCHTYPE_LARGEST_VALUE);
try {
// Input validation
self::validateLookupValue($lookupValue);
self::validateMatchType($matchType);
$matchType = self::validateMatchType($matchType);
self::validateLookupArray($lookupArray);
$keySet = array_keys($lookupArray);
@@ -87,36 +86,78 @@ class ExcelMatch
return ExcelError::NA();
}
private static function matchFirstValue($lookupArray, $lookupValue)
/**
* @param mixed $lookupValue
*
* @return mixed
*/
private static function matchFirstValue(array $lookupArray, $lookupValue)
{
$wildcardLookup = ((bool) preg_match('/([\?\*])/', $lookupValue));
$wildcard = WildcardMatch::wildcard($lookupValue);
if (is_string($lookupValue)) {
$valueIsString = true;
$wildcard = WildcardMatch::wildcard($lookupValue);
} else {
$valueIsString = false;
$wildcard = '';
}
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
foreach ($lookupArray as $i => $lookupArrayValue) {
$typeMatch = ((gettype($lookupValue) === gettype($lookupArrayValue)) ||
(is_numeric($lookupValue) && is_numeric($lookupArrayValue)));
if (
$typeMatch && is_string($lookupValue) &&
$wildcardLookup && WildcardMatch::compare($lookupArrayValue, $wildcard)
$valueIsString
&& is_string($lookupArrayValue)
) {
// wildcard match
return $i;
} elseif ($lookupArrayValue === $lookupValue) {
// exact match
return $i;
if (WildcardMatch::compare($lookupArrayValue, $wildcard)) {
return $i; // wildcard match
}
} else {
if ($lookupArrayValue === $lookupValue) {
return $i; // exact match
}
if (
$valueIsNumeric
&& (is_float($lookupArrayValue) || is_int($lookupArrayValue))
&& $lookupArrayValue == $lookupValue
) {
return $i; // exact match
}
}
}
return null;
}
private static function matchLargestValue($lookupArray, $lookupValue, $keySet)
/**
* @param mixed $lookupValue
*
* @return mixed
*/
private static function matchLargestValue(array $lookupArray, $lookupValue, array $keySet)
{
if (is_string($lookupValue)) {
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
$wildcard = WildcardMatch::wildcard($lookupValue);
foreach (array_reverse($lookupArray) as $i => $lookupArrayValue) {
if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
return $i;
}
}
} else {
foreach ($lookupArray as $i => $lookupArrayValue) {
if ($lookupArrayValue === $lookupValue) {
return $keySet[$i];
}
}
}
}
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
foreach ($lookupArray as $i => $lookupArrayValue) {
$typeMatch = ((gettype($lookupValue) === gettype($lookupArrayValue)) ||
(is_numeric($lookupValue) && is_numeric($lookupArrayValue)));
if ($valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue))) {
if ($lookupArrayValue <= $lookupValue) {
return array_search($i, $keySet);
}
}
$typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
if ($typeMatch && ($lookupArrayValue <= $lookupValue)) {
return array_search($i, $keySet);
}
@@ -125,21 +166,42 @@ class ExcelMatch
return null;
}
private static function matchSmallestValue($lookupArray, $lookupValue)
/**
* @param mixed $lookupValue
*
* @return mixed
*/
private static function matchSmallestValue(array $lookupArray, $lookupValue)
{
$valueKey = null;
if (is_string($lookupValue)) {
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
$wildcard = WildcardMatch::wildcard($lookupValue);
foreach ($lookupArray as $i => $lookupArrayValue) {
if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
return $i;
}
}
}
}
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
// The basic algorithm is:
// Iterate and keep the highest match until the next element is smaller than the searched value.
// Return immediately if perfect match is found
foreach ($lookupArray as $i => $lookupArrayValue) {
$typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
$bothNumeric = $valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue));
if ($lookupArrayValue === $lookupValue) {
// Another "special" case. If a perfect match is found,
// the algorithm gives up immediately
return $i;
} elseif ($typeMatch && $lookupArrayValue >= $lookupValue) {
}
if ($bothNumeric && $lookupValue == $lookupArrayValue) {
return $i; // exact match, as above
}
if (($typeMatch || $bothNumeric) && $lookupArrayValue >= $lookupValue) {
$valueKey = $i;
} elseif ($typeMatch && $lookupArrayValue < $lookupValue) {
//Excel algorithm gives up immediately if the first element is smaller than the searched value
@@ -150,6 +212,9 @@ class ExcelMatch
return $valueKey;
}
/**
* @param mixed $lookupValue
*/
private static function validateLookupValue($lookupValue): void
{
// Lookup_value type has to be number, text, or logical values
@@ -158,18 +223,29 @@ class ExcelMatch
}
}
private static function validateMatchType($matchType): void
/**
* @param mixed $matchType
*/
private static function validateMatchType($matchType): int
{
// Match_type is 0, 1 or -1
if (
($matchType !== self::MATCHTYPE_FIRST_VALUE) &&
($matchType !== self::MATCHTYPE_LARGEST_VALUE) && ($matchType !== self::MATCHTYPE_SMALLEST_VALUE)
) {
throw new Exception(ExcelError::NA());
// However Excel accepts other numeric values,
// including numeric strings and floats.
// It seems to just be interested in the sign.
if (!is_numeric($matchType)) {
throw new Exception(ExcelError::Value());
}
if ($matchType > 0) {
return self::MATCHTYPE_LARGEST_VALUE;
}
if ($matchType < 0) {
return self::MATCHTYPE_SMALLEST_VALUE;
}
return self::MATCHTYPE_FIRST_VALUE;
}
private static function validateLookupArray($lookupArray): void
private static function validateLookupArray(array $lookupArray): void
{
// Lookup_array should not be empty
$lookupArraySize = count($lookupArray);
@@ -178,7 +254,10 @@ class ExcelMatch
}
}
private static function prepareLookupArray($lookupArray, $matchType)
/**
* @param mixed $matchType
*/
private static function prepareLookupArray(array $lookupArray, $matchType): array
{
// Lookup_array should contain only number, text, or logical values, or empty (null) cells
foreach ($lookupArray as $i => $value) {
@@ -3,25 +3,77 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PHPUnit\Framework\TestCase;
class MatchTest extends TestCase
class MatchTest extends AllSetupTeardown
{
protected function setUp(): void
/**
* @dataProvider providerMATCH
*
* @param mixed $expectedResult
* @param mixed $input
* @param mixed $type
*/
public function testMATCH($expectedResult, $input, array $array, $type = null): void
{
Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
if (is_array($expectedResult)) {
$expectedResult = $expectedResult[0];
}
if ($expectedResult === 'incomplete') {
self::markTestIncomplete('Undefined behavior with different results in Excel and PhpSpreadsheet');
}
$this->mightHaveException($expectedResult);
$sheet = $this->getSheet();
$maxRow = count($array);
$formulaArray = '';
for ($row = 1; $row <= $maxRow; ++$row) {
$this->setCell("A$row", $array[$row - 1]);
$formulaArray = "A1:A$row";
}
$this->setCell('B1', $input);
if ($type === null) {
$formula = "=MATCH(B1,$formulaArray)";
} else {
$formula = "=MATCH(B1, $formulaArray, $type)";
}
$sheet->getCell('D1')->setValue($formula);
$result = $sheet->getCell('D1')->getCalculatedValue();
self::assertEquals($expectedResult, $result);
}
/**
* @dataProvider providerMATCH
*
* @param mixed $expectedResult
* @param mixed $input
* @param mixed $type
*/
public function testMATCH($expectedResult, ...$args): void
public function testMATCHLibre($expectedResult, $input, array $array, $type = null): void
{
$result = LookupRef::MATCH(...$args);
$this->setOpenOffice();
if (is_array($expectedResult)) {
$expectedResult = $expectedResult[1];
}
if ($expectedResult === 'incomplete') {
self::markTestIncomplete('Undefined behavior with different results in Excel and PhpSpreadsheet');
}
$this->mightHaveException($expectedResult);
$sheet = $this->getSheet();
$maxRow = count($array);
$formulaArray = '';
for ($row = 1; $row <= $maxRow; ++$row) {
$this->setCell("A$row", $array[$row - 1]);
$formulaArray = "A1:A$row";
}
$this->setCell('B1', $input);
if ($type === null) {
$formula = "=MATCH(B1,$formulaArray)";
} else {
$formula = "=MATCH(B1, $formulaArray, $type)";
}
$sheet->getCell('D1')->setValue($formula);
$result = $sheet->getCell('D1')->getCalculatedValue();
self::assertEquals($expectedResult, $result);
}
+186 -25
View File
@@ -1,6 +1,18 @@
<?php
return [
'unsorted numeric array still finds match with type 1?' => [
2, // Expected
2, // Input
[2, 0, 4, 3],
1,
],
'unsorted array still finds match with type 1?' => [
6,
'Amplitude',
['Aardvark', 'Apple', 'A~*e', 'A*e', 'A[solve', 'Amplitude', 'Adverse', 'Apartment'],
1,
],
// Third argument = 0
[
1, // Expected
@@ -33,12 +45,6 @@ return [
[2, 3, 4, 3],
1,
],
[
2, // Expected
2, // Input
[2, 0, 4, 3],
1,
],
[
3, // Expected
2, // Input
@@ -114,10 +120,10 @@ return [
1,
],
// 0s are causing errors, because things like 0 == 'x' is true. Thanks PHP!
[
'0 does not match x' => [
3,
'x',
[[0], [0], ['x'], ['x'], ['x']],
[0, 0, 'x', 'x', 'x'],
0,
],
[
@@ -201,12 +207,42 @@ return [
[true, false, 'a', 'z', 2, 888],
-1,
],
[
'number compared to string type -1' => [
'#N/A', // Expected
6,
['6'],
-1,
],
'number compared to string type 0' => [
'#N/A', // Expected
6,
['6'],
0,
],
'number compared to string type 1' => [
'#N/A', // Expected
6,
['6'],
1,
],
'string compared to number type -1' => [
'#N/A', // Expected
'6',
[6],
-1,
],
'string compared to number type 0' => [
'#N/A', // Expected
'6',
[6],
0,
],
'string compared to number type 1' => [
'#N/A', // Expected
'6',
[6],
1,
],
// expression match
[
2, // Expected
@@ -286,20 +322,38 @@ return [
[1, 22, 'aaa'],
1,
],
[
'#N/A', // Expected (Invalid match type)
'int match type other than 0/1/-1 is accepted' => [
3, // Expected (Invalid match type)
'abc',
[1, 22, 'aaa'],
123,
],
[
'float match type is accepted' => [
3, // Expected (Invalid match type)
'abc',
[1, 22, 'aaa'],
123.5,
],
'numeric string match type is accepted' => [
3, // Expected (Invalid match type)
'abc',
[1, 22, 'aaa'],
'89.7',
],
'non-numeric string match type is not accepted' => [
'#VALUE!', // Expected (Invalid match type)
'abc',
[1, 22, 'aaa'],
'"x"',
],
'empty lookup array' => [
'#N/A', // Expected (Empty lookup array)
'abc',
[],
1,
],
[
8,
'wildcard match 1 with type -1' => [
[8, 2], // LibreOffice matches wildcard but shouldn't
'A*e',
['Aardvark', 'Apple', 'Armadillo', 'Acre', 'Absolve', 'Amplitude', 'Adverse', 'Apartment'],
-1,
@@ -310,14 +364,22 @@ return [
['Aardvark', 'Apple', 'Armadillo', 'Acre', 'Absolve', 'Amplitude', 'Adverse', 'Apartment'],
0,
],
[
'#N/A',
'A*e',
['Aardvark', 'Apple', 'Armadillo', 'Acre', 'Absolve', 'Amplitude', 'Adverse', 'Apartment'],
1,
'wildcard match with tilde' => [
4,
'A~*e',
['Aardvark', 'Apple', 'A~*e', 'A*e', 'Absolve', 'Amplitude', 'Adverse', 'Apartment'],
0,
],
[
8,
'string with preg_quote escaped character' => [
5,
'A[solve',
['Aardvark', 'Apple', 'A~*e', 'A*e', 'A[solve', 'Amplitude', 'Adverse', 'Apartment'],
0,
],
// duplicate test deleted - see 'wildcard with type 1' below
// end of deletions
'wildcard match 2 with type -1' => [
[8, 5],
'A?s*e',
['Aardvark', 'Apple', 'Armadillo', 'Acre', 'Absolve', 'Amplitude', 'Adverse', 'Apartment'],
-1,
@@ -328,14 +390,14 @@ return [
['Aardvark', 'Apple', 'Armadillo', 'Acre', 'Absolve', 'Amplitude', 'Adverse', 'Apartment'],
0,
],
[
'#N/A',
'wildcard with type 1' => [
['#N/A', 2], // LibreOffice matches wildcard but shouldn't
'A*e',
['Aardvark', 'Apple', 'Armadillo', 'Acre', 'Absolve', 'Amplitude', 'Adverse', 'Apartment'],
1,
],
[
8,
'wildcard match 3 with type -1' => [
[8, 3], // LibreOffice matches wildcard but shouldn't
'*verse',
['Obtuse', 'Amuse', 'Obverse', 'Inverse', 'Assurance', 'Amplitude', 'Adverse', 'Apartment'],
-1,
@@ -358,4 +420,103 @@ return [
['abc123fff', 'abc/123fff'],
0,
],
'float lookup int array type0' => [
1, // Expected
2.0, // Input
[2, 3, 4, 5],
0,
],
'int lookup float array type0' => [
2, // Expected
3, // Input
[2, 3.0, 4, 5],
0,
],
'int lookup float array type0 not equal' => [
'#N/A', // Expected
3, // Input
[2, 3.1, 4, 5],
0,
],
'float lookup int array type0 not equal' => [
'#N/A', // Expected
3.1, // Input
[2, 3, 4, 5],
0,
],
'float lookup int array type1 equal' => [
1, // Expected
2.0, // Input
[2, 3, 4, 5],
1,
],
'int lookup float array type1 equal' => [
2, // Expected
3, // Input
[2, 3.0, 4, 5],
1,
],
'float lookup int array type1 less' => [
1, // Expected
2.5, // Input
[2, 3, 4, 5],
1,
],
'int lookup float array type1 less' => [
2, // Expected
3, // Input
[2, 2.9, 4, 5],
1,
],
'float lookup int array type -1 equal' => [
4, // Expected
2.0, // Input
[5, 4, 3, 2],
-1,
],
'int lookup float array type -1 equal' => [
3, // Expected
3, // Input
[5, 4, 3.0, 2],
-1,
],
'float lookup int array type -1 greater' => [
2, // Expected
3.5, // Input
[5, 4, 3, 2],
-1,
],
'int lookup float array type -1 greater' => [
2, // Expected
3, // Input
[5, 4, 2.9, 2],
-1,
],
'default type is type1' => [
3, // Expected
4, // Input
[1, 2, 3, 5],
],
'same as previous but explicit type1' => [
3, // Expected
4, // Input
[1, 2, 3, 5],
],
'same as previous but type0 so different result' => [
'#N/A', // Expected
4, // Input
[1, 2, 3, 5],
0,
],
'undefined behavior N/A in Excel 4 in PhpSpreadsheet' => [
'incomplete', // Expected
4, // Input
[8, 6, 3, 1],
],
'same as previous but type -1 so match' => [
2, // Expected
4, // Input
[8, 6, 3, 1],
-1,
],
];