nfTlWNl rn Ѻq$³*%yNk9S maq ƿfX5O&B CӝINg?3~)?5_.zHe˒{~ =-f06+ꓻ=m'Y7v 3IR.\S skZM^_2YZL_v xV8UUVWspg-DH$ҀA\on6$Mýy#|-4 _I'[LǡtGaMꜮFl&Z2,byFZq ݿu نNŇ̻ #qO\H5ty]T],  뜷aY}yǚ@‚M.Pׅ2=̙Ak":m;w"@~0Uv)st{:.2faLכݽ`V*J]qC8p0Ydi@)sV0% Ʈi+D1ၤ_4ȕ,Z!j'V"ptHVO}nASȎ݇@Vٺ@.r0i7o[B,MLDYhs 5-X pX7<"[ }D:vQUx6Y%s฼_nS%~qyR^C >y<4iD#hdW6%_tdL OJu\.wvs~,}R9[OX`NXXx)ي!SGVEvInOz\Zw}r!VUn P\aW-rg=1kZIsj}͈FVsvv _Й4EШɽ` eWn kMQ3N;4`؃˅.<1xE4u Ot1G܈rhӠ2*Plm6ڃBQ`®yFlk, bIlYG ލ%#K'k$.TUXbˏē"? $GT ԔT(/jǹ12ۻNnT֓ӭnv{OsZ9%_A7̼V.i2~VHnNj!LdZ=!:y8O.R+Fk9a ξC:z2lq<|voN⍡kmLzU!oM"^I8><@ ^9HW;/\5''5FDyJ@iNzls?yiֵZ듙kӐ=5)aPM WDҔ2KUSs=ߌckWZ/p(U'ZYQLW٤岥=7 0ʎz*4UbS~c49 >, 3e5Ez~d{eǂ\(ၿod/ =Q3Rap#\5Sc9:8pZxy.oi_-[mi#.SB35p*#L#8`%6D1l6o (2(VdѳDž8C!Nݪ+Ӑj J~4 )mm#6=Y=%Hge (#{Ӌ[~ \ !c|כ z޾!o+loU XLiQ\VF\Nų LN Fo^2izg|ؔ_ehuҋIl9=m8z#b?BhYow5+zp)[>Oެ>Uu(KCȹljrA|`cR])62=TJ8Q$YOh2B^E$5'h%1uZpȆdt9Krb@TKq[QÇ!q+g UHj~Dm& ]z#՝~sTG/0 ORܧ$2 NÌwoyTCLU-oU giRXՈlϦYu #Z]>XtFIf4k2ӓl)0eiePnΤZndFB9h*T5™T mϺqO69HQɟ*C;[8HƁۍϘ-ԣw>Z\s. Na)e$R/cUYM~^~ȸk2$3ـ s 8`{/Hal7?(!w_Wy/JJGee{ʩ f.q6nj͎7t%3-Qq"6-j(m\+Z/p(U'ZYQLW٤岥=7 0ʎz*4UbS~c49 >, 3e5Ez~d{eǂ\(ၿod/ =Q3Rap#DkBdg)G;ԕ)zF־Ngf\Ƶ t;fS ļe`9چj0 Ɯ32XJBAegZTyRr:xmhZ0"&FyB`c-عhՁu^ uRwqFo|ckű- # h4m Xpӫ[ZmLC$ TStymrMNXjK &y0a-gMWx6}3M3%Ad$>nq}p %+-nh\`]{Ws~ 5<=O/Vxd`MBBm[O,hs*ra1F=ʁrO2QSh5wOŠJ8v Cbg/wÀv" PB|)Cj'2Tt}vcBUVIDl5.ņ-&_s te(^)8S #oVdksgLɐbkλW0հ'^; pg =]%'֨PI*ޝkvlۄIrvQJfɪu!#ó *^Tn9'U-(4c6LeؼU|N[ż6bYZ>{'px҅6(g"*grzcJ&_gw/+WFa~P ?WD ř-K:ny7u^d@44jpt! ^`zҰp}X rJ{Zl`T(6j4}d?oZ&h1Z0אe=ECkجV&[TClݘP5@Z@Gqg4t:kf49 M_6YyuömeI=4xF{oay(Wވl=Go } elseif (count($args) === 2) { return self::COUNTIF(...$args); } $database = self::buildDatabase(...$args); $conditions = self::buildConditionSet(...$args); return DCount::evaluate($database, null, $conditions, false); } /** * MAXIFS. * * Returns the maximum value within a range of cells that contain numbers within the list of arguments * * Excel Function: * MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2]…) * * @param mixed $args Pairs of Ranges and Criteria * * @return null|float|string */ public static function MAXIFS(...$args) { if (empty($args)) { return 0.0; } $conditions = self::buildConditionSetForValueRange(...$args); $database = self::buildDatabaseWithValueRange(...$args); return DMax::evaluate($database, self::VALUE_COLUMN_NAME, $conditions, false); } /** * MINIFS. * * Returns the minimum value within a range of cells that contain numbers within the list of arguments * * Excel Function: * MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2]…) * * @param mixed $args Pairs of Ranges and Criteria * * @return null|float|string */ public static function MINIFS(...$args) { if (empty($args)) { return 0.0; } $conditions = self::buildConditionSetForValueRange(...$args); $database = self::buildDatabaseWithValueRange(...$args); return DMin::evaluate($database, self::VALUE_COLUMN_NAME, $conditions, false); } /** * SUMIF. * * Totals the values of cells that contain numbers within the list of arguments * * Excel Function: * SUMIF(range, criteria, [sum_range]) * * @param mixed $range Data values * @param mixed $sumRange * @param mixed $condition * * @return null|float|string */ public static function SUMIF($range, $condition, $sumRange = []) { $database = self::databaseFromRangeAndValue($range, $sumRange); $condition = [[self::CONDITION_COLUMN_NAME, self::VALUE_COLUMN_NAME], [$condition, null]]; return DSum::evaluate($database, self::VALUE_COLUMN_NAME, $condition); } /** * SUMIFS. * * Counts the number of cells that contain numbers within the list of arguments * * Excel Function: * SUMIFS(average_range, criteria_range1, criteria1, [criteria_range2, criteria2]…) * * @param mixed $args Pairs of Ranges and Criteria * * @return null|float|string */ public static function SUMIFS(...$args) { if (empty($args)) { return 0.0; } elseif (count($args) === 3) { return self::SUMIF($args[1], $args[2], $args[0]); } $conditions = self::buildConditionSetForValueRange(...$args); $database = self::buildDatabaseWithValueRange(...$args); return DSum::evaluate($database, self::VALUE_COLUMN_NAME, $conditions); } /** @param array $args */ private static function buildConditionSet(...$args): array { $conditions = self::buildConditions(1, ...$args); // Scrutinizer thinks first parameter of array_map can't be null. It is wrong. return array_map(/** @scrutinizer ignore-type */ null, ...$conditions); } /** @param array $args */ private static function buildConditionSetForValueRange(...$args): array { $conditions = self::buildConditions(2, ...$args); if (count($conditions) === 1) { return array_map( function ($value) { return [$value]; }, $conditions[0] ); } return array_map(/** @scrutinizer ignore-type */ null, ...$conditions); } /** @param array $args */ private static function buildConditions(int $startOffset, ...$args): array { $conditions = []; $pairCount = 1; $argumentCount = count($args); for ($argument = $startOffset; $argument < $argumentCount; $argument += 2) { $conditions[] = array_merge([sprintf(self::CONDITIONAL_COLUMN_NAME, $pairCount)], [$args[$argument]]); ++$pairCount; } return $conditions; } /** @param array $args */ private static function buildDatabase(...$args): array { $database = []; return self::buildDataSet(0, $database, ...$args); } /** @param array $args */ private static function buildDatabaseWithValueRange(...$args): array { $database = []; $database[] = array_merge( [self::VALUE_COLUMN_NAME], Functions::flattenArray($args[0]) ); return self::buildDataSet(1, $database, ...$args); } /** @param array $args */ private static function buildDataSet(int $startOffset, array $database, ...$args): array { $pairCount = 1; $argumentCount = count($args); for ($argument = $startOffset; $argument < $argumentCount; $argument += 2) { $database[] = array_merge( [sprintf(self::CONDITIONAL_COLUMN_NAME, $pairCount)], Functions::flattenArray($args[$argument]) ); ++$pairCount; } return array_map(/** @scrutinizer ignore-type */ null, ...$database); } private static function databaseFromRangeAndValue(array $range, array $valueRange = []): array { $range = Functions::flattenArray($range); $valueRange = Functions::flattenArray($valueRange); if (empty($valueRange)) { $valueRange = $range; } $database = array_map(/** @scrutinizer ignore-type */ null, array_merge([self::CONDITION_COLUMN_NAME], $range), array_merge([self::VALUE_COLUMN_NAME], $valueRange)); return $database; } }