From d1ee2435bcc2942ece5599c00a3990b9b78a0b6f Mon Sep 17 00:00:00 2001
From: Sara Arjona
Date: Tue, 11 Jan 2022 13:46:25 +0100
Subject: [PATCH 1/2] MDL-71720 lib: Upgrade PHPSpreadSheet to 1.21.0
---
lib/phpspreadsheet/vendor/autoload.php | 2 +-
.../vendor/composer/ClassLoader.php | 151 +-
.../vendor/composer/InstalledVersions.php | 634 ++--
.../vendor/composer/autoload_files.php | 67 -
.../vendor/composer/autoload_namespaces.php | 3 +-
.../vendor/composer/autoload_psr4.php | 3 -
.../vendor/composer/autoload_real.php | 28 +-
.../vendor/composer/autoload_static.php | 101 +-
.../vendor/composer/installed.json | 410 +--
.../vendor/composer/installed.php | 227 +-
.../vendor/composer/platform_check.php | 4 +-
.../vendor/markbaker/complex/README.md | 81 +-
.../markbaker/complex/classes/Autoloader.php | 53 -
.../markbaker/complex/classes/Bootstrap.php | 38 -
.../markbaker/complex/classes/src/Complex.php | 8 +-
.../complex/classes/src/Functions.php | 805 +++++
.../complex/classes/src/Operations.php | 210 ++
.../complex/classes/src/functions/abs.php | 29 -
.../complex/classes/src/functions/acos.php | 38 -
.../complex/classes/src/functions/acosh.php | 34 -
.../complex/classes/src/functions/acot.php | 25 -
.../complex/classes/src/functions/acoth.php | 25 -
.../complex/classes/src/functions/acsc.php | 29 -
.../complex/classes/src/functions/acsch.php | 29 -
.../classes/src/functions/argument.php | 28 -
.../complex/classes/src/functions/asec.php | 29 -
.../complex/classes/src/functions/asech.php | 29 -
.../complex/classes/src/functions/asin.php | 37 -
.../complex/classes/src/functions/asinh.php | 33 -
.../complex/classes/src/functions/atan.php | 45 -
.../complex/classes/src/functions/atanh.php | 38 -
.../classes/src/functions/conjugate.php | 28 -
.../complex/classes/src/functions/cos.php | 34 -
.../complex/classes/src/functions/cosh.php | 32 -
.../complex/classes/src/functions/cot.php | 29 -
.../complex/classes/src/functions/coth.php | 24 -
.../complex/classes/src/functions/csc.php | 29 -
.../complex/classes/src/functions/csch.php | 29 -
.../complex/classes/src/functions/exp.php | 34 -
.../complex/classes/src/functions/inverse.php | 29 -
.../complex/classes/src/functions/ln.php | 33 -
.../complex/classes/src/functions/log10.php | 32 -
.../complex/classes/src/functions/log2.php | 32 -
.../classes/src/functions/negative.php | 31 -
.../complex/classes/src/functions/pow.php | 40 -
.../complex/classes/src/functions/rho.php | 28 -
.../complex/classes/src/functions/sec.php | 25 -
.../complex/classes/src/functions/sech.php | 25 -
.../complex/classes/src/functions/sin.php | 32 -
.../complex/classes/src/functions/sinh.php | 32 -
.../complex/classes/src/functions/sqrt.php | 29 -
.../complex/classes/src/functions/tan.php | 40 -
.../complex/classes/src/functions/tanh.php | 35 -
.../complex/classes/src/functions/theta.php | 38 -
.../complex/classes/src/operations/add.php | 46 -
.../classes/src/operations/divideby.php | 56 -
.../classes/src/operations/divideinto.php | 56 -
.../classes/src/operations/multiply.php | 48 -
.../classes/src/operations/subtract.php | 46 -
.../vendor/markbaker/complex/composer.json | 53 +-
.../complex/examples/complexTest.php | 2 +-
.../complex/examples/testFunctions.php | 4 +-
.../complex/examples/testOperations.php | 9 +-
.../vendor/markbaker/matrix/README.md | 142 +-
.../markbaker/matrix/classes/Autoloader.php | 53 -
.../markbaker/matrix/classes/Bootstrap.php | 38 -
.../markbaker/matrix/classes/src/Builder.php | 10 +-
.../src/Decomposition/Decomposition.php | 27 +
.../matrix/classes/src/Decomposition/LU.php | 260 ++
.../matrix/classes/src/Decomposition/QR.php | 191 +
.../matrix/classes/src/Div0Exception.php | 13 +
.../matrix/classes/src/Functions.php | 83 +-
.../markbaker/matrix/classes/src/Matrix.php | 55 +-
.../matrix/classes/src/Operations.php | 157 +
.../matrix/classes/src/Operators/Division.php | 15 +-
.../classes/src/Operators/Multiplication.php | 37 +-
.../matrix/classes/src/functions/adjoint.php | 30 -
.../classes/src/functions/antidiagonal.php | 29 -
.../classes/src/functions/cofactors.php | 30 -
.../classes/src/functions/determinant.php | 30 -
.../matrix/classes/src/functions/diagonal.php | 30 -
.../matrix/classes/src/functions/identity.php | 30 -
.../matrix/classes/src/functions/inverse.php | 30 -
.../matrix/classes/src/functions/minors.php | 30 -
.../matrix/classes/src/functions/trace.php | 30 -
.../classes/src/functions/transpose.php | 30 -
.../matrix/classes/src/operations/add.php | 44 -
.../classes/src/operations/directsum.php | 44 -
.../classes/src/operations/divideby.php | 44 -
.../classes/src/operations/divideinto.php | 44 -
.../classes/src/operations/multiply.php | 44 -
.../classes/src/operations/subtract.php | 44 -
.../vendor/markbaker/matrix/composer.json | 46 +-
.../vendor/markbaker/matrix/examples/test.php | 34 +-
.../phpoffice/phpspreadsheet/CHANGELOG.md | 231 +-
.../phpoffice/phpspreadsheet/CONTRIBUTING.md | 9 +
.../phpoffice/phpspreadsheet/composer.json | 49 +-
.../Calculation/Calculation.php | 1539 ++++----
.../PhpSpreadsheet/Calculation/Database.php | 326 +-
.../Calculation/Database/DAverage.php | 45 +
.../Calculation/Database/DCount.php | 43 +
.../Calculation/Database/DCountA.php | 42 +
.../Calculation/Database/DGet.php | 51 +
.../Calculation/Database/DMax.php | 46 +
.../Calculation/Database/DMin.php | 46 +
.../Calculation/Database/DProduct.php | 45 +
.../Calculation/Database/DStDev.php | 46 +
.../Calculation/Database/DStDevP.php | 46 +
.../Calculation/Database/DSum.php | 45 +
.../Calculation/Database/DVar.php | 46 +
.../Calculation/Database/DVarP.php | 46 +
.../Calculation/Database/DatabaseAbstract.php | 174 +
.../PhpSpreadsheet/Calculation/DateTime.php | 1356 ++-----
.../Calculation/DateTimeExcel/Constants.php | 38 +
.../Calculation/DateTimeExcel/Current.php | 59 +
.../Calculation/DateTimeExcel/Date.php | 168 +
.../Calculation/DateTimeExcel/DateParts.php | 127 +
.../Calculation/DateTimeExcel/DateValue.php | 147 +
.../Calculation/DateTimeExcel/Days.php | 51 +
.../Calculation/DateTimeExcel/Days360.php | 106 +
.../Calculation/DateTimeExcel/Difference.php | 146 +
.../Calculation/DateTimeExcel/Helpers.php | 306 ++
.../Calculation/DateTimeExcel/Month.php | 82 +
.../Calculation/DateTimeExcel/NetworkDays.php | 102 +
.../Calculation/DateTimeExcel/Time.php | 119 +
.../Calculation/DateTimeExcel/TimeParts.php | 112 +
.../Calculation/DateTimeExcel/TimeValue.php | 67 +
.../Calculation/DateTimeExcel/Week.php | 254 ++
.../Calculation/DateTimeExcel/WorkDay.php | 189 +
.../Calculation/DateTimeExcel/YearFrac.php | 120 +
.../Calculation/Engine/Logger.php | 12 +-
.../Calculation/Engineering.php | 1181 ++----
.../Calculation/Engineering/BesselI.php | 137 +
.../Calculation/Engineering/BesselJ.php | 172 +
.../Calculation/Engineering/BesselK.php | 111 +
.../Calculation/Engineering/BesselY.php | 118 +
.../Calculation/Engineering/BitWise.php | 227 ++
.../Calculation/Engineering/Compare.php | 70 +
.../Calculation/Engineering/Complex.php | 99 +
.../Engineering/ComplexFunctions.php | 513 +++
.../Engineering/ComplexOperations.php | 120 +
.../Calculation/Engineering/Constants.php | 11 +
.../Calculation/Engineering/ConvertBase.php | 65 +
.../Calculation/Engineering/ConvertBinary.php | 134 +
.../Engineering/ConvertDecimal.php | 183 +
.../Calculation/Engineering/ConvertHex.php | 146 +
.../Calculation/Engineering/ConvertOctal.php | 145 +
.../Calculation/Engineering/ConvertUOM.php | 4 +-
.../Engineering/EngineeringValidations.php | 33 +
.../Calculation/Engineering/Erf.php | 91 +
.../Calculation/Engineering/ErfC.php | 68 +
.../PhpSpreadsheet/Calculation/Financial.php | 1981 +++-------
.../Calculation/Financial/Amortization.php | 210 ++
.../CashFlow/CashFlowValidations.php | 53 +
.../Financial/CashFlow/Constant/Periodic.php | 199 +
.../CashFlow/Constant/Periodic/Cumulative.php | 141 +
.../CashFlow/Constant/Periodic/Interest.php | 216 ++
.../Periodic/InterestAndPrincipal.php | 44 +
.../CashFlow/Constant/Periodic/Payments.php | 115 +
.../Calculation/Financial/CashFlow/Single.php | 108 +
.../CashFlow/Variable/NonPeriodic.php | 244 ++
.../Financial/CashFlow/Variable/Periodic.php | 160 +
.../Calculation/Financial/Constants.php | 19 +
.../Calculation/Financial/Coupons.php | 415 +++
.../Calculation/Financial/Depreciation.php | 266 ++
.../Calculation/Financial/Dollar.php | 97 +
.../Financial/FinancialValidations.php | 158 +
.../Calculation/Financial/Helpers.php | 58 +
.../Calculation/Financial/InterestRate.php | 72 +
.../Financial/Securities/AccruedInterest.php | 151 +
.../Financial/Securities/Price.php | 283 ++
.../Financial/Securities/Rates.php | 137 +
.../Securities/SecurityValidations.php | 42 +
.../Financial/Securities/Yields.php | 153 +
.../Calculation/Financial/TreasuryBill.php | 147 +
.../Calculation/FormulaParser.php | 24 +-
.../Calculation/FormulaToken.php | 14 +-
.../PhpSpreadsheet/Calculation/Functions.php | 79 +-
.../Calculation/Internal/MakeMatrix.php | 11 +
.../Calculation/Internal/WildcardMatch.php | 39 +
.../PhpSpreadsheet/Calculation/Logical.php | 266 +-
.../Calculation/Logical/Boolean.php | 36 +
.../Calculation/Logical/Conditional.php | 183 +
.../Calculation/Logical/Operations.php | 198 +
.../PhpSpreadsheet/Calculation/LookupRef.php | 904 +----
.../Calculation/LookupRef/Address.php | 98 +
.../Calculation/LookupRef/ExcelMatch.php | 198 +
.../Calculation/LookupRef/Formula.php | 43 +
.../Calculation/LookupRef/HLookup.php | 116 +
.../Calculation/LookupRef/Helpers.php | 74 +
.../Calculation/LookupRef/Hyperlink.php | 40 +
.../Calculation/LookupRef/Indirect.php | 97 +
.../Calculation/LookupRef/Lookup.php | 105 +
.../Calculation/LookupRef/LookupBase.php | 48 +
.../LookupRef/LookupRefValidations.php | 39 +
.../Calculation/LookupRef/Matrix.php | 110 +
.../Calculation/LookupRef/Offset.php | 136 +
.../LookupRef/RowColumnInformation.php | 209 ++
.../Calculation/LookupRef/Selection.php | 46 +
.../Calculation/LookupRef/VLookup.php | 105 +
.../PhpSpreadsheet/Calculation/MathTrig.php | 1903 ++++------
.../Calculation/MathTrig/Absolute.php | 28 +
.../Calculation/MathTrig/Angle.php | 48 +
.../Calculation/MathTrig/Arabic.php | 103 +
.../Calculation/MathTrig/Base.php | 49 +
.../Calculation/MathTrig/Ceiling.php | 138 +
.../Calculation/MathTrig/Combinations.php | 74 +
.../Calculation/MathTrig/Exp.php | 28 +
.../Calculation/MathTrig/Factorial.php | 110 +
.../Calculation/MathTrig/Floor.php | 166 +
.../Calculation/MathTrig/Gcd.php | 69 +
.../Calculation/MathTrig/Helpers.php | 129 +
.../Calculation/MathTrig/IntClass.php | 31 +
.../Calculation/MathTrig/Lcm.php | 110 +
.../Calculation/MathTrig/Logarithms.php | 77 +
.../Calculation/MathTrig/MatrixFunctions.php | 138 +
.../Calculation/MathTrig/Operations.php | 136 +
.../Calculation/MathTrig/Random.php | 39 +
.../Calculation/MathTrig/Roman.php | 835 +++++
.../Calculation/MathTrig/Round.php | 179 +
.../Calculation/MathTrig/SeriesSum.php | 46 +
.../Calculation/MathTrig/Sign.php | 29 +
.../Calculation/MathTrig/Sqrt.php | 49 +
.../Calculation/MathTrig/Subtotal.php | 111 +
.../Calculation/MathTrig/Sum.php | 115 +
.../Calculation/MathTrig/SumSquares.php | 142 +
.../Calculation/MathTrig/Trig/Cosecant.php | 49 +
.../Calculation/MathTrig/Trig/Cosine.php | 89 +
.../Calculation/MathTrig/Trig/Cotangent.php | 91 +
.../Calculation/MathTrig/Trig/Secant.php | 49 +
.../Calculation/MathTrig/Trig/Sine.php | 89 +
.../Calculation/MathTrig/Trig/Tangent.php | 127 +
.../Calculation/MathTrig/Trunc.php | 39 +
.../Calculation/Statistical.php | 3202 +++--------------
.../Calculation/Statistical/AggregateBase.php | 50 +
.../Calculation/Statistical/Averages.php | 259 ++
.../Calculation/Statistical/Averages/Mean.php | 131 +
.../Calculation/Statistical/Conditional.php | 304 ++
.../Calculation/Statistical/Confidence.php | 41 +
.../Calculation/Statistical/Counts.php | 95 +
.../Calculation/Statistical/Deviations.php | 141 +
.../Statistical/Distributions/Beta.php | 260 ++
.../Statistical/Distributions/Binomial.php | 202 ++
.../Statistical/Distributions/ChiSquared.php | 311 ++
.../Distributions/DistributionValidations.php | 24 +
.../Statistical/Distributions/Exponential.php | 47 +
.../Statistical/Distributions/F.php | 56 +
.../Statistical/Distributions/Fisher.php | 61 +
.../Statistical/Distributions/Gamma.php | 127 +
.../Statistical/Distributions/GammaBase.php | 381 ++
.../Distributions/HyperGeometric.php | 59 +
.../Statistical/Distributions/LogNormal.php | 119 +
.../Distributions/NewtonRaphson.php | 62 +
.../Statistical/Distributions/Normal.php | 166 +
.../Statistical/Distributions/Poisson.php | 53 +
.../Distributions/StandardNormal.php | 110 +
.../Statistical/Distributions/StudentT.php | 125 +
.../Statistical/Distributions/Weibull.php | 49 +
.../Calculation/Statistical/MaxMinBase.php | 17 +
.../Calculation/Statistical/Maximum.php | 78 +
.../Calculation/Statistical/Minimum.php | 78 +
.../Calculation/Statistical/Percentiles.php | 205 ++
.../Calculation/Statistical/Permutations.php | 77 +
.../Calculation/Statistical/Size.php | 96 +
.../Statistical/StandardDeviations.php | 95 +
.../Calculation/Statistical/Standardize.php | 41 +
.../Statistical/StatisticalValidations.php | 45 +
.../Calculation/Statistical/Trends.php | 421 +++
.../Calculation/Statistical/VarianceBase.php | 28 +
.../Calculation/Statistical/Variances.php | 185 +
.../PhpSpreadsheet/Calculation/TextData.php | 514 +--
.../Calculation/TextData/CaseConvert.php | 54 +
.../Calculation/TextData/CharacterConvert.php | 61 +
.../Calculation/TextData/Concatenate.php | 71 +
.../Calculation/TextData/Extract.php | 64 +
.../Calculation/TextData/Format.php | 236 ++
.../Calculation/TextData/Helpers.php | 90 +
.../Calculation/TextData/Replace.php | 79 +
.../Calculation/TextData/Search.php | 76 +
.../Calculation/TextData/Text.php | 54 +
.../Calculation/TextData/Trim.php | 34 +
.../src/PhpSpreadsheet/Calculation/Web.php | 40 +-
.../Calculation/Web/Service.php | 75 +
.../Calculation/functionlist.txt | 5 +
.../Calculation/locale/Translations.xlsx | Bin 0 -> 109827 bytes
.../Calculation/locale/cs/config | 31 +-
.../Calculation/locale/cs/functions | 856 +++--
.../Calculation/locale/da/config | 33 +-
.../Calculation/locale/da/functions | 873 +++--
.../Calculation/locale/de/config | 32 +-
.../Calculation/locale/de/functions | 869 +++--
.../Calculation/locale/es/config | 32 +-
.../Calculation/locale/es/functions | 873 +++--
.../Calculation/locale/fi/config | 32 +-
.../Calculation/locale/fi/functions | 873 +++--
.../Calculation/locale/fr/config | 32 +-
.../Calculation/locale/fr/functions | 860 +++--
.../Calculation/locale/hu/config | 31 +-
.../Calculation/locale/hu/functions | 873 +++--
.../Calculation/locale/it/config | 32 +-
.../Calculation/locale/it/functions | 873 +++--
.../Calculation/locale/nb/config | 20 +
.../Calculation/locale/nb/functions | 538 +++
.../Calculation/locale/nl/config | 32 +-
.../Calculation/locale/nl/functions | 872 +++--
.../Calculation/locale/no/config | 24 -
.../Calculation/locale/no/functions | 416 ---
.../Calculation/locale/pl/config | 32 +-
.../Calculation/locale/pl/functions | 872 +++--
.../Calculation/locale/pt/br/config | 32 +-
.../Calculation/locale/pt/br/functions | 863 +++--
.../Calculation/locale/pt/config | 32 +-
.../Calculation/locale/pt/functions | 873 +++--
.../Calculation/locale/ru/config | 32 +-
.../Calculation/locale/ru/functions | 872 +++--
.../Calculation/locale/sv/config | 32 +-
.../Calculation/locale/sv/functions | 868 +++--
.../Calculation/locale/tr/config | 32 +-
.../Calculation/locale/tr/functions | 873 +++--
.../src/PhpSpreadsheet/Cell/AddressHelper.php | 91 +-
.../Cell/AdvancedValueBinder.php | 151 +-
.../src/PhpSpreadsheet/Cell/Cell.php | 135 +-
.../src/PhpSpreadsheet/Cell/Coordinate.php | 173 +-
.../src/PhpSpreadsheet/Cell/DataType.php | 26 +-
.../PhpSpreadsheet/Cell/DataValidation.php | 94 +-
.../Cell/DefaultValueBinder.php | 31 +-
.../src/PhpSpreadsheet/Cell/Hyperlink.php | 22 +-
.../PhpSpreadsheet/Cell/StringValueBinder.php | 101 +-
.../src/PhpSpreadsheet/Chart/Axis.php | 178 +-
.../src/PhpSpreadsheet/Chart/Chart.php | 8 +-
.../src/PhpSpreadsheet/Chart/DataSeries.php | 20 +-
.../PhpSpreadsheet/Chart/DataSeriesValues.php | 4 +-
.../src/PhpSpreadsheet/Chart/GridLines.php | 154 +-
.../src/PhpSpreadsheet/Chart/Layout.php | 84 +-
.../src/PhpSpreadsheet/Chart/Legend.php | 10 +-
.../src/PhpSpreadsheet/Chart/PlotArea.php | 6 +-
.../src/PhpSpreadsheet/Chart/Properties.php | 12 +-
.../PhpSpreadsheet/Chart/Renderer/JpGraph.php | 4 +
.../src/PhpSpreadsheet/Chart/Title.php | 37 +-
.../src/PhpSpreadsheet/Collection/Cells.php | 105 +-
.../Collection/CellsFactory.php | 6 +-
.../src/PhpSpreadsheet/Collection/Memory.php | 44 +
.../src/PhpSpreadsheet/Comment.php | 201 +-
.../src/PhpSpreadsheet/DefinedName.php | 29 +-
.../PhpSpreadsheet/Document/Properties.php | 435 +--
.../src/PhpSpreadsheet/Document/Security.php | 121 +-
.../src/PhpSpreadsheet/DocumentGenerator.php | 97 -
.../src/PhpSpreadsheet/HashTable.php | 80 +-
.../src/PhpSpreadsheet/Helper/Dimension.php | 103 +
.../src/PhpSpreadsheet/Helper/Html.php | 10 +-
.../src/PhpSpreadsheet/Helper/Sample.php | 19 +-
.../src/PhpSpreadsheet/Helper/Size.php | 52 +
.../src/PhpSpreadsheet/IOFactory.php | 70 +-
.../src/PhpSpreadsheet/NamedFormula.php | 2 +-
.../src/PhpSpreadsheet/Reader/BaseReader.php | 43 +-
.../src/PhpSpreadsheet/Reader/Csv.php | 357 +-
.../PhpSpreadsheet/Reader/Csv/Delimiter.php | 151 +
.../Reader/DefaultReadFilter.php | 4 +-
.../src/PhpSpreadsheet/Reader/Gnumeric.php | 690 +---
.../Reader/Gnumeric/PageSetup.php | 15 +-
.../Reader/Gnumeric/Properties.php | 164 +
.../PhpSpreadsheet/Reader/Gnumeric/Styles.php | 278 ++
.../src/PhpSpreadsheet/Reader/Html.php | 173 +-
.../src/PhpSpreadsheet/Reader/IReadFilter.php | 4 +-
.../src/PhpSpreadsheet/Reader/IReader.php | 26 +-
.../src/PhpSpreadsheet/Reader/Ods.php | 283 +-
.../PhpSpreadsheet/Reader/Ods/AutoFilter.php | 45 +
.../PhpSpreadsheet/Reader/Ods/BaseReader.php | 77 +
.../Reader/Ods/DefinedNames.php | 66 +
.../Reader/Ods/PageSettings.php | 4 +-
.../PhpSpreadsheet/Reader/Ods/Properties.php | 9 +-
.../Reader/Security/XmlScanner.php | 13 +-
.../src/PhpSpreadsheet/Reader/Slk.php | 109 +-
.../src/PhpSpreadsheet/Reader/Xlsx.php | 1454 ++++----
.../PhpSpreadsheet/Reader/Xlsx/AutoFilter.php | 9 +-
.../src/PhpSpreadsheet/Reader/Xlsx/Chart.php | 73 +-
.../Reader/Xlsx/ColumnAndRowAttributes.php | 11 +-
.../Reader/Xlsx/ConditionalStyles.php | 80 +-
.../Reader/Xlsx/DataValidations.php | 10 +-
.../PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php | 31 +-
.../PhpSpreadsheet/Reader/Xlsx/Namespaces.php | 76 +
.../PhpSpreadsheet/Reader/Xlsx/PageSetup.php | 2 +-
.../PhpSpreadsheet/Reader/Xlsx/Properties.php | 41 +-
.../Reader/Xlsx/SheetViewOptions.php | 13 +-
.../PhpSpreadsheet/Reader/Xlsx/SheetViews.php | 70 +-
.../src/PhpSpreadsheet/Reader/Xlsx/Styles.php | 187 +-
.../src/PhpSpreadsheet/Reader/Xlsx/Theme.php | 12 +-
.../src/PhpSpreadsheet/Reader/Xml.php | 553 +--
.../Reader/Xml/PageSettings.php | 6 +-
.../PhpSpreadsheet/Reader/Xml/Properties.php | 157 +
.../src/PhpSpreadsheet/Reader/Xml/Style.php | 83 +
.../Reader/Xml/Style/Alignment.php | 58 +
.../Reader/Xml/Style/Border.php | 98 +
.../PhpSpreadsheet/Reader/Xml/Style/Fill.php | 63 +
.../PhpSpreadsheet/Reader/Xml/Style/Font.php | 79 +
.../Reader/Xml/Style/NumberFormat.php | 33 +
.../Reader/Xml/Style/StyleBase.php | 32 +
.../src/PhpSpreadsheet/ReferenceHelper.php | 500 ++-
.../PhpSpreadsheet/RichText/ITextElement.php | 2 +-
.../src/PhpSpreadsheet/RichText/RichText.php | 32 +-
.../src/PhpSpreadsheet/RichText/Run.php | 12 +-
.../PhpSpreadsheet/RichText/TextElement.php | 8 +-
.../src/PhpSpreadsheet/Settings.php | 64 +-
.../src/PhpSpreadsheet/Shared/CodePage.php | 19 +-
.../src/PhpSpreadsheet/Shared/Date.php | 141 +-
.../src/PhpSpreadsheet/Shared/Drawing.php | 92 +-
.../Escher/DgContainer/SpgrContainer.php | 10 +-
.../Shared/Escher/DggContainer.php | 6 +-
.../Escher/DggContainer/BstoreContainer.php | 6 +-
.../DggContainer/BstoreContainer/BSE.php | 12 +-
.../DggContainer/BstoreContainer/BSE/Blip.php | 12 +-
.../src/PhpSpreadsheet/Shared/File.php | 129 +-
.../src/PhpSpreadsheet/Shared/Font.php | 77 +-
.../src/PhpSpreadsheet/Shared/IntOrFloat.php | 21 +
.../Shared/JAMA/CholeskyDecomposition.php | 4 +-
.../Shared/JAMA/EigenvalueDecomposition.php | 15 +-
.../Shared/JAMA/LUDecomposition.php | 8 +-
.../src/PhpSpreadsheet/Shared/JAMA/Matrix.php | 21 +-
.../Shared/JAMA/QRDecomposition.php | 80 +-
.../JAMA/SingularValueDecomposition.php | 3 +-
.../PhpSpreadsheet/Shared/PasswordHasher.php | 47 +-
.../PhpSpreadsheet/Shared/StringHelper.php | 156 +-
.../src/PhpSpreadsheet/Shared/TimeZone.php | 36 +-
.../PhpSpreadsheet/Shared/Trend/BestFit.php | 88 +-
.../Shared/Trend/ExponentialBestFit.php | 21 +-
.../Shared/Trend/LinearBestFit.php | 5 +-
.../Shared/Trend/LogarithmicBestFit.php | 23 +-
.../Shared/Trend/PolynomialBestFit.php | 8 +-
.../Shared/Trend/PowerBestFit.php | 35 +-
.../src/PhpSpreadsheet/Shared/Trend/Trend.php | 13 +-
.../src/PhpSpreadsheet/Shared/XMLWriter.php | 24 +-
.../src/PhpSpreadsheet/Spreadsheet.php | 298 +-
.../src/PhpSpreadsheet/Style/Alignment.php | 140 +-
.../src/PhpSpreadsheet/Style/Border.php | 62 +-
.../src/PhpSpreadsheet/Style/Borders.php | 72 +-
.../src/PhpSpreadsheet/Style/Color.php | 177 +-
.../src/PhpSpreadsheet/Style/Conditional.php | 93 +-
.../ConditionalDataBar.php | 102 +
.../ConditionalDataBarExtension.php | 290 ++
.../ConditionalFormatValueObject.php | 78 +
.../ConditionalFormattingRuleExtension.php | 208 ++
.../src/PhpSpreadsheet/Style/Fill.php | 92 +-
.../src/PhpSpreadsheet/Style/Font.php | 180 +-
.../src/PhpSpreadsheet/Style/NumberFormat.php | 543 +--
.../Style/NumberFormat/BaseFormatter.php | 12 +
.../Style/NumberFormat/DateFormatter.php | 129 +
.../Style/NumberFormat/Formatter.php | 162 +
.../Style/NumberFormat/FractionFormatter.php | 64 +
.../Style/NumberFormat/NumberFormatter.php | 221 ++
.../NumberFormat/PercentageFormatter.php | 42 +
.../src/PhpSpreadsheet/Style/Protection.php | 35 +-
.../src/PhpSpreadsheet/Style/Style.php | 330 +-
.../src/PhpSpreadsheet/Style/Supervisor.php | 21 +-
.../PhpSpreadsheet/Worksheet/AutoFilter.php | 705 ++--
.../Worksheet/AutoFilter/Column.php | 130 +-
.../Worksheet/AutoFilter/Column/Rule.php | 171 +-
.../PhpSpreadsheet/Worksheet/BaseDrawing.php | 121 +-
.../PhpSpreadsheet/Worksheet/CellIterator.php | 14 +-
.../src/PhpSpreadsheet/Worksheet/Column.php | 6 +-
.../Worksheet/ColumnCellIterator.php | 26 +-
.../Worksheet/ColumnDimension.php | 48 +-
.../Worksheet/ColumnIterator.php | 30 +-
.../PhpSpreadsheet/Worksheet/Dimension.php | 40 +-
.../src/PhpSpreadsheet/Worksheet/Drawing.php | 138 +-
.../Worksheet/Drawing/Shadow.php | 46 +-
.../PhpSpreadsheet/Worksheet/HeaderFooter.php | 60 +-
.../src/PhpSpreadsheet/Worksheet/Iterator.php | 23 +-
.../Worksheet/MemoryDrawing.php | 71 +-
.../PhpSpreadsheet/Worksheet/PageMargins.php | 36 +-
.../PhpSpreadsheet/Worksheet/PageSetup.php | 133 +-
.../PhpSpreadsheet/Worksheet/Protection.php | 108 +-
.../src/PhpSpreadsheet/Worksheet/Row.php | 10 +-
.../Worksheet/RowCellIterator.php | 28 +-
.../PhpSpreadsheet/Worksheet/RowDimension.php | 46 +-
.../PhpSpreadsheet/Worksheet/RowIterator.php | 33 +-
.../PhpSpreadsheet/Worksheet/SheetView.php | 34 +-
.../PhpSpreadsheet/Worksheet/Worksheet.php | 934 ++---
.../src/PhpSpreadsheet/Writer/BaseWriter.php | 36 +-
.../src/PhpSpreadsheet/Writer/Csv.php | 110 +-
.../src/PhpSpreadsheet/Writer/Html.php | 418 ++-
.../src/PhpSpreadsheet/Writer/IWriter.php | 22 +-
.../src/PhpSpreadsheet/Writer/Ods.php | 128 +-
.../PhpSpreadsheet/Writer/Ods/AutoFilters.php | 63 +
.../PhpSpreadsheet/Writer/Ods/Cell/Style.php | 178 +
.../src/PhpSpreadsheet/Writer/Ods/Content.php | 106 +-
.../src/PhpSpreadsheet/Writer/Ods/Meta.php | 71 +-
.../src/PhpSpreadsheet/Writer/Ods/MetaInf.php | 2 +-
.../PhpSpreadsheet/Writer/Ods/Mimetype.php | 6 +-
.../Writer/Ods/NamedExpressions.php | 26 +-
.../PhpSpreadsheet/Writer/Ods/Settings.php | 56 +-
.../src/PhpSpreadsheet/Writer/Ods/Styles.php | 5 +-
.../PhpSpreadsheet/Writer/Ods/Thumbnails.php | 6 +-
.../PhpSpreadsheet/Writer/Ods/WriterPart.php | 2 +
.../src/PhpSpreadsheet/Writer/Pdf.php | 38 +-
.../src/PhpSpreadsheet/Writer/Pdf/Dompdf.php | 39 +-
.../src/PhpSpreadsheet/Writer/Pdf/Mpdf.php | 52 +-
.../src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php | 42 +-
.../src/PhpSpreadsheet/Writer/Xlsx.php | 420 ++-
.../src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 115 +-
.../PhpSpreadsheet/Writer/Xlsx/Comments.php | 45 +-
.../Writer/Xlsx/ContentTypes.php | 57 +-
.../Writer/Xlsx/DefinedNames.php | 239 +-
.../PhpSpreadsheet/Writer/Xlsx/DocProps.php | 25 +-
.../PhpSpreadsheet/Writer/Xlsx/Drawing.php | 113 +-
.../src/PhpSpreadsheet/Writer/Xlsx/Rels.php | 133 +-
.../Writer/Xlsx/StringTable.php | 47 +-
.../src/PhpSpreadsheet/Writer/Xlsx/Style.php | 251 +-
.../src/PhpSpreadsheet/Writer/Xlsx/Theme.php | 20 +-
.../PhpSpreadsheet/Writer/Xlsx/Workbook.php | 28 +-
.../PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 625 ++--
.../PhpSpreadsheet/Writer/Xlsx/WriterPart.php | 4 +-
.../src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php | 7 +
512 files changed, 46503 insertions(+), 27538 deletions(-)
delete mode 100644 lib/phpspreadsheet/vendor/composer/autoload_files.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/Autoloader.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/Bootstrap.php
create mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Functions.php
create mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Operations.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/abs.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acos.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acosh.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acot.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acoth.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsc.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsch.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/argument.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asec.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asech.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asin.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asinh.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atan.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atanh.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/conjugate.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cos.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cosh.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cot.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/coth.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csc.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csch.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/exp.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/inverse.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/ln.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log10.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log2.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/negative.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/pow.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/rho.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sec.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sech.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sin.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sinh.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sqrt.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tan.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tanh.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/theta.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/add.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideby.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideinto.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/multiply.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/subtract.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/Autoloader.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/Bootstrap.php
create mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php
create mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/LU.php
create mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/QR.php
create mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Div0Exception.php
create mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operations.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/adjoint.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/antidiagonal.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/cofactors.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/determinant.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/diagonal.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/identity.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/inverse.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/minors.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/trace.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/transpose.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/add.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/directsum.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideby.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideinto.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/multiply.php
delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/subtract.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCount.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCountA.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMin.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DProduct.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDev.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDevP.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DSum.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVar.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVarP.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Boolean.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Maximum.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/config
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/functions
delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/config
delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/functions
delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseReader.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Alignment.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Fill.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/NumberFormat.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php
create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php
diff --git a/lib/phpspreadsheet/vendor/autoload.php b/lib/phpspreadsheet/vendor/autoload.php
index ae97c5a8ef9..afaa78f0390 100644
--- a/lib/phpspreadsheet/vendor/autoload.php
+++ b/lib/phpspreadsheet/vendor/autoload.php
@@ -4,4 +4,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
-return ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b::getLoader();
+return ComposerAutoloaderInit47a82a2b792e78d18b5f54d474d822dc::getLoader();
diff --git a/lib/phpspreadsheet/vendor/composer/ClassLoader.php b/lib/phpspreadsheet/vendor/composer/ClassLoader.php
index 1a58957d25d..afef3fa2ad8 100644
--- a/lib/phpspreadsheet/vendor/composer/ClassLoader.php
+++ b/lib/phpspreadsheet/vendor/composer/ClassLoader.php
@@ -42,21 +42,75 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
+ /** @var ?string */
+ private $vendorDir;
+
// PSR-4
+ /**
+ * @var array[]
+ * @psalm-var array>
+ */
private $prefixLengthsPsr4 = array();
+ /**
+ * @var array[]
+ * @psalm-var array>
+ */
private $prefixDirsPsr4 = array();
+ /**
+ * @var array[]
+ * @psalm-var array
+ */
private $fallbackDirsPsr4 = array();
// PSR-0
+ /**
+ * @var array[]
+ * @psalm-var array>
+ */
private $prefixesPsr0 = array();
+ /**
+ * @var array[]
+ * @psalm-var array
+ */
private $fallbackDirsPsr0 = array();
+ /** @var bool */
private $useIncludePath = false;
+
+ /**
+ * @var string[]
+ * @psalm-var array
+ */
private $classMap = array();
+
+ /** @var bool */
private $classMapAuthoritative = false;
+
+ /**
+ * @var bool[]
+ * @psalm-var array
+ */
private $missingClasses = array();
+
+ /** @var ?string */
private $apcuPrefix;
+ /**
+ * @var self[]
+ */
+ private static $registeredLoaders = array();
+
+ /**
+ * @param ?string $vendorDir
+ */
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ }
+
+ /**
+ * @return string[]
+ */
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
@@ -66,28 +120,47 @@ class ClassLoader
return array();
}
+ /**
+ * @return array[]
+ * @psalm-return array>
+ */
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
+ /**
+ * @return array[]
+ * @psalm-return array
+ */
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
+ /**
+ * @return array[]
+ * @psalm-return array
+ */
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
+ /**
+ * @return string[] Array of classname => path
+ * @psalm-return array
+ */
public function getClassMap()
{
return $this->classMap;
}
/**
- * @param array $classMap Class to filename map
+ * @param string[] $classMap Class to filename map
+ * @psalm-param array $classMap
+ *
+ * @return void
*/
public function addClassMap(array $classMap)
{
@@ -102,9 +175,11 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
- * @param string $prefix The prefix
- * @param array|string $paths The PSR-0 root directories
- * @param bool $prepend Whether to prepend the directories
+ * @param string $prefix The prefix
+ * @param string[]|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
*/
public function add($prefix, $paths, $prepend = false)
{
@@ -147,11 +222,13 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
- * @param string $prefix The prefix/namespace, with trailing '\\'
- * @param array|string $paths The PSR-4 base directories
- * @param bool $prepend Whether to prepend the directories
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param string[]|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
+ *
+ * @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@@ -195,8 +272,10 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
- * @param string $prefix The prefix
- * @param array|string $paths The PSR-0 base directories
+ * @param string $prefix The prefix
+ * @param string[]|string $paths The PSR-0 base directories
+ *
+ * @return void
*/
public function set($prefix, $paths)
{
@@ -211,10 +290,12 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
- * @param string $prefix The prefix/namespace, with trailing '\\'
- * @param array|string $paths The PSR-4 base directories
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
+ *
+ * @return void
*/
public function setPsr4($prefix, $paths)
{
@@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
+ *
+ * @return void
*/
public function setUseIncludePath($useIncludePath)
{
@@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
+ *
+ * @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
@@ -276,6 +361,8 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
+ *
+ * @return void
*/
public function setApcuPrefix($apcuPrefix)
{
@@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
}
/**
* Unregisters this instance as an autoloader.
+ *
+ * @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
- * @return bool|null True if loaded, null otherwise
+ * @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
@@ -323,6 +429,8 @@ class ClassLoader
return true;
}
+
+ return null;
}
/**
@@ -367,6 +475,21 @@ class ClassLoader
return $file;
}
+ /**
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
+ *
+ * @return self[]
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
@@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include.
*
* Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ * @private
*/
function includeFile($file)
{
diff --git a/lib/phpspreadsheet/vendor/composer/InstalledVersions.php b/lib/phpspreadsheet/vendor/composer/InstalledVersions.php
index fe344fe10ce..d50e0c9fcc4 100644
--- a/lib/phpspreadsheet/vendor/composer/InstalledVersions.php
+++ b/lib/phpspreadsheet/vendor/composer/InstalledVersions.php
@@ -1,318 +1,350 @@
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
namespace Composer;
+use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
-
-
-
-
-
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ */
class InstalledVersions
{
-private static $installed = array (
- 'root' =>
- array (
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'aliases' =>
- array (
- ),
- 'reference' => '70d1b7d67bc280b21f450db41728869bd1bda8d8',
- 'name' => '__root__',
- ),
- 'versions' =>
- array (
- '__root__' =>
- array (
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'aliases' =>
- array (
- ),
- 'reference' => '70d1b7d67bc280b21f450db41728869bd1bda8d8',
- ),
- 'ezyang/htmlpurifier' =>
- array (
- 'pretty_version' => 'v4.13.0',
- 'version' => '4.13.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '08e27c97e4c6ed02f37c5b2b20488046c8d90d75',
- ),
- 'maennchen/zipstream-php' =>
- array (
- 'pretty_version' => '2.1.0',
- 'version' => '2.1.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => 'c4c5803cc1f93df3d2448478ef79394a5981cc58',
- ),
- 'markbaker/complex' =>
- array (
- 'pretty_version' => '2.0.0',
- 'version' => '2.0.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '9999f1432fae467bc93c53f357105b4c31bb994c',
- ),
- 'markbaker/matrix' =>
- array (
- 'pretty_version' => '2.0.0',
- 'version' => '2.0.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '9567d9c4c519fbe40de01dbd1e4469dbbb66f46a',
- ),
- 'myclabs/php-enum' =>
- array (
- 'pretty_version' => '1.7.7',
- 'version' => '1.7.7.0',
- 'aliases' =>
- array (
- ),
- 'reference' => 'd178027d1e679832db9f38248fcc7200647dc2b7',
- ),
- 'phpoffice/phpspreadsheet' =>
- array (
- 'pretty_version' => '1.16.0',
- 'version' => '1.16.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '76d4323b85129d0c368149c831a07a3e258b2b50',
- ),
- 'psr/http-client' =>
- array (
- 'pretty_version' => '1.0.1',
- 'version' => '1.0.1.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
- ),
- 'psr/http-factory' =>
- array (
- 'pretty_version' => '1.0.1',
- 'version' => '1.0.1.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
- ),
- 'psr/http-message' =>
- array (
- 'pretty_version' => '1.0.1',
- 'version' => '1.0.1.0',
- 'aliases' =>
- array (
- ),
- 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
- ),
- 'psr/simple-cache' =>
- array (
- 'pretty_version' => '1.0.1',
- 'version' => '1.0.1.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b',
- ),
- 'symfony/polyfill-mbstring' =>
- array (
- 'pretty_version' => 'v1.22.0',
- 'version' => '1.22.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => 'f377a3dd1fde44d37b9831d68dc8dea3ffd28e13',
- ),
- ),
-);
+ /**
+ * @var mixed[]|null
+ * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null
+ */
+ private static $installed;
+ /**
+ * @var bool|null
+ */
+ private static $canGetVendors;
+ /**
+ * @var array[]
+ * @psalm-var array}>
+ */
+ private static $installedByVendor = array();
+ /**
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
+ *
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackages()
+ {
+ $packages = array();
+ foreach (self::getInstalled() as $installed) {
+ $packages[] = array_keys($installed['versions']);
+ }
+ if (1 === \count($packages)) {
+ return $packages[0];
+ }
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+ }
+ /**
+ * Returns a list of all package names with a specific type e.g. 'library'
+ *
+ * @param string $type
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackagesByType($type)
+ {
+ $packagesByType = array();
-public static function getInstalledPackages()
-{
-return array_keys(self::$installed['versions']);
-}
-
-
-
-
-
-
-
-
-
-public static function isInstalled($packageName)
-{
-return isset(self::$installed['versions'][$packageName]);
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-public static function satisfies(VersionParser $parser, $packageName, $constraint)
-{
-$constraint = $parser->parseConstraints($constraint);
-$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
-
-return $provided->matches($constraint);
-}
-
-
-
-
-
-
-
-
-
-
-public static function getVersionRanges($packageName)
-{
-if (!isset(self::$installed['versions'][$packageName])) {
-throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
-}
-
-$ranges = array();
-if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
-$ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
-}
-if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
-$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
-}
-if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
-$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
-}
-if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
-$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
-}
-
-return implode(' || ', $ranges);
-}
-
-
-
-
-
-public static function getVersion($packageName)
-{
-if (!isset(self::$installed['versions'][$packageName])) {
-throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
-}
-
-if (!isset(self::$installed['versions'][$packageName]['version'])) {
-return null;
-}
-
-return self::$installed['versions'][$packageName]['version'];
-}
-
-
-
-
-
-public static function getPrettyVersion($packageName)
-{
-if (!isset(self::$installed['versions'][$packageName])) {
-throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
-}
-
-if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
-return null;
-}
-
-return self::$installed['versions'][$packageName]['pretty_version'];
-}
-
-
-
-
-
-public static function getReference($packageName)
-{
-if (!isset(self::$installed['versions'][$packageName])) {
-throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
-}
-
-if (!isset(self::$installed['versions'][$packageName]['reference'])) {
-return null;
-}
-
-return self::$installed['versions'][$packageName]['reference'];
-}
-
-
-
-
-
-public static function getRootPackage()
-{
-return self::$installed['root'];
-}
-
-
-
-
-
-
-
-public static function getRawData()
-{
-return self::$installed;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-public static function reload($data)
-{
-self::$installed = $data;
-}
+ foreach (self::getInstalled() as $installed) {
+ foreach ($installed['versions'] as $name => $package) {
+ if (isset($package['type']) && $package['type'] === $type) {
+ $packagesByType[] = $name;
+ }
+ }
+ }
+
+ return $packagesByType;
+ }
+
+ /**
+ * Checks whether the given package is installed
+ *
+ * This also returns true if the package name is provided or replaced by another package
+ *
+ * @param string $packageName
+ * @param bool $includeDevRequirements
+ * @return bool
+ */
+ public static function isInstalled($packageName, $includeDevRequirements = true)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (isset($installed['versions'][$packageName])) {
+ return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package satisfies a version constraint
+ *
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+ *
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+ *
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
+ * @param string $packageName
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+ * @return bool
+ */
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
+ {
+ $constraint = $parser->parseConstraints($constraint);
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+ return $provided->matches($constraint);
+ }
+
+ /**
+ * Returns a version constraint representing all the range(s) which are installed for a given package
+ *
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+ * whether a given version of a package is installed, and not just whether it exists
+ *
+ * @param string $packageName
+ * @return string Version constraint usable with composer/semver
+ */
+ public static function getVersionRanges($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ $ranges = array();
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+ }
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+ }
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+ }
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+ }
+
+ return implode(' || ', $ranges);
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getPrettyVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['pretty_version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+ */
+ public static function getReference($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['reference'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['reference'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+ */
+ public static function getInstallPath($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @return array
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
+ */
+ public static function getRootPackage()
+ {
+ $installed = self::getInstalled();
+
+ return $installed[0]['root'];
+ }
+
+ /**
+ * Returns the raw installed.php data for custom implementations
+ *
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+ * @return array[]
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}
+ */
+ public static function getRawData()
+ {
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = include __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ return self::$installed;
+ }
+
+ /**
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
+ *
+ * @return array[]
+ * @psalm-return list}>
+ */
+ public static function getAllRawData()
+ {
+ return self::getInstalled();
+ }
+
+ /**
+ * Lets you reload the static array from another file
+ *
+ * This is only useful for complex integrations in which a project needs to use
+ * this class but then also needs to execute another project's autoloader in process,
+ * and wants to ensure both projects have access to their version of installed.php.
+ *
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
+ * the data it needs from this class, then call reload() with
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+ * the project in which it runs can then also use this class safely, without
+ * interference between PHPUnit's dependencies and the project's dependencies.
+ *
+ * @param array[] $data A vendor/composer/installed.php data set
+ * @return void
+ *
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data
+ */
+ public static function reload($data)
+ {
+ self::$installed = $data;
+ self::$installedByVendor = array();
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return list}>
+ */
+ private static function getInstalled()
+ {
+ if (null === self::$canGetVendors) {
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+ }
+
+ $installed = array();
+
+ if (self::$canGetVendors) {
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ if (isset(self::$installedByVendor[$vendorDir])) {
+ $installed[] = self::$installedByVendor[$vendorDir];
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
+ $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+ self::$installed = $installed[count($installed) - 1];
+ }
+ }
+ }
+ }
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = require __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+ $installed[] = self::$installed;
+
+ return $installed;
+ }
}
diff --git a/lib/phpspreadsheet/vendor/composer/autoload_files.php b/lib/phpspreadsheet/vendor/composer/autoload_files.php
deleted file mode 100644
index fb9363c7250..00000000000
--- a/lib/phpspreadsheet/vendor/composer/autoload_files.php
+++ /dev/null
@@ -1,67 +0,0 @@
- $vendorDir . '/markbaker/matrix/classes/src/functions/adjoint.php',
- '6e78d1bdea6248d6aa117229efae50f2' => $vendorDir . '/markbaker/matrix/classes/src/functions/antidiagonal.php',
- '4623d87924d94f5412fe5afbf1cef31d' => $vendorDir . '/markbaker/matrix/classes/src/functions/cofactors.php',
- '901fd1f6950a637ca85f66b701a45e13' => $vendorDir . '/markbaker/matrix/classes/src/functions/determinant.php',
- '83057abc0e4acc99ba80154ee5d02a49' => $vendorDir . '/markbaker/matrix/classes/src/functions/diagonal.php',
- '07b7fd7a434451149b4fd477fca0ce06' => $vendorDir . '/markbaker/matrix/classes/src/functions/identity.php',
- 'c8d43b340583e07ae89f2a3baef2cf89' => $vendorDir . '/markbaker/matrix/classes/src/functions/inverse.php',
- '499bb10ed7a3aee2ba4c09a31a85e8d1' => $vendorDir . '/markbaker/matrix/classes/src/functions/minors.php',
- '1cad2e6414d652e8b1c64e8967f6f37d' => $vendorDir . '/markbaker/matrix/classes/src/functions/trace.php',
- '95a7f134ac17161d07def442b3b737e8' => $vendorDir . '/markbaker/matrix/classes/src/functions/transpose.php',
- 'b3a6bc628377118d4b4b8ba08d1eb949' => $vendorDir . '/markbaker/matrix/classes/src/operations/add.php',
- '5fef6d0e407f3f8887266dfa4a6c534c' => $vendorDir . '/markbaker/matrix/classes/src/operations/directsum.php',
- '684ba247e1385946e3babdaa054119de' => $vendorDir . '/markbaker/matrix/classes/src/operations/subtract.php',
- 'aa53dcba601214d17ad405b7c291b7e8' => $vendorDir . '/markbaker/matrix/classes/src/operations/multiply.php',
- '75c79eb1b25749b05a47976f32b0d8a2' => $vendorDir . '/markbaker/matrix/classes/src/operations/divideby.php',
- '6ab8ad87a734f276a6bcd5a0fe1289be' => $vendorDir . '/markbaker/matrix/classes/src/operations/divideinto.php',
- 'abede361264e2ae69ec1eee813a101af' => $vendorDir . '/markbaker/complex/classes/src/functions/abs.php',
- '21a5860fbef5be28db5ddfbc3cca67c4' => $vendorDir . '/markbaker/complex/classes/src/functions/acos.php',
- '1546e3f9d127f2a9bb2d1b6c31c26ef1' => $vendorDir . '/markbaker/complex/classes/src/functions/acosh.php',
- 'd2516f7f4fba5ea5905f494b4a8262e0' => $vendorDir . '/markbaker/complex/classes/src/functions/acot.php',
- '4511163d560956219b96882c0980b65e' => $vendorDir . '/markbaker/complex/classes/src/functions/acoth.php',
- 'c361f5616dc2a8da4fa3e137077cd4ea' => $vendorDir . '/markbaker/complex/classes/src/functions/acsc.php',
- '02d68920fc98da71991ce569c91df0f6' => $vendorDir . '/markbaker/complex/classes/src/functions/acsch.php',
- '88e19525eae308b4a6aa3419364875d3' => $vendorDir . '/markbaker/complex/classes/src/functions/argument.php',
- '60e8e2d0827b58bfc904f13957e51849' => $vendorDir . '/markbaker/complex/classes/src/functions/asec.php',
- '13d2f040713999eab66c359b4d79871d' => $vendorDir . '/markbaker/complex/classes/src/functions/asech.php',
- '838ab38beb32c68a79d3cd2c007d5a04' => $vendorDir . '/markbaker/complex/classes/src/functions/asin.php',
- 'bb28eccd0f8f008333a1b3c163d604ac' => $vendorDir . '/markbaker/complex/classes/src/functions/asinh.php',
- '9e483de83558c98f7d3feaa402c78cb3' => $vendorDir . '/markbaker/complex/classes/src/functions/atan.php',
- '36b74b5b765ded91ee58c8ee3c0e85e3' => $vendorDir . '/markbaker/complex/classes/src/functions/atanh.php',
- '05c15ee9510da7fd6bf6136f436500c0' => $vendorDir . '/markbaker/complex/classes/src/functions/conjugate.php',
- 'd3208dfbce2505e370788f9f22f6785f' => $vendorDir . '/markbaker/complex/classes/src/functions/cos.php',
- '141cf1fb3a3046f8b64534b0ebab33ca' => $vendorDir . '/markbaker/complex/classes/src/functions/cosh.php',
- 'be660df75fd0dbe7fa7c03b7434b3294' => $vendorDir . '/markbaker/complex/classes/src/functions/cot.php',
- '01e31ea298a51bc9e91517e3ce6b9e76' => $vendorDir . '/markbaker/complex/classes/src/functions/coth.php',
- '803ddd97f7b1da68982a7b087c3476f6' => $vendorDir . '/markbaker/complex/classes/src/functions/csc.php',
- '3001cdfd101ec3c32da34ee43c2e149b' => $vendorDir . '/markbaker/complex/classes/src/functions/csch.php',
- '77b2d7629ef2a93fabb8c56754a91051' => $vendorDir . '/markbaker/complex/classes/src/functions/exp.php',
- '4a4471296dec796c21d4f4b6552396a9' => $vendorDir . '/markbaker/complex/classes/src/functions/inverse.php',
- 'c3e9897e1744b88deb56fcdc39d34d85' => $vendorDir . '/markbaker/complex/classes/src/functions/ln.php',
- 'a83cacf2de942cff288de15a83afd26d' => $vendorDir . '/markbaker/complex/classes/src/functions/log2.php',
- '6a861dacc9ee2f3061241d4c7772fa21' => $vendorDir . '/markbaker/complex/classes/src/functions/log10.php',
- '4d2522d968c8ba78d6c13548a1b4200e' => $vendorDir . '/markbaker/complex/classes/src/functions/negative.php',
- 'fd587ca933fc0447fa5ab4843bdd97f7' => $vendorDir . '/markbaker/complex/classes/src/functions/pow.php',
- '383ef01c62028fc78cd4388082fce3c2' => $vendorDir . '/markbaker/complex/classes/src/functions/rho.php',
- '150fbd1b95029dc47292da97ecab9375' => $vendorDir . '/markbaker/complex/classes/src/functions/sec.php',
- '549abd9bae174286d660bdaa07407c68' => $vendorDir . '/markbaker/complex/classes/src/functions/sech.php',
- '6bfbf5eaea6b17a0ed85cb21ba80370c' => $vendorDir . '/markbaker/complex/classes/src/functions/sin.php',
- '22efe13f1a497b8e199540ae2d9dc59c' => $vendorDir . '/markbaker/complex/classes/src/functions/sinh.php',
- 'e90135ab8e787795a509ed7147de207d' => $vendorDir . '/markbaker/complex/classes/src/functions/sqrt.php',
- 'bb0a7923ffc6a90919cd64ec54ff06bc' => $vendorDir . '/markbaker/complex/classes/src/functions/tan.php',
- '2d302f32ce0fd4e433dd91c5bb404a28' => $vendorDir . '/markbaker/complex/classes/src/functions/tanh.php',
- '24dd4658a952171a4ee79218c4f9fd06' => $vendorDir . '/markbaker/complex/classes/src/functions/theta.php',
- 'e49b7876281d6f5bc39536dde96d1f4a' => $vendorDir . '/markbaker/complex/classes/src/operations/add.php',
- '47596e02b43cd6da7700134fd08f88cf' => $vendorDir . '/markbaker/complex/classes/src/operations/subtract.php',
- '883af48563631547925fa4c3b48ead07' => $vendorDir . '/markbaker/complex/classes/src/operations/multiply.php',
- 'f190e3308e6ca23234a2875edc985c03' => $vendorDir . '/markbaker/complex/classes/src/operations/divideby.php',
- 'ac9e33ce6841aa5bf5d16d465a2f03a7' => $vendorDir . '/markbaker/complex/classes/src/operations/divideinto.php',
-);
diff --git a/lib/phpspreadsheet/vendor/composer/autoload_namespaces.php b/lib/phpspreadsheet/vendor/composer/autoload_namespaces.php
index 97f041d5b3a..b7fc0125dbc 100644
--- a/lib/phpspreadsheet/vendor/composer/autoload_namespaces.php
+++ b/lib/phpspreadsheet/vendor/composer/autoload_namespaces.php
@@ -5,4 +5,5 @@
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
-return array();
+return array(
+);
diff --git a/lib/phpspreadsheet/vendor/composer/autoload_psr4.php b/lib/phpspreadsheet/vendor/composer/autoload_psr4.php
index def19d8baa9..a86a0028118 100644
--- a/lib/phpspreadsheet/vendor/composer/autoload_psr4.php
+++ b/lib/phpspreadsheet/vendor/composer/autoload_psr4.php
@@ -6,13 +6,10 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
- 'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'),
- 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
- 'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'),
'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'),
'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'),
);
diff --git a/lib/phpspreadsheet/vendor/composer/autoload_real.php b/lib/phpspreadsheet/vendor/composer/autoload_real.php
index 9a3431ed05d..0745ab34d43 100644
--- a/lib/phpspreadsheet/vendor/composer/autoload_real.php
+++ b/lib/phpspreadsheet/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
-class ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b
+class ComposerAutoloaderInit47a82a2b792e78d18b5f54d474d822dc
{
private static $loader;
@@ -24,15 +24,15 @@ class ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b
require __DIR__ . '/platform_check.php';
- spl_autoload_register(array('ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b', 'loadClassLoader'), true, true);
- self::$loader = $loader = new \Composer\Autoload\ClassLoader();
- spl_autoload_unregister(array('ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b', 'loadClassLoader'));
+ spl_autoload_register(array('ComposerAutoloaderInit47a82a2b792e78d18b5f54d474d822dc', 'loadClassLoader'), true, true);
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
+ spl_autoload_unregister(array('ComposerAutoloaderInit47a82a2b792e78d18b5f54d474d822dc', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
- call_user_func(\Composer\Autoload\ComposerStaticInit596026141085fc6d905fff9fde42dc1b::getInitializer($loader));
+ call_user_func(\Composer\Autoload\ComposerStaticInit47a82a2b792e78d18b5f54d474d822dc::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
@@ -52,24 +52,6 @@ class ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b
$loader->register(true);
- if ($useStaticLoader) {
- $includeFiles = Composer\Autoload\ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$files;
- } else {
- $includeFiles = require __DIR__ . '/autoload_files.php';
- }
- foreach ($includeFiles as $fileIdentifier => $file) {
- composerRequire596026141085fc6d905fff9fde42dc1b($fileIdentifier, $file);
- }
-
return $loader;
}
}
-
-function composerRequire596026141085fc6d905fff9fde42dc1b($fileIdentifier, $file)
-{
- if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
- require $file;
-
- $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
- }
-}
diff --git a/lib/phpspreadsheet/vendor/composer/autoload_static.php b/lib/phpspreadsheet/vendor/composer/autoload_static.php
index ec49ef17ee3..bdc8de8fb9e 100644
--- a/lib/phpspreadsheet/vendor/composer/autoload_static.php
+++ b/lib/phpspreadsheet/vendor/composer/autoload_static.php
@@ -4,78 +4,9 @@
namespace Composer\Autoload;
-class ComposerStaticInit596026141085fc6d905fff9fde42dc1b
+class ComposerStaticInit47a82a2b792e78d18b5f54d474d822dc
{
- public static $files = array (
- '9d8e013a5160a09477beb8e44f8ae97b' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/adjoint.php',
- '6e78d1bdea6248d6aa117229efae50f2' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/antidiagonal.php',
- '4623d87924d94f5412fe5afbf1cef31d' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/cofactors.php',
- '901fd1f6950a637ca85f66b701a45e13' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/determinant.php',
- '83057abc0e4acc99ba80154ee5d02a49' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/diagonal.php',
- '07b7fd7a434451149b4fd477fca0ce06' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/identity.php',
- 'c8d43b340583e07ae89f2a3baef2cf89' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/inverse.php',
- '499bb10ed7a3aee2ba4c09a31a85e8d1' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/minors.php',
- '1cad2e6414d652e8b1c64e8967f6f37d' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/trace.php',
- '95a7f134ac17161d07def442b3b737e8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/transpose.php',
- 'b3a6bc628377118d4b4b8ba08d1eb949' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/add.php',
- '5fef6d0e407f3f8887266dfa4a6c534c' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/directsum.php',
- '684ba247e1385946e3babdaa054119de' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/subtract.php',
- 'aa53dcba601214d17ad405b7c291b7e8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/multiply.php',
- '75c79eb1b25749b05a47976f32b0d8a2' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/divideby.php',
- '6ab8ad87a734f276a6bcd5a0fe1289be' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/divideinto.php',
- 'abede361264e2ae69ec1eee813a101af' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/abs.php',
- '21a5860fbef5be28db5ddfbc3cca67c4' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acos.php',
- '1546e3f9d127f2a9bb2d1b6c31c26ef1' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acosh.php',
- 'd2516f7f4fba5ea5905f494b4a8262e0' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acot.php',
- '4511163d560956219b96882c0980b65e' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acoth.php',
- 'c361f5616dc2a8da4fa3e137077cd4ea' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acsc.php',
- '02d68920fc98da71991ce569c91df0f6' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acsch.php',
- '88e19525eae308b4a6aa3419364875d3' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/argument.php',
- '60e8e2d0827b58bfc904f13957e51849' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/asec.php',
- '13d2f040713999eab66c359b4d79871d' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/asech.php',
- '838ab38beb32c68a79d3cd2c007d5a04' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/asin.php',
- 'bb28eccd0f8f008333a1b3c163d604ac' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/asinh.php',
- '9e483de83558c98f7d3feaa402c78cb3' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/atan.php',
- '36b74b5b765ded91ee58c8ee3c0e85e3' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/atanh.php',
- '05c15ee9510da7fd6bf6136f436500c0' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/conjugate.php',
- 'd3208dfbce2505e370788f9f22f6785f' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/cos.php',
- '141cf1fb3a3046f8b64534b0ebab33ca' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/cosh.php',
- 'be660df75fd0dbe7fa7c03b7434b3294' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/cot.php',
- '01e31ea298a51bc9e91517e3ce6b9e76' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/coth.php',
- '803ddd97f7b1da68982a7b087c3476f6' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/csc.php',
- '3001cdfd101ec3c32da34ee43c2e149b' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/csch.php',
- '77b2d7629ef2a93fabb8c56754a91051' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/exp.php',
- '4a4471296dec796c21d4f4b6552396a9' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/inverse.php',
- 'c3e9897e1744b88deb56fcdc39d34d85' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/ln.php',
- 'a83cacf2de942cff288de15a83afd26d' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/log2.php',
- '6a861dacc9ee2f3061241d4c7772fa21' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/log10.php',
- '4d2522d968c8ba78d6c13548a1b4200e' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/negative.php',
- 'fd587ca933fc0447fa5ab4843bdd97f7' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/pow.php',
- '383ef01c62028fc78cd4388082fce3c2' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/rho.php',
- '150fbd1b95029dc47292da97ecab9375' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sec.php',
- '549abd9bae174286d660bdaa07407c68' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sech.php',
- '6bfbf5eaea6b17a0ed85cb21ba80370c' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sin.php',
- '22efe13f1a497b8e199540ae2d9dc59c' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sinh.php',
- 'e90135ab8e787795a509ed7147de207d' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sqrt.php',
- 'bb0a7923ffc6a90919cd64ec54ff06bc' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/tan.php',
- '2d302f32ce0fd4e433dd91c5bb404a28' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/tanh.php',
- '24dd4658a952171a4ee79218c4f9fd06' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/theta.php',
- 'e49b7876281d6f5bc39536dde96d1f4a' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/add.php',
- '47596e02b43cd6da7700134fd08f88cf' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/subtract.php',
- '883af48563631547925fa4c3b48ead07' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/multiply.php',
- 'f190e3308e6ca23234a2875edc985c03' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideby.php',
- 'ac9e33ce6841aa5bf5d16d465a2f03a7' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideinto.php',
- );
-
public static $prefixLengthsPsr4 = array (
- 'Z' =>
- array (
- 'ZipStream\\' => 10,
- ),
- 'S' =>
- array (
- 'Symfony\\Polyfill\\Mbstring\\' => 26,
- ),
'P' =>
array (
'Psr\\SimpleCache\\' => 16,
@@ -85,7 +16,6 @@ class ComposerStaticInit596026141085fc6d905fff9fde42dc1b
),
'M' =>
array (
- 'MyCLabs\\Enum\\' => 13,
'Matrix\\' => 7,
),
'C' =>
@@ -95,14 +25,6 @@ class ComposerStaticInit596026141085fc6d905fff9fde42dc1b
);
public static $prefixDirsPsr4 = array (
- 'ZipStream\\' =>
- array (
- 0 => __DIR__ . '/..' . '/maennchen/zipstream-php/src',
- ),
- 'Symfony\\Polyfill\\Mbstring\\' =>
- array (
- 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
- ),
'Psr\\SimpleCache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
@@ -120,10 +42,6 @@ class ComposerStaticInit596026141085fc6d905fff9fde42dc1b
array (
0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet',
),
- 'MyCLabs\\Enum\\' =>
- array (
- 0 => __DIR__ . '/..' . '/myclabs/php-enum/src',
- ),
'Matrix\\' =>
array (
0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src',
@@ -134,16 +52,6 @@ class ComposerStaticInit596026141085fc6d905fff9fde42dc1b
),
);
- public static $prefixesPsr0 = array (
- 'H' =>
- array (
- 'HTMLPurifier' =>
- array (
- 0 => __DIR__ . '/..' . '/ezyang/htmlpurifier/library',
- ),
- ),
- );
-
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
@@ -151,10 +59,9 @@ class ComposerStaticInit596026141085fc6d905fff9fde42dc1b
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
- $loader->prefixLengthsPsr4 = ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$prefixLengthsPsr4;
- $loader->prefixDirsPsr4 = ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$prefixDirsPsr4;
- $loader->prefixesPsr0 = ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$prefixesPsr0;
- $loader->classMap = ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$classMap;
+ $loader->prefixLengthsPsr4 = ComposerStaticInit47a82a2b792e78d18b5f54d474d822dc::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit47a82a2b792e78d18b5f54d474d822dc::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInit47a82a2b792e78d18b5f54d474d822dc::$classMap;
}, null, ClassLoader::class);
}
diff --git a/lib/phpspreadsheet/vendor/composer/installed.json b/lib/phpspreadsheet/vendor/composer/installed.json
index aac52851b06..e9a76b7b20d 100644
--- a/lib/phpspreadsheet/vendor/composer/installed.json
+++ b/lib/phpspreadsheet/vendor/composer/installed.json
@@ -1,149 +1,18 @@
{
"packages": [
- {
- "name": "ezyang/htmlpurifier",
- "version": "v4.13.0",
- "version_normalized": "4.13.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/ezyang/htmlpurifier.git",
- "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
- "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
- "shasum": ""
- },
- "require": {
- "php": ">=5.2"
- },
- "require-dev": {
- "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
- },
- "time": "2020-06-29T00:56:53+00:00",
- "type": "library",
- "installation-source": "dist",
- "autoload": {
- "psr-0": {
- "HTMLPurifier": "library/"
- },
- "files": [
- "library/HTMLPurifier.composer.php"
- ],
- "exclude-from-classmap": [
- "/library/HTMLPurifier/Language/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "LGPL-2.1-or-later"
- ],
- "authors": [
- {
- "name": "Edward Z. Yang",
- "email": "admin@htmlpurifier.org",
- "homepage": "http://ezyang.com"
- }
- ],
- "description": "Standards compliant HTML filter written in PHP",
- "homepage": "http://htmlpurifier.org/",
- "keywords": [
- "html"
- ],
- "support": {
- "issues": "https://github.com/ezyang/htmlpurifier/issues",
- "source": "https://github.com/ezyang/htmlpurifier/tree/master"
- },
- "install-path": "../ezyang/htmlpurifier"
- },
- {
- "name": "maennchen/zipstream-php",
- "version": "2.1.0",
- "version_normalized": "2.1.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/maennchen/ZipStream-PHP.git",
- "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58",
- "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58",
- "shasum": ""
- },
- "require": {
- "myclabs/php-enum": "^1.5",
- "php": ">= 7.1",
- "psr/http-message": "^1.0",
- "symfony/polyfill-mbstring": "^1.0"
- },
- "require-dev": {
- "ext-zip": "*",
- "guzzlehttp/guzzle": ">= 6.3",
- "mikey179/vfsstream": "^1.6",
- "phpunit/phpunit": ">= 7.5"
- },
- "time": "2020-05-30T13:11:16+00:00",
- "type": "library",
- "installation-source": "dist",
- "autoload": {
- "psr-4": {
- "ZipStream\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Paul Duncan",
- "email": "pabs@pablotron.org"
- },
- {
- "name": "Jonatan Männchen",
- "email": "jonatan@maennchen.ch"
- },
- {
- "name": "Jesse Donat",
- "email": "donatj@gmail.com"
- },
- {
- "name": "András Kolesár",
- "email": "kolesar@kolesar.hu"
- }
- ],
- "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
- "keywords": [
- "stream",
- "zip"
- ],
- "support": {
- "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
- "source": "https://github.com/maennchen/ZipStream-PHP/tree/master"
- },
- "funding": [
- {
- "url": "https://opencollective.com/zipstream",
- "type": "open_collective"
- }
- ],
- "install-path": "../maennchen/zipstream-php"
- },
{
"name": "markbaker/complex",
- "version": "2.0.0",
- "version_normalized": "2.0.0.0",
+ "version": "3.0.1",
+ "version_normalized": "3.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/MarkBaker/PHPComplex.git",
- "reference": "9999f1432fae467bc93c53f357105b4c31bb994c"
+ "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/9999f1432fae467bc93c53f357105b4c31bb994c",
- "reference": "9999f1432fae467bc93c53f357105b4c31bb994c",
+ "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/ab8bc271e404909db09ff2d5ffa1e538085c0f22",
+ "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22",
"shasum": ""
},
"require": {
@@ -152,64 +21,16 @@
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"phpcompatibility/php-compatibility": "^9.0",
- "phpdocumentor/phpdocumentor": "2.*",
- "phploc/phploc": "^4.0",
- "phpmd/phpmd": "2.*",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
- "sebastian/phpcpd": "^4.0",
"squizlabs/php_codesniffer": "^3.4"
},
- "time": "2020-08-26T10:42:07+00:00",
+ "time": "2021-06-29T15:32:53+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Complex\\": "classes/src/"
- },
- "files": [
- "classes/src/functions/abs.php",
- "classes/src/functions/acos.php",
- "classes/src/functions/acosh.php",
- "classes/src/functions/acot.php",
- "classes/src/functions/acoth.php",
- "classes/src/functions/acsc.php",
- "classes/src/functions/acsch.php",
- "classes/src/functions/argument.php",
- "classes/src/functions/asec.php",
- "classes/src/functions/asech.php",
- "classes/src/functions/asin.php",
- "classes/src/functions/asinh.php",
- "classes/src/functions/atan.php",
- "classes/src/functions/atanh.php",
- "classes/src/functions/conjugate.php",
- "classes/src/functions/cos.php",
- "classes/src/functions/cosh.php",
- "classes/src/functions/cot.php",
- "classes/src/functions/coth.php",
- "classes/src/functions/csc.php",
- "classes/src/functions/csch.php",
- "classes/src/functions/exp.php",
- "classes/src/functions/inverse.php",
- "classes/src/functions/ln.php",
- "classes/src/functions/log2.php",
- "classes/src/functions/log10.php",
- "classes/src/functions/negative.php",
- "classes/src/functions/pow.php",
- "classes/src/functions/rho.php",
- "classes/src/functions/sec.php",
- "classes/src/functions/sech.php",
- "classes/src/functions/sin.php",
- "classes/src/functions/sinh.php",
- "classes/src/functions/sqrt.php",
- "classes/src/functions/tan.php",
- "classes/src/functions/tanh.php",
- "classes/src/functions/theta.php",
- "classes/src/operations/add.php",
- "classes/src/operations/subtract.php",
- "classes/src/operations/multiply.php",
- "classes/src/operations/divideby.php",
- "classes/src/operations/divideinto.php"
- ]
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -229,27 +50,27 @@
],
"support": {
"issues": "https://github.com/MarkBaker/PHPComplex/issues",
- "source": "https://github.com/MarkBaker/PHPComplex/tree/PHP8"
+ "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.1"
},
"install-path": "../markbaker/complex"
},
{
"name": "markbaker/matrix",
- "version": "2.0.0",
- "version_normalized": "2.0.0.0",
+ "version": "3.0.0",
+ "version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/MarkBaker/PHPMatrix.git",
- "reference": "9567d9c4c519fbe40de01dbd1e4469dbbb66f46a"
+ "reference": "c66aefcafb4f6c269510e9ac46b82619a904c576"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/9567d9c4c519fbe40de01dbd1e4469dbbb66f46a",
- "reference": "9567d9c4c519fbe40de01dbd1e4469dbbb66f46a",
+ "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/c66aefcafb4f6c269510e9ac46b82619a904c576",
+ "reference": "c66aefcafb4f6c269510e9ac46b82619a904c576",
"shasum": ""
},
"require": {
- "php": "^7.2 || ^8.0"
+ "php": "^7.1 || ^8.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
@@ -261,31 +82,13 @@
"sebastian/phpcpd": "^4.0",
"squizlabs/php_codesniffer": "^3.4"
},
- "time": "2020-08-28T17:11:00+00:00",
+ "time": "2021-07-01T19:01:15+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Matrix\\": "classes/src/"
- },
- "files": [
- "classes/src/functions/adjoint.php",
- "classes/src/functions/antidiagonal.php",
- "classes/src/functions/cofactors.php",
- "classes/src/functions/determinant.php",
- "classes/src/functions/diagonal.php",
- "classes/src/functions/identity.php",
- "classes/src/functions/inverse.php",
- "classes/src/functions/minors.php",
- "classes/src/functions/trace.php",
- "classes/src/functions/transpose.php",
- "classes/src/operations/add.php",
- "classes/src/operations/directsum.php",
- "classes/src/operations/subtract.php",
- "classes/src/operations/multiply.php",
- "classes/src/operations/divideby.php",
- "classes/src/operations/divideinto.php"
- ]
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -306,86 +109,23 @@
],
"support": {
"issues": "https://github.com/MarkBaker/PHPMatrix/issues",
- "source": "https://github.com/MarkBaker/PHPMatrix/tree/PHP8"
+ "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.0"
},
"install-path": "../markbaker/matrix"
},
- {
- "name": "myclabs/php-enum",
- "version": "1.7.7",
- "version_normalized": "1.7.7.0",
- "source": {
- "type": "git",
- "url": "https://github.com/myclabs/php-enum.git",
- "reference": "d178027d1e679832db9f38248fcc7200647dc2b7"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/myclabs/php-enum/zipball/d178027d1e679832db9f38248fcc7200647dc2b7",
- "reference": "d178027d1e679832db9f38248fcc7200647dc2b7",
- "shasum": ""
- },
- "require": {
- "ext-json": "*",
- "php": ">=7.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^7",
- "squizlabs/php_codesniffer": "1.*",
- "vimeo/psalm": "^3.8"
- },
- "time": "2020-11-14T18:14:52+00:00",
- "type": "library",
- "installation-source": "dist",
- "autoload": {
- "psr-4": {
- "MyCLabs\\Enum\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "PHP Enum contributors",
- "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
- }
- ],
- "description": "PHP Enum implementation",
- "homepage": "http://github.com/myclabs/php-enum",
- "keywords": [
- "enum"
- ],
- "support": {
- "issues": "https://github.com/myclabs/php-enum/issues",
- "source": "https://github.com/myclabs/php-enum/tree/1.7.7"
- },
- "funding": [
- {
- "url": "https://github.com/mnapoli",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
- "type": "tidelift"
- }
- ],
- "install-path": "../myclabs/php-enum"
- },
{
"name": "phpoffice/phpspreadsheet",
- "version": "1.16.0",
- "version_normalized": "1.16.0.0",
+ "version": "1.21.0",
+ "version_normalized": "1.21.0.0",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
- "reference": "76d4323b85129d0c368149c831a07a3e258b2b50"
+ "reference": "1a359d2ccbb89c05f5dffb32711a95f4afc67964"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/76d4323b85129d0c368149c831a07a3e258b2b50",
- "reference": "76d4323b85129d0c368149c831a07a3e258b2b50",
+ "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/1a359d2ccbb89c05f5dffb32711a95f4afc67964",
+ "reference": "1a359d2ccbb89c05f5dffb32711a95f4afc67964",
"shasum": ""
},
"require": {
@@ -404,22 +144,25 @@
"ext-zlib": "*",
"ezyang/htmlpurifier": "^4.13",
"maennchen/zipstream-php": "^2.1",
- "markbaker/complex": "^1.5||^2.0",
- "markbaker/matrix": "^1.2||^2.0",
- "php": "^7.2||^8.0",
+ "markbaker/complex": "^3.0",
+ "markbaker/matrix": "^3.0",
+ "php": "^7.3 || ^8.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"psr/simple-cache": "^1.0"
},
"require-dev": {
- "dompdf/dompdf": "^0.8.5",
- "friendsofphp/php-cs-fixer": "^2.16",
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "dompdf/dompdf": "^1.0",
+ "friendsofphp/php-cs-fixer": "^3.2",
"jpgraph/jpgraph": "^4.0",
"mpdf/mpdf": "^8.0",
"phpcompatibility/php-compatibility": "^9.3",
- "phpunit/phpunit": "^8.5||^9.3",
- "squizlabs/php_codesniffer": "^3.5",
- "tecnickcom/tcpdf": "^6.3"
+ "phpstan/phpstan": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^8.5 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.6",
+ "tecnickcom/tcpdf": "^6.4"
},
"suggest": {
"dompdf/dompdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)",
@@ -427,7 +170,7 @@
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)"
},
- "time": "2020-12-31T18:03:49+00:00",
+ "time": "2022-01-06T11:10:08+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -473,7 +216,7 @@
],
"support": {
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
- "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.16.0"
+ "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.21.0"
},
"install-path": "../phpoffice/phpspreadsheet"
},
@@ -699,89 +442,6 @@
"source": "https://github.com/php-fig/simple-cache/tree/master"
},
"install-path": "../psr/simple-cache"
- },
- {
- "name": "symfony/polyfill-mbstring",
- "version": "v1.22.0",
- "version_normalized": "1.22.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
- "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1"
- },
- "suggest": {
- "ext-mbstring": "For best performance"
- },
- "time": "2021-01-07T16:49:33+00:00",
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "1.22-dev"
- },
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
- "installation-source": "dist",
- "autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Mbstring\\": ""
- },
- "files": [
- "bootstrap.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for the Mbstring extension",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "mbstring",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "install-path": "../symfony/polyfill-mbstring"
}
],
"dev": true,
diff --git a/lib/phpspreadsheet/vendor/composer/installed.php b/lib/phpspreadsheet/vendor/composer/installed.php
index 1ca76964218..91c054a37b6 100644
--- a/lib/phpspreadsheet/vendor/composer/installed.php
+++ b/lib/phpspreadsheet/vendor/composer/installed.php
@@ -1,123 +1,110 @@
-
- array (
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'aliases' =>
- array (
+ array(
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'reference' => NULL,
+ 'name' => '__root__',
+ 'dev' => true,
),
- 'reference' => '70d1b7d67bc280b21f450db41728869bd1bda8d8',
- 'name' => '__root__',
- ),
- 'versions' =>
- array (
- '__root__' =>
- array (
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'aliases' =>
- array (
- ),
- 'reference' => '70d1b7d67bc280b21f450db41728869bd1bda8d8',
+ 'versions' => array(
+ '__root__' => array(
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'reference' => NULL,
+ 'dev_requirement' => false,
+ ),
+ 'ezyang/htmlpurifier' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '*',
+ ),
+ ),
+ 'maennchen/zipstream-php' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '*',
+ ),
+ ),
+ 'markbaker/complex' => array(
+ 'pretty_version' => '3.0.1',
+ 'version' => '3.0.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../markbaker/complex',
+ 'aliases' => array(),
+ 'reference' => 'ab8bc271e404909db09ff2d5ffa1e538085c0f22',
+ 'dev_requirement' => false,
+ ),
+ 'markbaker/matrix' => array(
+ 'pretty_version' => '3.0.0',
+ 'version' => '3.0.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../markbaker/matrix',
+ 'aliases' => array(),
+ 'reference' => 'c66aefcafb4f6c269510e9ac46b82619a904c576',
+ 'dev_requirement' => false,
+ ),
+ 'myclabs/php-enum' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '*',
+ ),
+ ),
+ 'phpoffice/phpspreadsheet' => array(
+ 'pretty_version' => '1.21.0',
+ 'version' => '1.21.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../phpoffice/phpspreadsheet',
+ 'aliases' => array(),
+ 'reference' => '1a359d2ccbb89c05f5dffb32711a95f4afc67964',
+ 'dev_requirement' => false,
+ ),
+ 'psr/http-client' => array(
+ 'pretty_version' => '1.0.1',
+ 'version' => '1.0.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/http-client',
+ 'aliases' => array(),
+ 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
+ 'dev_requirement' => false,
+ ),
+ 'psr/http-factory' => array(
+ 'pretty_version' => '1.0.1',
+ 'version' => '1.0.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/http-factory',
+ 'aliases' => array(),
+ 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
+ 'dev_requirement' => false,
+ ),
+ 'psr/http-message' => array(
+ 'pretty_version' => '1.0.1',
+ 'version' => '1.0.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/http-message',
+ 'aliases' => array(),
+ 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
+ 'dev_requirement' => false,
+ ),
+ 'psr/simple-cache' => array(
+ 'pretty_version' => '1.0.1',
+ 'version' => '1.0.1.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/simple-cache',
+ 'aliases' => array(),
+ 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b',
+ 'dev_requirement' => false,
+ ),
+ 'symfony/polyfill-mbstring' => array(
+ 'dev_requirement' => false,
+ 'replaced' => array(
+ 0 => '*',
+ ),
+ ),
),
- 'ezyang/htmlpurifier' =>
- array (
- 'pretty_version' => 'v4.13.0',
- 'version' => '4.13.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '08e27c97e4c6ed02f37c5b2b20488046c8d90d75',
- ),
- 'maennchen/zipstream-php' =>
- array (
- 'pretty_version' => '2.1.0',
- 'version' => '2.1.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => 'c4c5803cc1f93df3d2448478ef79394a5981cc58',
- ),
- 'markbaker/complex' =>
- array (
- 'pretty_version' => '2.0.0',
- 'version' => '2.0.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '9999f1432fae467bc93c53f357105b4c31bb994c',
- ),
- 'markbaker/matrix' =>
- array (
- 'pretty_version' => '2.0.0',
- 'version' => '2.0.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '9567d9c4c519fbe40de01dbd1e4469dbbb66f46a',
- ),
- 'myclabs/php-enum' =>
- array (
- 'pretty_version' => '1.7.7',
- 'version' => '1.7.7.0',
- 'aliases' =>
- array (
- ),
- 'reference' => 'd178027d1e679832db9f38248fcc7200647dc2b7',
- ),
- 'phpoffice/phpspreadsheet' =>
- array (
- 'pretty_version' => '1.16.0',
- 'version' => '1.16.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '76d4323b85129d0c368149c831a07a3e258b2b50',
- ),
- 'psr/http-client' =>
- array (
- 'pretty_version' => '1.0.1',
- 'version' => '1.0.1.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
- ),
- 'psr/http-factory' =>
- array (
- 'pretty_version' => '1.0.1',
- 'version' => '1.0.1.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
- ),
- 'psr/http-message' =>
- array (
- 'pretty_version' => '1.0.1',
- 'version' => '1.0.1.0',
- 'aliases' =>
- array (
- ),
- 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
- ),
- 'psr/simple-cache' =>
- array (
- 'pretty_version' => '1.0.1',
- 'version' => '1.0.1.0',
- 'aliases' =>
- array (
- ),
- 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b',
- ),
- 'symfony/polyfill-mbstring' =>
- array (
- 'pretty_version' => 'v1.22.0',
- 'version' => '1.22.0.0',
- 'aliases' =>
- array (
- ),
- 'reference' => 'f377a3dd1fde44d37b9831d68dc8dea3ffd28e13',
- ),
- ),
);
diff --git a/lib/phpspreadsheet/vendor/composer/platform_check.php b/lib/phpspreadsheet/vendor/composer/platform_check.php
index 589e9e770b9..92370c5a0c9 100644
--- a/lib/phpspreadsheet/vendor/composer/platform_check.php
+++ b/lib/phpspreadsheet/vendor/composer/platform_check.php
@@ -4,8 +4,8 @@
$issues = array();
-if (!(PHP_VERSION_ID >= 70200)) {
- $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
+if (!(PHP_VERSION_ID >= 70300)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/README.md b/lib/phpspreadsheet/vendor/markbaker/complex/README.md
index c306394eeb6..27afc59c4eb 100644
--- a/lib/phpspreadsheet/vendor/markbaker/complex/README.md
+++ b/lib/phpspreadsheet/vendor/markbaker/complex/README.md
@@ -3,11 +3,13 @@ PHPComplex
---
-PHP Class for handling Complex numbers
+PHP Class Library for working with Complex numbers
-Master: [](http://travis-ci.org/MarkBaker/PHPComplex)
+[](https://github.com/MarkBaker/PHPComplex/actions)
+[](https://packagist.org/packages/markbaker/complex)
+[](https://packagist.org/packages/markbaker/complex)
+[](https://packagist.org/packages/markbaker/complex)
-Develop: [](http://travis-ci.org/MarkBaker/PHPComplex)
[](https://xkcd.com/2028/)
@@ -63,19 +65,37 @@ together with functions for
---
+# Installation
+
+```shell
+composer require markbaker/complex:^3.0
+```
+
+# Important BC Note
+
+If you've previously been using procedural calls to functions and operations using this library, then from version 3.0 you should use [MarkBaker/PHPComplexFunctions](https://github.com/MarkBaker/PHPComplexFunctions) instead (available on packagist as [markbaker/complex-functions](https://packagist.org/packages/markbaker/complex-functions)).
+
+You'll need to replace `markbaker/complex`in your `composer.json` file with the new library, but otherwise there should be no difference in the namespacing, or in the way that you have called the Complex functions in the past, so no actual code changes are required.
+
+```shell
+composer require markbaker/complex-functions:^1.0
+```
+
+You should not reference this library (`markbaker/complex`) in your `composer.json`, composer wil take care of that for you.
+
# Usage
To create a new complex object, you can provide either the real, imaginary and suffix parts as individual values, or as an array of values passed passed to the constructor; or a string representing the value. e.g
-```
+```php
$real = 1.23;
$imaginary = -4.56;
$suffix = 'i';
$complexObject = new Complex\Complex($real, $imaginary, $suffix);
```
-or
-```
+or as an array
+```php
$real = 1.23;
$imaginary = -4.56;
$suffix = 'i';
@@ -84,8 +104,8 @@ $arguments = [$real, $imaginary, $suffix];
$complexObject = new Complex\Complex($arguments);
```
-or
-```
+or as a string
+```php
$complexString = '1.23-4.56i';
$complexObject = new Complex\Complex($complexString);
@@ -98,57 +118,54 @@ This also allows you to chain multiple methods as you would for a fluent interfa
To perform mathematical operations with Complex values, you can call the appropriate method against a complex value, passing other values as arguments
-```
+```php
$complexString1 = '1.23-4.56i';
$complexString2 = '2.34+5.67i';
$complexObject = new Complex\Complex($complexString1);
echo $complexObject->add($complexString2);
```
-or pass all values to the appropriate function
-```
+
+or use the static Operation methods
+```php
$complexString1 = '1.23-4.56i';
$complexString2 = '2.34+5.67i';
-echo Complex\add($complexString1, $complexString2);
+echo Complex\Operations::add($complexString1, $complexString2);
```
If you want to perform the same operation against multiple values (e.g. to add three or more complex numbers), then you can pass multiple arguments to any of the operations.
-You can pass these arguments as Complex objects, or as an array or string that will parse to a complex object.
+You can pass these arguments as Complex objects, or as an array, or string that will parse to a complex object.
## Using functions
When calling any of the available functions for a complex value, you can either call the relevant method for the Complex object
-```
+```php
$complexString = '1.23-4.56i';
$complexObject = new Complex\Complex($complexString);
echo $complexObject->sinh();
```
-or you can call the function as you would in procedural code, passing the Complex object as an argument
+
+or use the static Functions methods
+```php
+$complexString = '1.23-4.56i';
+
+echo Complex\Functions::sinh($complexString);
```
+As with operations, you can pass these arguments as Complex objects, or as an array or string that will parse to a complex object.
+
+
+In the case of the `pow()` function (the only implemented function that requires an additional argument) you need to pass both arguments when calling the function
+
+```php
$complexString = '1.23-4.56i';
$complexObject = new Complex\Complex($complexString);
-echo Complex\sinh($complexObject);
-```
-When called procedurally using the function, you can pass in the argument as a Complex object, or as an array or string that will parse to a complex object.
-```
-$complexString = '1.23-4.56i';
-
-echo Complex\sinh($complexString);
-```
-
-In the case of the `pow()` function (the only implemented function that requires an additional argument) you need to pass both arguments when calling the function procedurally
-
-```
-$complexString = '1.23-4.56i';
-
-$complexObject = new Complex\Complex($complexString);
-echo Complex\pow($complexObject, 2);
+echo Complex\Functions::pow($complexObject, 2);
```
or pass the additional argument when calling the method
-```
+```php
$complexString = '1.23-4.56i';
$complexObject = new Complex\Complex($complexString);
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/Autoloader.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/Autoloader.php
deleted file mode 100644
index 792ecef0d97..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/Autoloader.php
+++ /dev/null
@@ -1,53 +0,0 @@
-regex = $regex;
- parent::__construct($it, $regex);
- }
-}
-
-class FilenameFilter extends FilesystemRegexFilter
-{
- // Filter files against the regex
- public function accept()
- {
- return (!$this->isFile() || preg_match($this->regex, $this->getFilename()));
- }
-}
-
-
-$srcFolder = __DIR__ . DIRECTORY_SEPARATOR . 'src';
-$srcDirectory = new RecursiveDirectoryIterator($srcFolder);
-
-$filteredFileList = new FilenameFilter($srcDirectory, '/(?:php)$/i');
-$filteredFileList = new FilenameFilter($filteredFileList, '/^(?!.*(Complex|Exception)\.php).*$/i');
-
-foreach (new RecursiveIteratorIterator($filteredFileList) as $file) {
- if ($file->isFile()) {
- include_once $file;
- }
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php
index f7ba162188b..25414ee612e 100644
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php
+++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php
@@ -186,7 +186,7 @@ class Complex
// Set parsed values in our properties
$this->realPart = (float) $realPart;
$this->imaginaryPart = (float) $imaginaryPart;
- $this->suffix = strtolower($suffix);
+ $this->suffix = strtolower($suffix ?? '');
}
/**
@@ -377,13 +377,11 @@ class Complex
// Test for function calls
if (in_array($functionName, self::$functions, true)) {
- $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
- return $functionName($this, ...$arguments);
+ return Functions::$functionName($this, ...$arguments);
}
// Test for operation calls
if (in_array($functionName, self::$operations, true)) {
- $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
- return $functionName($this, ...$arguments);
+ return Operations::$functionName($this, ...$arguments);
}
throw new Exception('Complex Function or Operation does not exist');
}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Functions.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Functions.php
new file mode 100644
index 00000000000..ba593ac3683
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Functions.php
@@ -0,0 +1,805 @@
+getReal() - $invsqrt->getImaginary(),
+ $complex->getImaginary() + $invsqrt->getReal()
+ );
+ $log = self::ln($adjust);
+
+ return new Complex(
+ $log->getImaginary(),
+ -1 * $log->getReal()
+ );
+ }
+
+ /**
+ * Returns the inverse hyperbolic cosine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic cosine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function acosh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal() && ($complex->getReal() > 1)) {
+ return new Complex(\acosh($complex->getReal()));
+ }
+
+ $acosh = self::acos($complex)
+ ->reverse();
+ if ($acosh->getReal() < 0.0) {
+ $acosh = $acosh->invertReal();
+ }
+
+ return $acosh;
+ }
+
+ /**
+ * Returns the inverse cotangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse cotangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function acot($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::atan(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse hyperbolic cotangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic cotangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function acoth($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::atanh(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse cosecant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse cosecant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function acsc($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::asin(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse hyperbolic cosecant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic cosecant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function acsch($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::asinh(self::inverse($complex));
+ }
+
+ /**
+ * Returns the argument of a complex number.
+ * Also known as the theta of the complex number, i.e. the angle in radians
+ * from the real axis to the representation of the number in polar coordinates.
+ *
+ * This function is a synonym for theta()
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return float The argument (or theta) value of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ *
+ * @see theta
+ */
+ public static function argument($complex): float
+ {
+ return self::theta($complex);
+ }
+
+ /**
+ * Returns the inverse secant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse secant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function asec($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::acos(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse hyperbolic secant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic secant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function asech($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::acosh(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse sine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse sine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function asin($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ $square = Operations::multiply($complex, $complex);
+ $invsqrt = new Complex(1.0);
+ $invsqrt = Operations::subtract($invsqrt, $square);
+ $invsqrt = self::sqrt($invsqrt);
+ $adjust = new Complex(
+ $invsqrt->getReal() - $complex->getImaginary(),
+ $invsqrt->getImaginary() + $complex->getReal()
+ );
+ $log = self::ln($adjust);
+
+ return new Complex(
+ $log->getImaginary(),
+ -1 * $log->getReal()
+ );
+ }
+
+ /**
+ * Returns the inverse hyperbolic sine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic sine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function asinh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal() && ($complex->getReal() > 1)) {
+ return new Complex(\asinh($complex->getReal()));
+ }
+
+ $asinh = clone $complex;
+ $asinh = $asinh->reverse()
+ ->invertReal();
+ $asinh = self::asin($asinh);
+
+ return $asinh->reverse()
+ ->invertImaginary();
+ }
+
+ /**
+ * Returns the inverse tangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse tangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function atan($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\atan($complex->getReal()));
+ }
+
+ $t1Value = new Complex(-1 * $complex->getImaginary(), $complex->getReal());
+ $uValue = new Complex(1, 0);
+
+ $d1Value = clone $uValue;
+ $d1Value = Operations::subtract($d1Value, $t1Value);
+ $d2Value = Operations::add($t1Value, $uValue);
+ $uResult = $d1Value->divideBy($d2Value);
+ $uResult = self::ln($uResult);
+
+ return new Complex(
+ (($uResult->getImaginary() == M_PI) ? -M_PI : $uResult->getImaginary()) * -0.5,
+ $uResult->getReal() * 0.5,
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the inverse hyperbolic tangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic tangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function atanh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ $real = $complex->getReal();
+ if ($real >= -1.0 && $real <= 1.0) {
+ return new Complex(\atanh($real));
+ } else {
+ return new Complex(\atanh(1 / $real), (($real < 0.0) ? M_PI_2 : -1 * M_PI_2));
+ }
+ }
+
+ $iComplex = clone $complex;
+ $iComplex = $iComplex->invertImaginary()
+ ->reverse();
+ return self::atan($iComplex)
+ ->invertReal()
+ ->reverse();
+ }
+
+ /**
+ * Returns the complex conjugate of a complex number
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The conjugate of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function conjugate($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return new Complex(
+ $complex->getReal(),
+ -1 * $complex->getImaginary(),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the cosine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The cosine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function cos($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\cos($complex->getReal()));
+ }
+
+ return self::conjugate(
+ new Complex(
+ \cos($complex->getReal()) * \cosh($complex->getImaginary()),
+ \sin($complex->getReal()) * \sinh($complex->getImaginary()),
+ $complex->getSuffix()
+ )
+ );
+ }
+
+ /**
+ * Returns the hyperbolic cosine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic cosine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function cosh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\cosh($complex->getReal()));
+ }
+
+ return new Complex(
+ \cosh($complex->getReal()) * \cos($complex->getImaginary()),
+ \sinh($complex->getReal()) * \sin($complex->getImaginary()),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the cotangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The cotangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function cot($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::inverse(self::tan($complex));
+ }
+
+ /**
+ * Returns the hyperbolic cotangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic cotangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function coth($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::inverse(self::tanh($complex));
+ }
+
+ /**
+ * Returns the cosecant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The cosecant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function csc($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::inverse(self::sin($complex));
+ }
+
+ /**
+ * Returns the hyperbolic cosecant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic cosecant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function csch($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::inverse(self::sinh($complex));
+ }
+
+ /**
+ * Returns the exponential of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The exponential of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function exp($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (($complex->getReal() == 0.0) && (\abs($complex->getImaginary()) == M_PI)) {
+ return new Complex(-1.0, 0.0);
+ }
+
+ $rho = \exp($complex->getReal());
+
+ return new Complex(
+ $rho * \cos($complex->getImaginary()),
+ $rho * \sin($complex->getImaginary()),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the inverse of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If function would result in a division by zero
+ */
+ public static function inverse($complex): Complex
+ {
+ $complex = clone Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ return $complex->divideInto(1.0);
+ }
+
+ /**
+ * Returns the natural logarithm of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The natural logarithm of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If the real and the imaginary parts are both zero
+ */
+ public static function ln($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
+ throw new InvalidArgumentException();
+ }
+
+ return new Complex(
+ \log(self::rho($complex)),
+ self::theta($complex),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the base-2 logarithm of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The base-2 logarithm of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If the real and the imaginary parts are both zero
+ */
+ public static function log2($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
+ throw new InvalidArgumentException();
+ } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) {
+ return new Complex(\log($complex->getReal(), 2), 0.0, $complex->getSuffix());
+ }
+
+ return self::ln($complex)
+ ->multiply(\log(Complex::EULER, 2));
+ }
+
+ /**
+ * Returns the common logarithm (base 10) of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The common logarithm (base 10) of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If the real and the imaginary parts are both zero
+ */
+ public static function log10($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
+ throw new InvalidArgumentException();
+ } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) {
+ return new Complex(\log10($complex->getReal()), 0.0, $complex->getSuffix());
+ }
+
+ return self::ln($complex)
+ ->multiply(\log10(Complex::EULER));
+ }
+
+ /**
+ * Returns the negative of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The negative value of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ *
+ * @see rho
+ *
+ */
+ public static function negative($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return new Complex(
+ -1 * $complex->getReal(),
+ -1 * $complex->getImaginary(),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns a complex number raised to a power.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @param float|integer $power The power to raise this value to
+ * @return Complex The complex argument raised to the real power.
+ * @throws Exception If the power argument isn't a valid real
+ */
+ public static function pow($complex, $power): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (!is_numeric($power)) {
+ throw new Exception('Power argument must be a real number');
+ }
+
+ if ($complex->getImaginary() == 0.0 && $complex->getReal() >= 0.0) {
+ return new Complex(\pow($complex->getReal(), $power));
+ }
+
+ $rValue = \sqrt(($complex->getReal() * $complex->getReal()) + ($complex->getImaginary() * $complex->getImaginary()));
+ $rPower = \pow($rValue, $power);
+ $theta = $complex->argument() * $power;
+ if ($theta == 0) {
+ return new Complex(1);
+ }
+
+ return new Complex($rPower * \cos($theta), $rPower * \sin($theta), $complex->getSuffix());
+ }
+
+ /**
+ * Returns the rho of a complex number.
+ * This is the distance/radius from the centrepoint to the representation of the number in polar coordinates.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return float The rho value of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function rho($complex): float
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return \sqrt(
+ ($complex->getReal() * $complex->getReal()) +
+ ($complex->getImaginary() * $complex->getImaginary())
+ );
+ }
+
+ /**
+ * Returns the secant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The secant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function sec($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::inverse(self::cos($complex));
+ }
+
+ /**
+ * Returns the hyperbolic secant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic secant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function sech($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::inverse(self::cosh($complex));
+ }
+
+ /**
+ * Returns the sine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The sine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function sin($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\sin($complex->getReal()));
+ }
+
+ return new Complex(
+ \sin($complex->getReal()) * \cosh($complex->getImaginary()),
+ \cos($complex->getReal()) * \sinh($complex->getImaginary()),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the hyperbolic sine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic sine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function sinh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\sinh($complex->getReal()));
+ }
+
+ return new Complex(
+ \sinh($complex->getReal()) * \cos($complex->getImaginary()),
+ \cosh($complex->getReal()) * \sin($complex->getImaginary()),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the square root of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The Square root of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function sqrt($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ $theta = self::theta($complex);
+ $delta1 = \cos($theta / 2);
+ $delta2 = \sin($theta / 2);
+ $rho = \sqrt(self::rho($complex));
+
+ return new Complex($delta1 * $rho, $delta2 * $rho, $complex->getSuffix());
+ }
+
+ /**
+ * Returns the tangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The tangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If function would result in a division by zero
+ */
+ public static function tan($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\tan($complex->getReal()));
+ }
+
+ $real = $complex->getReal();
+ $imaginary = $complex->getImaginary();
+ $divisor = 1 + \pow(\tan($real), 2) * \pow(\tanh($imaginary), 2);
+ if ($divisor == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ return new Complex(
+ \pow(self::sech($imaginary)->getReal(), 2) * \tan($real) / $divisor,
+ \pow(self::sec($real)->getReal(), 2) * \tanh($imaginary) / $divisor,
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the hyperbolic tangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic tangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function tanh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+ $real = $complex->getReal();
+ $imaginary = $complex->getImaginary();
+ $divisor = \cos($imaginary) * \cos($imaginary) + \sinh($real) * \sinh($real);
+ if ($divisor == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ return new Complex(
+ \sinh($real) * \cosh($real) / $divisor,
+ 0.5 * \sin(2 * $imaginary) / $divisor,
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the theta of a complex number.
+ * This is the angle in radians from the real axis to the representation of the number in polar coordinates.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return float The theta value of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function theta($complex): float
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0) {
+ if ($complex->isReal()) {
+ return 0.0;
+ } elseif ($complex->getImaginary() < 0.0) {
+ return M_PI / -2;
+ }
+ return M_PI / 2;
+ } elseif ($complex->getReal() > 0.0) {
+ return \atan($complex->getImaginary() / $complex->getReal());
+ } elseif ($complex->getImaginary() < 0.0) {
+ return -(M_PI - \atan(\abs($complex->getImaginary()) / \abs($complex->getReal())));
+ }
+
+ return M_PI - \atan($complex->getImaginary() / \abs($complex->getReal()));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Operations.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Operations.php
new file mode 100644
index 00000000000..b13a8734dc4
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Operations.php
@@ -0,0 +1,210 @@
+isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+
+ $real = $result->getReal() + $complex->getReal();
+ $imaginary = $result->getImaginary() + $complex->getImaginary();
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Divides two or more complex numbers
+ *
+ * @param array of string|integer|float|Complex $complexValues The numbers to divide
+ * @return Complex
+ */
+ public static function divideby(...$complexValues): Complex
+ {
+ if (count($complexValues) < 2) {
+ throw new \Exception('This function requires at least 2 arguments');
+ }
+
+ $base = array_shift($complexValues);
+ $result = clone Complex::validateComplexArgument($base);
+
+ foreach ($complexValues as $complex) {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($result->isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ $delta1 = ($result->getReal() * $complex->getReal()) +
+ ($result->getImaginary() * $complex->getImaginary());
+ $delta2 = ($result->getImaginary() * $complex->getReal()) -
+ ($result->getReal() * $complex->getImaginary());
+ $delta3 = ($complex->getReal() * $complex->getReal()) +
+ ($complex->getImaginary() * $complex->getImaginary());
+
+ $real = $delta1 / $delta3;
+ $imaginary = $delta2 / $delta3;
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Divides two or more complex numbers
+ *
+ * @param array of string|integer|float|Complex $complexValues The numbers to divide
+ * @return Complex
+ */
+ public static function divideinto(...$complexValues): Complex
+ {
+ if (count($complexValues) < 2) {
+ throw new \Exception('This function requires at least 2 arguments');
+ }
+
+ $base = array_shift($complexValues);
+ $result = clone Complex::validateComplexArgument($base);
+
+ foreach ($complexValues as $complex) {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($result->isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+ if ($result->getReal() == 0.0 && $result->getImaginary() == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ $delta1 = ($complex->getReal() * $result->getReal()) +
+ ($complex->getImaginary() * $result->getImaginary());
+ $delta2 = ($complex->getImaginary() * $result->getReal()) -
+ ($complex->getReal() * $result->getImaginary());
+ $delta3 = ($result->getReal() * $result->getReal()) +
+ ($result->getImaginary() * $result->getImaginary());
+
+ $real = $delta1 / $delta3;
+ $imaginary = $delta2 / $delta3;
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Multiplies two or more complex numbers
+ *
+ * @param array of string|integer|float|Complex $complexValues The numbers to multiply
+ * @return Complex
+ */
+ public static function multiply(...$complexValues): Complex
+ {
+ if (count($complexValues) < 2) {
+ throw new \Exception('This function requires at least 2 arguments');
+ }
+
+ $base = array_shift($complexValues);
+ $result = clone Complex::validateComplexArgument($base);
+
+ foreach ($complexValues as $complex) {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($result->isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+
+ $real = ($result->getReal() * $complex->getReal()) -
+ ($result->getImaginary() * $complex->getImaginary());
+ $imaginary = ($result->getReal() * $complex->getImaginary()) +
+ ($result->getImaginary() * $complex->getReal());
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Subtracts two or more complex numbers
+ *
+ * @param array of string|integer|float|Complex $complexValues The numbers to subtract
+ * @return Complex
+ */
+ public static function subtract(...$complexValues): Complex
+ {
+ if (count($complexValues) < 2) {
+ throw new \Exception('This function requires at least 2 arguments');
+ }
+
+ $base = array_shift($complexValues);
+ $result = clone Complex::validateComplexArgument($base);
+
+ foreach ($complexValues as $complex) {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($result->isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+
+ $real = $result->getReal() - $complex->getReal();
+ $imaginary = $result->getImaginary() - $complex->getImaginary();
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/abs.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/abs.php
deleted file mode 100644
index 6e2729a891e..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/abs.php
+++ /dev/null
@@ -1,29 +0,0 @@
-getReal() - $invsqrt->getImaginary(),
- $complex->getImaginary() + $invsqrt->getReal()
- );
- $log = ln($adjust);
-
- return new Complex(
- $log->getImaginary(),
- -1 * $log->getReal()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acosh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acosh.php
deleted file mode 100644
index 18a992e4308..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acosh.php
+++ /dev/null
@@ -1,34 +0,0 @@
-isReal() && ($complex->getReal() > 1)) {
- return new Complex(\acosh($complex->getReal()));
- }
-
- $acosh = acos($complex)
- ->reverse();
- if ($acosh->getReal() < 0.0) {
- $acosh = $acosh->invertReal();
- }
-
- return $acosh;
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acot.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acot.php
deleted file mode 100644
index 4ddc2ddb37c..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acot.php
+++ /dev/null
@@ -1,25 +0,0 @@
-getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return new Complex(INF);
- }
-
- return asin(inverse($complex));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsch.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsch.php
deleted file mode 100644
index 66d9bdf2c09..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsch.php
+++ /dev/null
@@ -1,29 +0,0 @@
-getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return new Complex(INF);
- }
-
- return asinh(inverse($complex));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/argument.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/argument.php
deleted file mode 100644
index 17217bb3073..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/argument.php
+++ /dev/null
@@ -1,28 +0,0 @@
-getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return new Complex(INF);
- }
-
- return acos(inverse($complex));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asech.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asech.php
deleted file mode 100644
index 929b0d19f1a..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asech.php
+++ /dev/null
@@ -1,29 +0,0 @@
-getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return new Complex(INF);
- }
-
- return acosh(inverse($complex));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asin.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asin.php
deleted file mode 100644
index b675046a80e..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asin.php
+++ /dev/null
@@ -1,37 +0,0 @@
-getReal() - $complex->getImaginary(),
- $invsqrt->getImaginary() + $complex->getReal()
- );
- $log = ln($adjust);
-
- return new Complex(
- $log->getImaginary(),
- -1 * $log->getReal()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asinh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asinh.php
deleted file mode 100644
index 3e5c2944fee..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asinh.php
+++ /dev/null
@@ -1,33 +0,0 @@
-isReal() && ($complex->getReal() > 1)) {
- return new Complex(\asinh($complex->getReal()));
- }
-
- $asinh = clone $complex;
- $asinh = $asinh->reverse()
- ->invertReal();
- $asinh = asin($asinh);
- return $asinh->reverse()
- ->invertImaginary();
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atan.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atan.php
deleted file mode 100644
index ecbea801c58..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atan.php
+++ /dev/null
@@ -1,45 +0,0 @@
-isReal()) {
- return new Complex(\atan($complex->getReal()));
- }
-
- $t1Value = new Complex(-1 * $complex->getImaginary(), $complex->getReal());
- $uValue = new Complex(1, 0);
-
- $d1Value = clone $uValue;
- $d1Value = subtract($d1Value, $t1Value);
- $d2Value = add($t1Value, $uValue);
- $uResult = $d1Value->divideBy($d2Value);
- $uResult = ln($uResult);
-
- return new Complex(
- (($uResult->getImaginary() == M_PI) ? -M_PI : $uResult->getImaginary()) * -0.5,
- $uResult->getReal() * 0.5,
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atanh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atanh.php
deleted file mode 100644
index 189493b8831..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atanh.php
+++ /dev/null
@@ -1,38 +0,0 @@
-isReal()) {
- $real = $complex->getReal();
- if ($real >= -1.0 && $real <= 1.0) {
- return new Complex(\atanh($real));
- } else {
- return new Complex(\atanh(1 / $real), (($real < 0.0) ? M_PI_2 : -1 * M_PI_2));
- }
- }
-
- $iComplex = clone $complex;
- $iComplex = $iComplex->invertImaginary()
- ->reverse();
- return atan($iComplex)
- ->invertReal()
- ->reverse();
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/conjugate.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/conjugate.php
deleted file mode 100644
index 52666176e11..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/conjugate.php
+++ /dev/null
@@ -1,28 +0,0 @@
-getReal(),
- -1 * $complex->getImaginary(),
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cos.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cos.php
deleted file mode 100644
index 0c6ea1a8e49..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cos.php
+++ /dev/null
@@ -1,34 +0,0 @@
-isReal()) {
- return new Complex(\cos($complex->getReal()));
- }
-
- return conjugate(
- new Complex(
- \cos($complex->getReal()) * \cosh($complex->getImaginary()),
- \sin($complex->getReal()) * \sinh($complex->getImaginary()),
- $complex->getSuffix()
- )
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cosh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cosh.php
deleted file mode 100644
index ee674c27053..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cosh.php
+++ /dev/null
@@ -1,32 +0,0 @@
-isReal()) {
- return new Complex(\cosh($complex->getReal()));
- }
-
- return new Complex(
- \cosh($complex->getReal()) * \cos($complex->getImaginary()),
- \sinh($complex->getReal()) * \sin($complex->getImaginary()),
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cot.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cot.php
deleted file mode 100644
index 693d0f7c5c2..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cot.php
+++ /dev/null
@@ -1,29 +0,0 @@
-getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return new Complex(INF);
- }
-
- return inverse(tan($complex));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/coth.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/coth.php
deleted file mode 100644
index 1ff1ad50627..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/coth.php
+++ /dev/null
@@ -1,24 +0,0 @@
-getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return new Complex(INF);
- }
-
- return inverse(sin($complex));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csch.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csch.php
deleted file mode 100644
index acaa6c0cd8b..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csch.php
+++ /dev/null
@@ -1,29 +0,0 @@
-getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return new Complex(INF);
- }
-
- return inverse(sinh($complex));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/exp.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/exp.php
deleted file mode 100644
index 8ab3b3e5968..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/exp.php
+++ /dev/null
@@ -1,34 +0,0 @@
-getReal() == 0.0) && (\abs($complex->getImaginary()) == M_PI)) {
- return new Complex(-1.0, 0.0);
- }
-
- $rho = \exp($complex->getReal());
-
- return new Complex(
- $rho * \cos($complex->getImaginary()),
- $rho * \sin($complex->getImaginary()),
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/inverse.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/inverse.php
deleted file mode 100644
index 563e3fd8dc6..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/inverse.php
+++ /dev/null
@@ -1,29 +0,0 @@
-getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- throw new \InvalidArgumentException('Division by zero');
- }
-
- return $complex->divideInto(1.0);
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/ln.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/ln.php
deleted file mode 100644
index d57bb7af586..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/ln.php
+++ /dev/null
@@ -1,33 +0,0 @@
-getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
- throw new \InvalidArgumentException();
- }
-
- return new Complex(
- \log(rho($complex)),
- theta($complex),
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log10.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log10.php
deleted file mode 100644
index ce4d001fc2e..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log10.php
+++ /dev/null
@@ -1,32 +0,0 @@
-getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
- throw new \InvalidArgumentException();
- } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) {
- return new Complex(\log10($complex->getReal()), 0.0, $complex->getSuffix());
- }
-
- return ln($complex)
- ->multiply(\log10(Complex::EULER));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log2.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log2.php
deleted file mode 100644
index 21b3e2aabb5..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log2.php
+++ /dev/null
@@ -1,32 +0,0 @@
-getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
- throw new \InvalidArgumentException();
- } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) {
- return new Complex(\log($complex->getReal(), 2), 0.0, $complex->getSuffix());
- }
-
- return ln($complex)
- ->multiply(\log(Complex::EULER, 2));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/negative.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/negative.php
deleted file mode 100644
index 232e178a076..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/negative.php
+++ /dev/null
@@ -1,31 +0,0 @@
-getReal(),
- -1 * $complex->getImaginary(),
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/pow.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/pow.php
deleted file mode 100644
index da3340fe1bb..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/pow.php
+++ /dev/null
@@ -1,40 +0,0 @@
-getImaginary() == 0.0 && $complex->getReal() >= 0.0) {
- return new Complex(\pow($complex->getReal(), $power));
- }
-
- $rValue = \sqrt(($complex->getReal() * $complex->getReal()) + ($complex->getImaginary() * $complex->getImaginary()));
- $rPower = \pow($rValue, $power);
- $theta = $complex->argument() * $power;
- if ($theta == 0) {
- return new Complex(1);
- }
-
- return new Complex($rPower * \cos($theta), $rPower * \sin($theta), $complex->getSuffix());
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/rho.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/rho.php
deleted file mode 100644
index d5264a769ff..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/rho.php
+++ /dev/null
@@ -1,28 +0,0 @@
-getReal() * $complex->getReal()) +
- ($complex->getImaginary() * $complex->getImaginary())
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sec.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sec.php
deleted file mode 100644
index 1c7768d4ff7..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sec.php
+++ /dev/null
@@ -1,25 +0,0 @@
-isReal()) {
- return new Complex(\sin($complex->getReal()));
- }
-
- return new Complex(
- \sin($complex->getReal()) * \cosh($complex->getImaginary()),
- \cos($complex->getReal()) * \sinh($complex->getImaginary()),
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sinh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sinh.php
deleted file mode 100644
index f051a8e2a59..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sinh.php
+++ /dev/null
@@ -1,32 +0,0 @@
-isReal()) {
- return new Complex(\sinh($complex->getReal()));
- }
-
- return new Complex(
- \sinh($complex->getReal()) * \cos($complex->getImaginary()),
- \cosh($complex->getReal()) * \sin($complex->getImaginary()),
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sqrt.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sqrt.php
deleted file mode 100644
index 17c19c70bf9..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sqrt.php
+++ /dev/null
@@ -1,29 +0,0 @@
-getSuffix());
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tan.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tan.php
deleted file mode 100644
index 6996a3a622a..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tan.php
+++ /dev/null
@@ -1,40 +0,0 @@
-isReal()) {
- return new Complex(\tan($complex->getReal()));
- }
-
- $real = $complex->getReal();
- $imaginary = $complex->getImaginary();
- $divisor = 1 + \pow(\tan($real), 2) * \pow(\tanh($imaginary), 2);
- if ($divisor == 0.0) {
- throw new \InvalidArgumentException('Division by zero');
- }
-
- return new Complex(
- \pow(sech($imaginary)->getReal(), 2) * \tan($real) / $divisor,
- \pow(sec($real)->getReal(), 2) * \tanh($imaginary) / $divisor,
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tanh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tanh.php
deleted file mode 100644
index a401042ad5c..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tanh.php
+++ /dev/null
@@ -1,35 +0,0 @@
-getReal();
- $imaginary = $complex->getImaginary();
- $divisor = \cos($imaginary) * \cos($imaginary) + \sinh($real) * \sinh($real);
- if ($divisor == 0.0) {
- throw new \InvalidArgumentException('Division by zero');
- }
-
- return new Complex(
- \sinh($real) * \cosh($real) / $divisor,
- 0.5 * \sin(2 * $imaginary) / $divisor,
- $complex->getSuffix()
- );
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/theta.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/theta.php
deleted file mode 100644
index f022abb4ec9..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/theta.php
+++ /dev/null
@@ -1,38 +0,0 @@
-getReal() == 0.0) {
- if ($complex->isReal()) {
- return 0.0;
- } elseif ($complex->getImaginary() < 0.0) {
- return M_PI / -2;
- }
- return M_PI / 2;
- } elseif ($complex->getReal() > 0.0) {
- return \atan($complex->getImaginary() / $complex->getReal());
- } elseif ($complex->getImaginary() < 0.0) {
- return -(M_PI - \atan(\abs($complex->getImaginary()) / \abs($complex->getReal())));
- }
-
- return M_PI - \atan($complex->getImaginary() / \abs($complex->getReal()));
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/add.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/add.php
deleted file mode 100644
index 6963bb0450a..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/add.php
+++ /dev/null
@@ -1,46 +0,0 @@
-isComplex() && $complex->isComplex() &&
- $result->getSuffix() !== $complex->getSuffix()) {
- throw new Exception('Suffix Mismatch');
- }
-
- $real = $result->getReal() + $complex->getReal();
- $imaginary = $result->getImaginary() + $complex->getImaginary();
-
- $result = new Complex(
- $real,
- $imaginary,
- ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
- );
- }
-
- return $result;
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideby.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideby.php
deleted file mode 100644
index a680931cb20..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideby.php
+++ /dev/null
@@ -1,56 +0,0 @@
-isComplex() && $complex->isComplex() &&
- $result->getSuffix() !== $complex->getSuffix()) {
- throw new Exception('Suffix Mismatch');
- }
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- throw new \InvalidArgumentException('Division by zero');
- }
-
- $delta1 = ($result->getReal() * $complex->getReal()) +
- ($result->getImaginary() * $complex->getImaginary());
- $delta2 = ($result->getImaginary() * $complex->getReal()) -
- ($result->getReal() * $complex->getImaginary());
- $delta3 = ($complex->getReal() * $complex->getReal()) +
- ($complex->getImaginary() * $complex->getImaginary());
-
- $real = $delta1 / $delta3;
- $imaginary = $delta2 / $delta3;
-
- $result = new Complex(
- $real,
- $imaginary,
- ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
- );
- }
-
- return $result;
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideinto.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideinto.php
deleted file mode 100644
index 70869933efd..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideinto.php
+++ /dev/null
@@ -1,56 +0,0 @@
-isComplex() && $complex->isComplex() &&
- $result->getSuffix() !== $complex->getSuffix()) {
- throw new Exception('Suffix Mismatch');
- }
- if ($result->getReal() == 0.0 && $result->getImaginary() == 0.0) {
- throw new \InvalidArgumentException('Division by zero');
- }
-
- $delta1 = ($complex->getReal() * $result->getReal()) +
- ($complex->getImaginary() * $result->getImaginary());
- $delta2 = ($complex->getImaginary() * $result->getReal()) -
- ($complex->getReal() * $result->getImaginary());
- $delta3 = ($result->getReal() * $result->getReal()) +
- ($result->getImaginary() * $result->getImaginary());
-
- $real = $delta1 / $delta3;
- $imaginary = $delta2 / $delta3;
-
- $result = new Complex(
- $real,
- $imaginary,
- ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
- );
- }
-
- return $result;
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/multiply.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/multiply.php
deleted file mode 100644
index 06a52b2781f..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/multiply.php
+++ /dev/null
@@ -1,48 +0,0 @@
-isComplex() && $complex->isComplex() &&
- $result->getSuffix() !== $complex->getSuffix()) {
- throw new Exception('Suffix Mismatch');
- }
-
- $real = ($result->getReal() * $complex->getReal()) -
- ($result->getImaginary() * $complex->getImaginary());
- $imaginary = ($result->getReal() * $complex->getImaginary()) +
- ($result->getImaginary() * $complex->getReal());
-
- $result = new Complex(
- $real,
- $imaginary,
- ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
- );
- }
-
- return $result;
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/subtract.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/subtract.php
deleted file mode 100644
index 0d9985cc534..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/subtract.php
+++ /dev/null
@@ -1,46 +0,0 @@
-isComplex() && $complex->isComplex() &&
- $result->getSuffix() !== $complex->getSuffix()) {
- throw new Exception('Suffix Mismatch');
- }
-
- $real = $result->getReal() - $complex->getReal();
- $imaginary = $result->getImaginary() - $complex->getImaginary();
-
- $result = new Complex(
- $real,
- $imaginary,
- ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
- );
- }
-
- return $result;
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/composer.json b/lib/phpspreadsheet/vendor/markbaker/complex/composer.json
index a343d6e8708..ea1d06f5638 100644
--- a/lib/phpspreadsheet/vendor/markbaker/complex/composer.json
+++ b/lib/phpspreadsheet/vendor/markbaker/complex/composer.json
@@ -16,10 +16,6 @@
},
"require-dev": {
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
- "phpdocumentor/phpdocumentor": "2.*",
- "phpmd/phpmd": "2.*",
- "sebastian/phpcpd": "^4.0",
- "phploc/phploc": "^4.0",
"squizlabs/php_codesniffer": "^3.4",
"phpcompatibility/php-compatibility": "^9.0",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0"
@@ -27,57 +23,10 @@
"autoload": {
"psr-4": {
"Complex\\": "classes/src/"
- },
- "files": [
- "classes/src/functions/abs.php",
- "classes/src/functions/acos.php",
- "classes/src/functions/acosh.php",
- "classes/src/functions/acot.php",
- "classes/src/functions/acoth.php",
- "classes/src/functions/acsc.php",
- "classes/src/functions/acsch.php",
- "classes/src/functions/argument.php",
- "classes/src/functions/asec.php",
- "classes/src/functions/asech.php",
- "classes/src/functions/asin.php",
- "classes/src/functions/asinh.php",
- "classes/src/functions/atan.php",
- "classes/src/functions/atanh.php",
- "classes/src/functions/conjugate.php",
- "classes/src/functions/cos.php",
- "classes/src/functions/cosh.php",
- "classes/src/functions/cot.php",
- "classes/src/functions/coth.php",
- "classes/src/functions/csc.php",
- "classes/src/functions/csch.php",
- "classes/src/functions/exp.php",
- "classes/src/functions/inverse.php",
- "classes/src/functions/ln.php",
- "classes/src/functions/log2.php",
- "classes/src/functions/log10.php",
- "classes/src/functions/negative.php",
- "classes/src/functions/pow.php",
- "classes/src/functions/rho.php",
- "classes/src/functions/sec.php",
- "classes/src/functions/sech.php",
- "classes/src/functions/sin.php",
- "classes/src/functions/sinh.php",
- "classes/src/functions/sqrt.php",
- "classes/src/functions/tan.php",
- "classes/src/functions/tanh.php",
- "classes/src/functions/theta.php",
- "classes/src/operations/add.php",
- "classes/src/operations/subtract.php",
- "classes/src/operations/multiply.php",
- "classes/src/operations/divideby.php",
- "classes/src/operations/divideinto.php"
- ]
+ }
},
"scripts": {
"style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n",
- "mess": "phpmd classes/src/ xml codesize,unusedcode,design,naming -n",
- "lines": "phploc classes/src/ -n",
- "cpd": "phpcpd classes/src/ -n",
"versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full classes/src/ --runtime-set testVersion 7.2- -n"
},
"minimum-stability": "dev"
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/examples/complexTest.php b/lib/phpspreadsheet/vendor/markbaker/complex/examples/complexTest.php
index 7dafd8a6392..9a5e1238d11 100644
--- a/lib/phpspreadsheet/vendor/markbaker/complex/examples/complexTest.php
+++ b/lib/phpspreadsheet/vendor/markbaker/complex/examples/complexTest.php
@@ -2,7 +2,7 @@
use Complex\Complex as Complex;
-include('../classes/Bootstrap.php');
+include(__DIR__ . '/../vendor/autoload.php');
echo 'Create', PHP_EOL;
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/examples/testFunctions.php b/lib/phpspreadsheet/vendor/markbaker/complex/examples/testFunctions.php
index 4d5ed7358b4..bad1c03ded5 100644
--- a/lib/phpspreadsheet/vendor/markbaker/complex/examples/testFunctions.php
+++ b/lib/phpspreadsheet/vendor/markbaker/complex/examples/testFunctions.php
@@ -2,7 +2,7 @@
namespace Complex;
-include('../classes/Bootstrap.php');
+include(__DIR__ . '/../vendor/autoload.php');
echo 'Function Examples', PHP_EOL;
@@ -39,7 +39,7 @@ $functions = array(
for ($real = -3.5; $real <= 3.5; $real += 0.5) {
for ($imaginary = -3.5; $imaginary <= 3.5; $imaginary += 0.5) {
foreach ($functions as $function) {
- $complexFunction = __NAMESPACE__ . '\\' . $function;
+ $complexFunction = __NAMESPACE__ . '\\Functions::' . $function;
$complex = new Complex($real, $imaginary);
try {
echo $function, '(', $complex, ') = ', $complexFunction($complex), PHP_EOL;
diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/examples/testOperations.php b/lib/phpspreadsheet/vendor/markbaker/complex/examples/testOperations.php
index f791263efd3..2b7e0ba4b66 100644
--- a/lib/phpspreadsheet/vendor/markbaker/complex/examples/testOperations.php
+++ b/lib/phpspreadsheet/vendor/markbaker/complex/examples/testOperations.php
@@ -1,8 +1,9 @@
', $result, PHP_EOL;
echo PHP_EOL;
echo 'Subtraction', PHP_EOL;
-$result = \Complex\subtract(...$values);
+$result = Operations::subtract(...$values);
echo '=> ', $result, PHP_EOL;
echo PHP_EOL;
echo 'Multiplication', PHP_EOL;
-$result = \Complex\multiply(...$values);
+$result = Operations::multiply(...$values);
echo '=> ', $result, PHP_EOL;
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/README.md b/lib/phpspreadsheet/vendor/markbaker/matrix/README.md
index 66a1de4d387..d0dc91452c7 100644
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/README.md
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/README.md
@@ -5,9 +5,11 @@ PHPMatrix
PHP Class for handling Matrices
-Master: [](http://travis-ci.org/MarkBaker/PHPMatrix)
+[](https://github.com/MarkBaker/PHPMatrix/actions)
+[](https://packagist.org/packages/markbaker/matrix)
+[](https://packagist.org/packages/markbaker/matrix)
+[](https://packagist.org/packages/markbaker/matrix)
-Develop: [](http://travis-ci.org/MarkBaker/PHPMatrix)
[](https://xkcd.com/184/)
@@ -37,22 +39,54 @@ together with functions for
- minors
- trace
- transpose
+ - solve
+ Given Matrices A and B, calculate X for A.X = B
+
+and classes for
+
+ - Decomposition
+ - LU Decomposition with partial row pivoting,
+
+ such that [P].[A] = [L].[U] and [A] = [P]|.[L].[U]
+ - QR Decomposition
+
+ such that [A] = [Q].[R]
## TO DO
- - power()
- - EigenValues
- - EigenVectors
+ - power() function
- Decomposition
+ - Cholesky Decomposition
+ - EigenValue Decomposition
+ - EigenValues
+ - EigenVectors
---
+# Installation
+
+```shell
+composer require markbaker/matrix:^3.0
+```
+
+# Important BC Note
+
+If you've previously been using procedural calls to functions and operations using this library, then from version 3.0 you should use [MarkBaker/PHPMatrixFunctions](https://github.com/MarkBaker/PHPMatrixFunctions) instead (available on packagist as [markbaker/matrix-functions](https://packagist.org/packages/markbaker/matrix-functions)).
+
+You'll need to replace `markbaker/matrix`in your `composer.json` file with the new library, but otherwise there should be no difference in the namespacing, or in the way that you have called the Matrix functions in the past, so no actual code changes are required.
+
+```shell
+composer require markbaker/matrix-functions:^1.0
+```
+
+You should not reference this library (`markbaker/matrix`) in your `composer.json`, composer wil take care of that for you.
+
# Usage
To create a new Matrix object, provide an array as the constructor argument
-```
+```php
$grid = [
[16, 3, 2, 13],
[ 5, 10, 11, 8],
@@ -63,12 +97,12 @@ $grid = [
$matrix = new Matrix\Matrix($grid);
```
The `Builder` class provides helper methods for creating specific matrices, specifically an identity matrix of a specified size; or a matrix of a specified dimensions, with every cell containing a set value.
-```
-$matrix = new Matrix\Builder::createFilledMatrix(1, 5, 3);
+```php
+$matrix = Matrix\Builder::createFilledMatrix(1, 5, 3);
```
Will create a matrix of 5 rows and 3 columns, filled with a `1` in every cell; while
-```
-$matrix = new Matrix\Builder::createIdentityMatrix(3);
+```php
+$matrix = Matrix\Builder::createIdentityMatrix(3);
```
will create a 3x3 identity matrix.
@@ -79,34 +113,34 @@ Matrix objects are immutable: whenever you call a method or pass a grid to a fun
To perform mathematical operations with Matrices, you can call the appropriate method against a matrix value, passing other values as arguments
-```
-$matrix1 = new Matrix([
+```php
+$matrix1 = new Matrix\Matrix([
[2, 7, 6],
[9, 5, 1],
[4, 3, 8],
]);
-$matrix2 = new Matrix([
+$matrix2 = new Matrix\Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]);
-echo $matrix1->multiply($matrix2);
+var_dump($matrix1->multiply($matrix2)->toArray());
```
-or pass all values to the appropriate function
-```
-$matrix1 = new Matrix([
+or pass all values to the appropriate static method
+```php
+$matrix1 = new Matrix\Matrix([
[2, 7, 6],
[9, 5, 1],
[4, 3, 8],
]);
-$matrix2 = new Matrix([
+$matrix2 = new Matrix\Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]);
-echo Matrix\multiply($matrix1, $matrix2);
+var_dump(Matrix\Operations::multiply($matrix1, $matrix2)->toArray());
```
You can pass in the arguments as Matrix objects, or as arrays.
@@ -115,7 +149,7 @@ If you want to perform the same operation against multiple values (e.g. to add t
## Using functions
When calling any of the available functions for a matrix value, you can either call the relevant method for the Matrix object
-```
+```php
$grid = [
[16, 3, 2, 13],
[ 5, 10, 11, 8],
@@ -127,31 +161,8 @@ $matrix = new Matrix\Matrix($grid);
echo $matrix->trace();
```
-or you can call the function as you would in procedural code, passing the Matrix object as an argument
-```
-$grid = [
- [16, 3, 2, 13],
- [ 5, 10, 11, 8],
- [ 9, 6, 7, 12],
- [ 4, 15, 14, 1],
-];
-
-$matrix = new Matrix\Matrix($grid);
-echo Matrix\trace($matrix);
-```
-When called procedurally using the function, you can pass in the argument as a Matrix object, or as an array.
-```
-$grid = [
- [16, 3, 2, 13],
- [ 5, 10, 11, 8],
- [ 9, 6, 7, 12],
- [ 4, 15, 14, 1],
-];
-
-echo Matrix\trace($grid);
-```
-As an alternative, it is also possible to call the method directly from the `Functions` class.
-```
+or you can call the static method, passing the Matrix object or array as an argument
+```php
$grid = [
[16, 3, 2, 13],
[ 5, 10, 11, 8],
@@ -162,4 +173,43 @@ $grid = [
$matrix = new Matrix\Matrix($grid);
echo Matrix\Functions::trace($matrix);
```
-Used this way, methods must be called statically, and the argument must be the Matrix object, and cannot be an array.
+```php
+$grid = [
+ [16, 3, 2, 13],
+ [ 5, 10, 11, 8],
+ [ 9, 6, 7, 12],
+ [ 4, 15, 14, 1],
+];
+
+echo Matrix\Functions::trace($grid);
+```
+
+## Decomposition
+
+The library also provides classes for matrix decomposition. You can access these using
+```php
+$grid = [
+ [1, 2],
+ [3, 4],
+];
+
+$matrix = new Matrix\Matrix($grid);
+
+$decomposition = new Matrix\Decomposition\QR($matrix);
+$Q = $decomposition->getQ();
+$R = $decomposition->getR();
+```
+
+or alternatively us the `Decomposition` factory, identifying which form of decomposition you want to use
+```php
+$grid = [
+ [1, 2],
+ [3, 4],
+];
+
+$matrix = new Matrix\Matrix($grid);
+
+$decomposition = Matrix\Decomposition\Decomposition::decomposition(Matrix\Decomposition\Decomposition::QR, $matrix);
+$Q = $decomposition->getQ();
+$R = $decomposition->getR();
+```
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/Autoloader.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/Autoloader.php
deleted file mode 100644
index 279d176eff7..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/Autoloader.php
+++ /dev/null
@@ -1,53 +0,0 @@
-regex = $regex;
- parent::__construct($it, $regex);
- }
-}
-
-class FilenameFilter extends FilesystemRegexFilter
-{
- // Filter files against the regex
- public function accept()
- {
- return (!$this->isFile() || preg_match($this->regex, $this->getFilename()));
- }
-}
-
-
-$srcFolder = __DIR__ . DIRECTORY_SEPARATOR . 'src';
-$srcDirectory = new RecursiveDirectoryIterator($srcFolder);
-
-$filteredFileList = new FilenameFilter($srcDirectory, '/(?:php)$/i');
-$filteredFileList = new FilenameFilter($filteredFileList, '/^(?!.*(Matrix|Exception)\.php).*$/i');
-
-foreach (new RecursiveIteratorIterator($filteredFileList) as $file) {
- if ($file->isFile()) {
- include_once $file;
- }
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Builder.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Builder.php
index 6bc334adcf9..161bb6889d9 100644
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Builder.php
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Builder.php
@@ -21,13 +21,13 @@ class Builder
* Create a new matrix of specified dimensions, and filled with a specified value
* If the column argument isn't provided, then a square matrix will be created
*
- * @param mixed $value
+ * @param mixed $fillValue
* @param int $rows
* @param int|null $columns
* @return Matrix
* @throws Exception
*/
- public static function createFilledMatrix($value, $rows, $columns = null)
+ public static function createFilledMatrix($fillValue, $rows, $columns = null)
{
if ($columns === null) {
$columns = $rows;
@@ -43,7 +43,7 @@ class Builder
array_fill(
0,
$columns,
- $value
+ $fillValue
)
)
);
@@ -57,9 +57,9 @@ class Builder
* @return Matrix
* @throws Exception
*/
- public static function createIdentityMatrix($dimensions)
+ public static function createIdentityMatrix($dimensions, $fillValue = null)
{
- $grid = static::createFilledMatrix(null, $dimensions)->toArray();
+ $grid = static::createFilledMatrix($fillValue, $dimensions)->toArray();
for ($x = 0; $x < $dimensions; ++$x) {
$grid[$x][$x] = 1;
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php
new file mode 100644
index 00000000000..f0144487586
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php
@@ -0,0 +1,27 @@
+luMatrix = $matrix->toArray();
+ $this->rows = $matrix->rows;
+ $this->columns = $matrix->columns;
+
+ $this->buildPivot();
+ }
+
+ /**
+ * Get lower triangular factor.
+ *
+ * @return Matrix Lower triangular factor
+ */
+ public function getL(): Matrix
+ {
+ $lower = [];
+
+ $columns = min($this->rows, $this->columns);
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $columns; ++$column) {
+ if ($row > $column) {
+ $lower[$row][$column] = $this->luMatrix[$row][$column];
+ } elseif ($row === $column) {
+ $lower[$row][$column] = 1.0;
+ } else {
+ $lower[$row][$column] = 0.0;
+ }
+ }
+ }
+
+ return new Matrix($lower);
+ }
+
+ /**
+ * Get upper triangular factor.
+ *
+ * @return Matrix Upper triangular factor
+ */
+ public function getU(): Matrix
+ {
+ $upper = [];
+
+ $rows = min($this->rows, $this->columns);
+ for ($row = 0; $row < $rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ if ($row <= $column) {
+ $upper[$row][$column] = $this->luMatrix[$row][$column];
+ } else {
+ $upper[$row][$column] = 0.0;
+ }
+ }
+ }
+
+ return new Matrix($upper);
+ }
+
+ /**
+ * Return pivot permutation vector.
+ *
+ * @return Matrix Pivot matrix
+ */
+ public function getP(): Matrix
+ {
+ $pMatrix = [];
+
+ $pivots = $this->pivot;
+ $pivotCount = count($pivots);
+ foreach ($pivots as $row => $pivot) {
+ $pMatrix[$row] = array_fill(0, $pivotCount, 0);
+ $pMatrix[$row][$pivot] = 1;
+ }
+
+ return new Matrix($pMatrix);
+ }
+
+ /**
+ * Return pivot permutation vector.
+ *
+ * @return array Pivot vector
+ */
+ public function getPivot(): array
+ {
+ return $this->pivot;
+ }
+
+ /**
+ * Is the matrix nonsingular?
+ *
+ * @return bool true if U, and hence A, is nonsingular
+ */
+ public function isNonsingular(): bool
+ {
+ for ($diagonal = 0; $diagonal < $this->columns; ++$diagonal) {
+ if ($this->luMatrix[$diagonal][$diagonal] === 0.0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private function buildPivot(): void
+ {
+ for ($row = 0; $row < $this->rows; ++$row) {
+ $this->pivot[$row] = $row;
+ }
+
+ for ($column = 0; $column < $this->columns; ++$column) {
+ $luColumn = $this->localisedReferenceColumn($column);
+
+ $this->applyTransformations($column, $luColumn);
+
+ $pivot = $this->findPivot($column, $luColumn);
+ if ($pivot !== $column) {
+ $this->pivotExchange($pivot, $column);
+ }
+
+ $this->computeMultipliers($column);
+
+ unset($luColumn);
+ }
+ }
+
+ private function localisedReferenceColumn($column): array
+ {
+ $luColumn = [];
+
+ for ($row = 0; $row < $this->rows; ++$row) {
+ $luColumn[$row] = &$this->luMatrix[$row][$column];
+ }
+
+ return $luColumn;
+ }
+
+ private function applyTransformations($column, array $luColumn): void
+ {
+ for ($row = 0; $row < $this->rows; ++$row) {
+ $luRow = $this->luMatrix[$row];
+ // Most of the time is spent in the following dot product.
+ $kmax = min($row, $column);
+ $sValue = 0.0;
+ for ($kValue = 0; $kValue < $kmax; ++$kValue) {
+ $sValue += $luRow[$kValue] * $luColumn[$kValue];
+ }
+ $luRow[$column] = $luColumn[$row] -= $sValue;
+ }
+ }
+
+ private function findPivot($column, array $luColumn): int
+ {
+ $pivot = $column;
+ for ($row = $column + 1; $row < $this->rows; ++$row) {
+ if (abs($luColumn[$row]) > abs($luColumn[$pivot])) {
+ $pivot = $row;
+ }
+ }
+
+ return $pivot;
+ }
+
+ private function pivotExchange($pivot, $column): void
+ {
+ for ($kValue = 0; $kValue < $this->columns; ++$kValue) {
+ $tValue = $this->luMatrix[$pivot][$kValue];
+ $this->luMatrix[$pivot][$kValue] = $this->luMatrix[$column][$kValue];
+ $this->luMatrix[$column][$kValue] = $tValue;
+ }
+
+ $lValue = $this->pivot[$pivot];
+ $this->pivot[$pivot] = $this->pivot[$column];
+ $this->pivot[$column] = $lValue;
+ }
+
+ private function computeMultipliers($diagonal): void
+ {
+ if (($diagonal < $this->rows) && ($this->luMatrix[$diagonal][$diagonal] != 0.0)) {
+ for ($row = $diagonal + 1; $row < $this->rows; ++$row) {
+ $this->luMatrix[$row][$diagonal] /= $this->luMatrix[$diagonal][$diagonal];
+ }
+ }
+ }
+
+ private function pivotB(Matrix $B): array
+ {
+ $X = [];
+ foreach ($this->pivot as $rowId) {
+ $row = $B->getRows($rowId + 1)->toArray();
+ $X[] = array_pop($row);
+ }
+
+ return $X;
+ }
+
+ /**
+ * Solve A*X = B.
+ *
+ * @param Matrix $B a Matrix with as many rows as A and any number of columns
+ *
+ * @throws Exception
+ *
+ * @return Matrix X so that L*U*X = B(piv,:)
+ */
+ public function solve(Matrix $B): Matrix
+ {
+ if ($B->rows !== $this->rows) {
+ throw new Exception('Matrix row dimensions are not equal');
+ }
+
+ if ($this->rows !== $this->columns) {
+ throw new Exception('LU solve() only works on square matrices');
+ }
+
+ if (!$this->isNonsingular()) {
+ throw new Exception('Can only perform operation on singular matrix');
+ }
+
+ // Copy right hand side with pivoting
+ $nx = $B->columns;
+ $X = $this->pivotB($B);
+
+ // Solve L*Y = B(piv,:)
+ for ($k = 0; $k < $this->columns; ++$k) {
+ for ($i = $k + 1; $i < $this->columns; ++$i) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X[$i][$j] -= $X[$k][$j] * $this->luMatrix[$i][$k];
+ }
+ }
+ }
+
+ // Solve U*X = Y;
+ for ($k = $this->columns - 1; $k >= 0; --$k) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X[$k][$j] /= $this->luMatrix[$k][$k];
+ }
+ for ($i = 0; $i < $k; ++$i) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X[$i][$j] -= $X[$k][$j] * $this->luMatrix[$i][$k];
+ }
+ }
+ }
+
+ return new Matrix($X);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/QR.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/QR.php
new file mode 100644
index 00000000000..4b6106f6413
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Decomposition/QR.php
@@ -0,0 +1,191 @@
+qrMatrix = $matrix->toArray();
+ $this->rows = $matrix->rows;
+ $this->columns = $matrix->columns;
+
+ $this->decompose();
+ }
+
+ public function getHouseholdVectors(): Matrix
+ {
+ $householdVectors = [];
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ if ($row >= $column) {
+ $householdVectors[$row][$column] = $this->qrMatrix[$row][$column];
+ } else {
+ $householdVectors[$row][$column] = 0.0;
+ }
+ }
+ }
+
+ return new Matrix($householdVectors);
+ }
+
+ public function getQ(): Matrix
+ {
+ $qGrid = [];
+
+ $rowCount = $this->rows;
+ for ($k = $this->columns - 1; $k >= 0; --$k) {
+ for ($i = 0; $i < $this->rows; ++$i) {
+ $qGrid[$i][$k] = 0.0;
+ }
+ $qGrid[$k][$k] = 1.0;
+ if ($this->columns > $this->rows) {
+ $qGrid = array_slice($qGrid, 0, $this->rows);
+ }
+
+ for ($j = $k; $j < $this->columns; ++$j) {
+ if (isset($this->qrMatrix[$k], $this->qrMatrix[$k][$k]) && $this->qrMatrix[$k][$k] != 0.0) {
+ $s = 0.0;
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $s += $this->qrMatrix[$i][$k] * $qGrid[$i][$j];
+ }
+ $s = -$s / $this->qrMatrix[$k][$k];
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $qGrid[$i][$j] += $s * $this->qrMatrix[$i][$k];
+ }
+ }
+ }
+ }
+
+ array_walk(
+ $qGrid,
+ function (&$row) use ($rowCount) {
+ $row = array_reverse($row);
+ $row = array_slice($row, 0, $rowCount);
+ }
+ );
+
+ return new Matrix($qGrid);
+ }
+
+ public function getR(): Matrix
+ {
+ $rGrid = [];
+
+ for ($row = 0; $row < $this->columns; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ if ($row < $column) {
+ $rGrid[$row][$column] = $this->qrMatrix[$row][$column] ?? 0.0;
+ } elseif ($row === $column) {
+ $rGrid[$row][$column] = $this->rDiagonal[$row] ?? 0.0;
+ } else {
+ $rGrid[$row][$column] = 0.0;
+ }
+ }
+ }
+
+ if ($this->columns > $this->rows) {
+ $rGrid = array_slice($rGrid, 0, $this->rows);
+ }
+
+ return new Matrix($rGrid);
+ }
+
+ private function hypo($a, $b): float
+ {
+ if (abs($a) > abs($b)) {
+ $r = $b / $a;
+ $r = abs($a) * sqrt(1 + $r * $r);
+ } elseif ($b != 0.0) {
+ $r = $a / $b;
+ $r = abs($b) * sqrt(1 + $r * $r);
+ } else {
+ $r = 0.0;
+ }
+
+ return $r;
+ }
+
+ /**
+ * QR Decomposition computed by Householder reflections.
+ */
+ private function decompose(): void
+ {
+ for ($k = 0; $k < $this->columns; ++$k) {
+ // Compute 2-norm of k-th column without under/overflow.
+ $norm = 0.0;
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $norm = $this->hypo($norm, $this->qrMatrix[$i][$k]);
+ }
+ if ($norm != 0.0) {
+ // Form k-th Householder vector.
+ if ($this->qrMatrix[$k][$k] < 0.0) {
+ $norm = -$norm;
+ }
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $this->qrMatrix[$i][$k] /= $norm;
+ }
+ $this->qrMatrix[$k][$k] += 1.0;
+ // Apply transformation to remaining columns.
+ for ($j = $k + 1; $j < $this->columns; ++$j) {
+ $s = 0.0;
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $s += $this->qrMatrix[$i][$k] * $this->qrMatrix[$i][$j];
+ }
+ $s = -$s / $this->qrMatrix[$k][$k];
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $this->qrMatrix[$i][$j] += $s * $this->qrMatrix[$i][$k];
+ }
+ }
+ }
+ $this->rDiagonal[$k] = -$norm;
+ }
+ }
+
+ public function isFullRank(): bool
+ {
+ for ($j = 0; $j < $this->columns; ++$j) {
+ if ($this->rDiagonal[$j] == 0.0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Least squares solution of A*X = B.
+ *
+ * @param Matrix $B a Matrix with as many rows as A and any number of columns
+ *
+ * @throws Exception
+ *
+ * @return Matrix matrix that minimizes the two norm of Q*R*X-B
+ */
+ public function solve(Matrix $B): Matrix
+ {
+ if ($B->rows !== $this->rows) {
+ throw new Exception('Matrix row dimensions are not equal');
+ }
+
+ if (!$this->isFullRank()) {
+ throw new Exception('Can only perform this operation on a full-rank matrix');
+ }
+
+ // Compute Y = transpose(Q)*B
+ $Y = $this->getQ()->transpose()
+ ->multiply($B);
+ // Solve R*X = Y;
+ return $this->getR()->inverse()
+ ->multiply($Y);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Div0Exception.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Div0Exception.php
new file mode 100644
index 00000000000..eba28f8a457
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Div0Exception.php
@@ -0,0 +1,13 @@
+isSquare()) {
throw new Exception('Adjoint can only be calculated for a square matrix');
}
@@ -67,13 +88,15 @@ class Functions
/**
* Return the cofactors of this matrix
*
- * @param Matrix $matrix The matrix whose cofactors we wish to calculate
+ * @param Matrix|array $matrix The matrix whose cofactors we wish to calculate
* @return Matrix
*
* @throws Exception
*/
- public static function cofactors(Matrix $matrix)
+ public static function cofactors($matrix)
{
+ $matrix = self::validateMatrix($matrix);
+
if (!$matrix->isSquare()) {
throw new Exception('Cofactors can only be calculated for a square matrix');
}
@@ -141,12 +164,14 @@ class Functions
/**
* Return the determinant of this matrix
*
- * @param Matrix $matrix The matrix whose determinant we wish to calculate
+ * @param Matrix|array $matrix The matrix whose determinant we wish to calculate
* @return float
* @throws Exception
**/
- public static function determinant(Matrix $matrix)
+ public static function determinant($matrix)
{
+ $matrix = self::validateMatrix($matrix);
+
if (!$matrix->isSquare()) {
throw new Exception('Determinant can only be calculated for a square matrix');
}
@@ -157,12 +182,14 @@ class Functions
/**
* Return the diagonal of this matrix
*
- * @param Matrix $matrix The matrix whose diagonal we wish to calculate
+ * @param Matrix|array $matrix The matrix whose diagonal we wish to calculate
* @return Matrix
* @throws Exception
**/
- public static function diagonal(Matrix $matrix)
+ public static function diagonal($matrix)
{
+ $matrix = self::validateMatrix($matrix);
+
if (!$matrix->isSquare()) {
throw new Exception('Diagonal can only be extracted from a square matrix');
}
@@ -181,12 +208,14 @@ class Functions
/**
* Return the antidiagonal of this matrix
*
- * @param Matrix $matrix The matrix whose antidiagonal we wish to calculate
+ * @param Matrix|array $matrix The matrix whose antidiagonal we wish to calculate
* @return Matrix
* @throws Exception
**/
- public static function antidiagonal(Matrix $matrix)
+ public static function antidiagonal($matrix)
{
+ $matrix = self::validateMatrix($matrix);
+
if (!$matrix->isSquare()) {
throw new Exception('Anti-Diagonal can only be extracted from a square matrix');
}
@@ -207,12 +236,14 @@ class Functions
* The identity matrix, or sometimes ambiguously called a unit matrix, of size n is the n × n square matrix
* with ones on the main diagonal and zeros elsewhere
*
- * @param Matrix $matrix The matrix whose identity we wish to calculate
+ * @param Matrix|array $matrix The matrix whose identity we wish to calculate
* @return Matrix
* @throws Exception
**/
- public static function identity(Matrix $matrix)
+ public static function identity($matrix)
{
+ $matrix = self::validateMatrix($matrix);
+
if (!$matrix->isSquare()) {
throw new Exception('Identity can only be created for a square matrix');
}
@@ -225,19 +256,21 @@ class Functions
/**
* Return the inverse of this matrix
*
- * @param Matrix $matrix The matrix whose inverse we wish to calculate
+ * @param Matrix|array $matrix The matrix whose inverse we wish to calculate
* @return Matrix
* @throws Exception
**/
- public static function inverse(Matrix $matrix)
+ public static function inverse($matrix, string $type = 'inverse')
{
+ $matrix = self::validateMatrix($matrix);
+
if (!$matrix->isSquare()) {
- throw new Exception('Inverse can only be calculated for a square matrix');
+ throw new Exception(ucfirst($type) . ' can only be calculated for a square matrix');
}
$determinant = self::getDeterminant($matrix);
if ($determinant == 0.0) {
- throw new Exception('Inverse can only be calculated for a matrix with a non-zero determinant');
+ throw new Div0Exception(ucfirst($type) . ' can only be calculated for a matrix with a non-zero determinant');
}
if ($matrix->rows == 1) {
@@ -281,12 +314,14 @@ class Functions
* calculating matrix cofactors, which in turn are useful for computing both the determinant and inverse of
* square matrices.
*
- * @param Matrix $matrix The matrix whose minors we wish to calculate
+ * @param Matrix|array $matrix The matrix whose minors we wish to calculate
* @return Matrix
* @throws Exception
**/
- public static function minors(Matrix $matrix)
+ public static function minors($matrix)
{
+ $matrix = self::validateMatrix($matrix);
+
if (!$matrix->isSquare()) {
throw new Exception('Minors can only be calculated for a square matrix');
}
@@ -299,12 +334,14 @@ class Functions
* The trace is defined as the sum of the elements on the main diagonal (the diagonal from the upper left to the lower right)
* of the matrix
*
- * @param Matrix $matrix The matrix whose trace we wish to calculate
+ * @param Matrix|array $matrix The matrix whose trace we wish to calculate
* @return float
* @throws Exception
**/
- public static function trace(Matrix $matrix)
+ public static function trace($matrix)
{
+ $matrix = self::validateMatrix($matrix);
+
if (!$matrix->isSquare()) {
throw new Exception('Trace can only be extracted from a square matrix');
}
@@ -321,11 +358,13 @@ class Functions
/**
* Return the transpose of this matrix
*
- * @param Matrix $matrix The matrix whose transpose we wish to calculate
+ * @param Matrix|\a $matrix The matrix whose transpose we wish to calculate
* @return Matrix
**/
- public static function transpose(Matrix $matrix)
+ public static function transpose($matrix)
{
+ $matrix = self::validateMatrix($matrix);
+
$array = array_values(array_merge([null], $matrix->toArray()));
$grid = call_user_func_array(
'array_map',
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Matrix.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Matrix.php
index e4e3140d12e..95f55e75a35 100644
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Matrix.php
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Matrix.php
@@ -10,6 +10,10 @@
namespace Matrix;
+use Generator;
+use Matrix\Decomposition\LU;
+use Matrix\Decomposition\QR;
+
/**
* Matrix object.
*
@@ -24,7 +28,6 @@ namespace Matrix;
* @method Matrix diagonal()
* @method Matrix identity()
* @method Matrix inverse()
- * @method Matrix pseudoInverse()
* @method Matrix minors()
* @method float trace()
* @method Matrix transpose()
@@ -33,6 +36,7 @@ namespace Matrix;
* @method Matrix multiply(...$matrices)
* @method Matrix divideby(...$matrices)
* @method Matrix divideinto(...$matrices)
+ * @method Matrix directsum(...$matrices)
*/
class Matrix
{
@@ -270,11 +274,11 @@ class Matrix
/**
* Returns a Generator that will yield each row of the matrix in turn as a vector matrix
- * or the value of each cell if the matrix is a vector
+ * or the value of each cell if the matrix is a column vector
*
- * @return \Generator|Matrix[]|mixed[]
+ * @return Generator|Matrix[]|mixed[]
*/
- public function rows(): \Generator
+ public function rows(): Generator
{
foreach ($this->grid as $i => $row) {
yield $i + 1 => ($this->columns == 1)
@@ -285,11 +289,11 @@ class Matrix
/**
* Returns a Generator that will yield each column of the matrix in turn as a vector matrix
- * or the value of each cell if the matrix is a vector
+ * or the value of each cell if the matrix is a row vector
*
- * @return \Generator|Matrix[]|mixed[]
+ * @return Generator|Matrix[]|mixed[]
*/
- public function columns(): \Generator
+ public function columns(): Generator
{
for ($i = 0; $i < $this->columns; ++$i) {
yield $i + 1 => ($this->rows == 1)
@@ -306,7 +310,7 @@ class Matrix
*/
public function isSquare(): bool
{
- return $this->rows == $this->columns;
+ return $this->rows === $this->columns;
}
/**
@@ -317,7 +321,7 @@ class Matrix
*/
public function isVector(): bool
{
- return $this->rows == 1 || $this->columns == 1;
+ return $this->rows === 1 || $this->columns === 1;
}
/**
@@ -330,6 +334,24 @@ class Matrix
return $this->grid;
}
+ /**
+ * Solve A*X = B.
+ *
+ * @param Matrix $B Right hand side
+ *
+ * @throws Exception
+ *
+ * @return Matrix ... Solution if A is square, least squares solution otherwise
+ */
+ public function solve(Matrix $B): Matrix
+ {
+ if ($this->columns === $this->rows) {
+ return (new LU($this))->solve($B);
+ }
+
+ return (new QR($this))->solve($B);
+ }
+
protected static $getters = [
'rows',
'columns',
@@ -355,8 +377,8 @@ class Matrix
}
protected static $functions = [
- 'antidiagonal',
'adjoint',
+ 'antidiagonal',
'cofactors',
'determinant',
'diagonal',
@@ -388,12 +410,13 @@ class Matrix
{
$functionName = strtolower(str_replace('_', '', $functionName));
- if (in_array($functionName, self::$functions, true) || in_array($functionName, self::$operations, true)) {
- $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
- if (is_callable($functionName)) {
- $arguments = array_values(array_merge([$this], $arguments));
- return call_user_func_array($functionName, $arguments);
- }
+ // Test for function calls
+ if (in_array($functionName, self::$functions, true)) {
+ return Functions::$functionName($this, ...$arguments);
+ }
+ // Test for operation calls
+ if (in_array($functionName, self::$operations, true)) {
+ return Operations::$functionName($this, ...$arguments);
}
throw new Exception('Function or Operation does not exist');
}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operations.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operations.php
new file mode 100644
index 00000000000..e3d88d64183
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operations.php
@@ -0,0 +1,157 @@
+execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function directsum(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('DirectSum operation requires at least 2 arguments');
+ }
+
+ $matrix = array_shift($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('DirectSum arguments must be Matrix or array');
+ }
+
+ $result = new DirectSum($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function divideby(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('Division operation requires at least 2 arguments');
+ }
+
+ $matrix = array_shift($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('Division arguments must be Matrix or array');
+ }
+
+ $result = new Division($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function divideinto(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('Division operation requires at least 2 arguments');
+ }
+
+ $matrix = array_pop($matrixValues);
+ $matrixValues = array_reverse($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('Division arguments must be Matrix or array');
+ }
+
+ $result = new Division($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function multiply(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('Multiplication operation requires at least 2 arguments');
+ }
+
+ $matrix = array_shift($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('Multiplication arguments must be Matrix or array');
+ }
+
+ $result = new Multiplication($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function subtract(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('Subtraction operation requires at least 2 arguments');
+ }
+
+ $matrix = array_shift($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('Subtraction arguments must be Matrix or array');
+ }
+
+ $result = new Subtraction($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Division.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Division.php
index b262f596ae2..dbfec9d3f7d 100644
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Division.php
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Division.php
@@ -2,9 +2,10 @@
namespace Matrix\Operators;
+use Matrix\Div0Exception;
+use Matrix\Exception;
use \Matrix\Matrix;
use \Matrix\Functions;
-use Matrix\Exception;
class Division extends Multiplication
{
@@ -15,22 +16,18 @@ class Division extends Multiplication
* @throws Exception If the provided argument is not appropriate for the operation
* @return $this The operation object, allowing multiple divisions to be chained
**/
- public function execute($value): Operator
+ public function execute($value, string $type = 'division'): Operator
{
if (is_array($value)) {
$value = new Matrix($value);
}
if (is_object($value) && ($value instanceof Matrix)) {
- try {
- $value = Functions::inverse($value);
- } catch (Exception $e) {
- throw new Exception('Division can only be calculated using a matrix with a non-zero determinant');
- }
+ $value = Functions::inverse($value, $type);
- return $this->multiplyMatrix($value);
+ return $this->multiplyMatrix($value, $type);
} elseif (is_numeric($value)) {
- return $this->multiplyScalar(1 / $value);
+ return $this->multiplyScalar(1 / $value, $type);
}
throw new Exception('Invalid argument for division');
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php
index e75d0ade17b..0761e466a40 100644
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php
@@ -5,6 +5,7 @@ namespace Matrix\Operators;
use Matrix\Matrix;
use \Matrix\Builder;
use Matrix\Exception;
+use Throwable;
class Multiplication extends Operator
{
@@ -15,19 +16,19 @@ class Multiplication extends Operator
* @throws Exception If the provided argument is not appropriate for the operation
* @return $this The operation object, allowing multiple multiplications to be chained
**/
- public function execute($value): Operator
+ public function execute($value, string $type = 'multiplication'): Operator
{
if (is_array($value)) {
$value = new Matrix($value);
}
if (is_object($value) && ($value instanceof Matrix)) {
- return $this->multiplyMatrix($value);
+ return $this->multiplyMatrix($value, $type);
} elseif (is_numeric($value)) {
- return $this->multiplyScalar($value);
+ return $this->multiplyScalar($value, $type);
}
- throw new Exception('Invalid argument for multiplication');
+ throw new Exception("Invalid argument for $type");
}
/**
@@ -36,12 +37,16 @@ class Multiplication extends Operator
* @param mixed $value The numeric value to multiply with the current base value
* @return $this The operation object, allowing multiple mutiplications to be chained
**/
- protected function multiplyScalar($value): Operator
+ protected function multiplyScalar($value, string $type = 'multiplication'): Operator
{
- for ($row = 0; $row < $this->rows; ++$row) {
- for ($column = 0; $column < $this->columns; ++$column) {
- $this->matrix[$row][$column] *= $value;
+ try {
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ $this->matrix[$row][$column] *= $value;
+ }
}
+ } catch (Throwable $e) {
+ throw new Exception("Invalid argument for $type");
}
return $this;
@@ -54,7 +59,7 @@ class Multiplication extends Operator
* @return $this The operation object, allowing multiple mutiplications to be chained
* @throws Exception If the provided argument is not appropriate for the operation
**/
- protected function multiplyMatrix(Matrix $value): Operator
+ protected function multiplyMatrix(Matrix $value, string $type = 'multiplication'): Operator
{
$this->validateReflectingDimensions($value);
@@ -62,13 +67,17 @@ class Multiplication extends Operator
$newColumns = $value->columns;
$matrix = Builder::createFilledMatrix(0, $newRows, $newColumns)
->toArray();
- for ($row = 0; $row < $newRows; ++$row) {
- for ($column = 0; $column < $newColumns; ++$column) {
- $columnData = $value->getColumns($column + 1)->toArray();
- foreach ($this->matrix[$row] as $key => $valueData) {
- $matrix[$row][$column] += $valueData * $columnData[$key][0];
+ try {
+ for ($row = 0; $row < $newRows; ++$row) {
+ for ($column = 0; $column < $newColumns; ++$column) {
+ $columnData = $value->getColumns($column + 1)->toArray();
+ foreach ($this->matrix[$row] as $key => $valueData) {
+ $matrix[$row][$column] += $valueData * $columnData[$key][0];
+ }
}
}
+ } catch (Throwable $e) {
+ throw new Exception("Invalid argument for $type");
}
$this->matrix = $matrix;
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/adjoint.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/adjoint.php
deleted file mode 100644
index ec1933f11ef..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/adjoint.php
+++ /dev/null
@@ -1,30 +0,0 @@
- $matrixValues The matrices to add
- * @return Matrix
- * @throws Exception
- */
-function add(...$matrixValues): Matrix
-{
- if (count($matrixValues) < 2) {
- throw new Exception('Addition operation requires at least 2 arguments');
- }
-
- $matrix = array_shift($matrixValues);
-
- if (is_array($matrix)) {
- $matrix = new Matrix($matrix);
- }
- if (!$matrix instanceof Matrix) {
- throw new Exception('Addition arguments must be Matrix or array');
- }
-
- $result = new Addition($matrix);
-
- foreach ($matrixValues as $matrix) {
- $result->execute($matrix);
- }
-
- return $result->result();
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/directsum.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/directsum.php
deleted file mode 100644
index 0fb540d7ff0..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/directsum.php
+++ /dev/null
@@ -1,44 +0,0 @@
- $matrixValues The matrices to add
- * @return Matrix
- * @throws Exception
- */
-function directsum(...$matrixValues): Matrix
-{
- if (count($matrixValues) < 2) {
- throw new Exception('DirectSum operation requires at least 2 arguments');
- }
-
- $matrix = array_shift($matrixValues);
-
- if (is_array($matrix)) {
- $matrix = new Matrix($matrix);
- }
- if (!$matrix instanceof Matrix) {
- throw new Exception('DirectSum arguments must be Matrix or array');
- }
-
- $result = new DirectSum($matrix);
-
- foreach ($matrixValues as $matrix) {
- $result->execute($matrix);
- }
-
- return $result->result();
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideby.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideby.php
deleted file mode 100644
index 3c6074bb069..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideby.php
+++ /dev/null
@@ -1,44 +0,0 @@
- $matrixValues The matrices to divide
- * @return Matrix
- * @throws Exception
- */
-function divideby(...$matrixValues): Matrix
-{
- if (count($matrixValues) < 2) {
- throw new Exception('Division operation requires at least 2 arguments');
- }
-
- $matrix = array_shift($matrixValues);
-
- if (is_array($matrix)) {
- $matrix = new Matrix($matrix);
- }
- if (!$matrix instanceof Matrix) {
- throw new Exception('Division arguments must be Matrix or array');
- }
-
- $result = new Division($matrix);
-
- foreach ($matrixValues as $matrix) {
- $result->execute($matrix);
- }
-
- return $result->result();
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideinto.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideinto.php
deleted file mode 100644
index d0487c8e8a5..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideinto.php
+++ /dev/null
@@ -1,44 +0,0 @@
- $matrixValues The numbers to divide
- * @return Matrix
- * @throws Exception
- */
-function divideinto(...$matrixValues): Matrix
-{
- if (count($matrixValues) < 2) {
- throw new Exception('Division operation requires at least 2 arguments');
- }
-
- $matrix = array_shift($matrixValues);
-
- if (is_array($matrix)) {
- $matrix = new Matrix($matrix);
- }
- if (!$matrix instanceof Matrix) {
- throw new Exception('Division arguments must be Matrix or array');
- }
-
- $result = new Division($matrix);
-
- foreach ($matrixValues as $matrix) {
- $result->execute($matrix);
- }
-
- return $result->result();
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/multiply.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/multiply.php
deleted file mode 100644
index 10bca05f35e..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/multiply.php
+++ /dev/null
@@ -1,44 +0,0 @@
- $matrixValues The matrices to multiply
- * @return Matrix
- * @throws Exception
- */
-function multiply(...$matrixValues): Matrix
-{
- if (count($matrixValues) < 2) {
- throw new Exception('Multiplication operation requires at least 2 arguments');
- }
-
- $matrix = array_shift($matrixValues);
-
- if (is_array($matrix)) {
- $matrix = new Matrix($matrix);
- }
- if (!$matrix instanceof Matrix) {
- throw new Exception('Multiplication arguments must be Matrix or array');
- }
-
- $result = new Multiplication($matrix);
-
- foreach ($matrixValues as $matrix) {
- $result->execute($matrix);
- }
-
- return $result->result();
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/subtract.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/subtract.php
deleted file mode 100644
index 55a827f6844..00000000000
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/subtract.php
+++ /dev/null
@@ -1,44 +0,0 @@
- $matrixValues The matrices to subtract
- * @return Matrix
- * @throws Exception
- */
-function subtract(...$matrixValues): Matrix
-{
- if (count($matrixValues) < 2) {
- throw new Exception('Subtraction operation requires at least 2 arguments');
- }
-
- $matrix = array_shift($matrixValues);
-
- if (is_array($matrix)) {
- $matrix = new Matrix($matrix);
- }
- if (!$matrix instanceof Matrix) {
- throw new Exception('Subtraction arguments must be Matrix or array');
- }
-
- $result = new Subtraction($matrix);
-
- foreach ($matrixValues as $matrix) {
- $result->execute($matrix);
- }
-
- return $result->result();
-}
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/composer.json b/lib/phpspreadsheet/vendor/markbaker/matrix/composer.json
index 1386afc2aa3..fa90f56390b 100644
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/composer.json
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/composer.json
@@ -12,7 +12,7 @@
}
],
"require": {
- "php": "^7.2 || ^8.0"
+ "php": "^7.1 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
@@ -27,48 +27,12 @@
"autoload": {
"psr-4": {
"Matrix\\": "classes/src/"
- },
- "files": [
- "classes/src/functions/adjoint.php",
- "classes/src/functions/antidiagonal.php",
- "classes/src/functions/cofactors.php",
- "classes/src/functions/determinant.php",
- "classes/src/functions/diagonal.php",
- "classes/src/functions/identity.php",
- "classes/src/functions/inverse.php",
- "classes/src/functions/minors.php",
- "classes/src/functions/trace.php",
- "classes/src/functions/transpose.php",
- "classes/src/operations/add.php",
- "classes/src/operations/directsum.php",
- "classes/src/operations/subtract.php",
- "classes/src/operations/multiply.php",
- "classes/src/operations/divideby.php",
- "classes/src/operations/divideinto.php"
- ]
+ }
},
"autoload-dev": {
"psr-4": {
- "Matrix\\Test\\": "unitTests/classes/src/"
- },
- "files": [
- "unitTests/classes/src/functions/adjointTest.php",
- "unitTests/classes/src/functions/antidiagonalTest.php",
- "unitTests/classes/src/functions/cofactorsTest.php",
- "unitTests/classes/src/functions/determinantTest.php",
- "unitTests/classes/src/functions/diagonalTest.php",
- "unitTests/classes/src/functions/identityTest.php",
- "unitTests/classes/src/functions/inverseTest.php",
- "unitTests/classes/src/functions/minorsTest.php",
- "unitTests/classes/src/functions/traceTest.php",
- "unitTests/classes/src/functions/transposeTest.php",
- "unitTests/classes/src/operations/addTest.php",
- "unitTests/classes/src/operations/directsumTest.php",
- "unitTests/classes/src/operations/subtractTest.php",
- "unitTests/classes/src/operations/multiplyTest.php",
- "unitTests/classes/src/operations/dividebyTest.php",
- "unitTests/classes/src/operations/divideintoTest.php"
- ]
+ "MatrixTest\\": "unitTests/classes/src/"
+ }
},
"scripts": {
"style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n",
@@ -80,4 +44,4 @@
"coverage": "phpunit -c phpunit.xml.dist --coverage-text --coverage-html ./build/coverage"
},
"minimum-stability": "dev"
-}
+}
\ No newline at end of file
diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/examples/test.php b/lib/phpspreadsheet/vendor/markbaker/matrix/examples/test.php
index d8b56dcd9a7..071dae910c0 100644
--- a/lib/phpspreadsheet/vendor/markbaker/matrix/examples/test.php
+++ b/lib/phpspreadsheet/vendor/markbaker/matrix/examples/test.php
@@ -1,19 +1,33 @@
directsum(new Matrix\Matrix($grid2));
+$matrix = new Matrix($grid);
+$target = new Matrix($targetGrid);
-var_dump($new);
+$decomposition = new QR($matrix);
+
+$X = $decomposition->solve($target);
+
+echo 'X', PHP_EOL;
+var_export($X->toArray());
+echo PHP_EOL;
+
+$resolve = $matrix->multiply($X);
+
+echo 'Resolve', PHP_EOL;
+var_export($resolve->toArray());
+echo PHP_EOL;
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.md b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
index a4741afe0aa..3bfc215a0b7 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
@@ -5,11 +5,96 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com)
and this project adheres to [Semantic Versioning](https://semver.org).
-## Unreleased - TBD
+## 1.21.0 - 2022-01-06
### Added
-- Nothing.
+- Ability to add a picture to the background of the comment. Supports four image formats: png, jpeg, gif, bmp. New `Comment::setSizeAsBackgroundImage()` to change the size of a comment to the size of a background image. [Issue #1547](https://github.com/PHPOffice/PhpSpreadsheet/issues/1547) [PR #2422](https://github.com/PHPOffice/PhpSpreadsheet/pull/2422)
+- Ability to set default paper size and orientation [PR #2410](https://github.com/PHPOffice/PhpSpreadsheet/pull/2410)
+- Ability to extend AutoFilter to Maximum Row [PR #2414](https://github.com/PHPOffice/PhpSpreadsheet/pull/2414)
+
+### Changed
+
+- Xlsx Writer will evaluate AutoFilter only if it is as yet unevaluated, or has changed since it was last evaluated [PR #2414](https://github.com/PHPOffice/PhpSpreadsheet/pull/2414)
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Rounding in `NumberFormatter` [Issue #2385](https://github.com/PHPOffice/PhpSpreadsheet/issues/2385) [PR #2399](https://github.com/PHPOffice/PhpSpreadsheet/pull/2399)
+- Support for themes [Issue #2075](https://github.com/PHPOffice/PhpSpreadsheet/issues/2075) [Issue #2387](https://github.com/PHPOffice/PhpSpreadsheet/issues/2387) [PR #2403](https://github.com/PHPOffice/PhpSpreadsheet/pull/2403)
+- Read spreadsheet with `#` in name [Issue #2405](https://github.com/PHPOffice/PhpSpreadsheet/issues/2405) [PR #2409](https://github.com/PHPOffice/PhpSpreadsheet/pull/2409)
+- Improve PDF support for page size and orientation [Issue #1691](https://github.com/PHPOffice/PhpSpreadsheet/issues/1691) [PR #2410](https://github.com/PHPOffice/PhpSpreadsheet/pull/2410)
+- Wildcard handling issues in text match [Issue #2430](https://github.com/PHPOffice/PhpSpreadsheet/issues/2430) [PR #2431](https://github.com/PHPOffice/PhpSpreadsheet/pull/2431)
+- Respect DataType in `insertNewBefore` [PR #2433](https://github.com/PHPOffice/PhpSpreadsheet/pull/2433)
+- Handle rows explicitly hidden after AutoFilter [Issue #1641](https://github.com/PHPOffice/PhpSpreadsheet/issues/1641) [PR #2414](https://github.com/PHPOffice/PhpSpreadsheet/pull/2414)
+- Special characters in image file name [Issue #1470](https://github.com/PHPOffice/PhpSpreadsheet/issues/1470) [Issue #2415](https://github.com/PHPOffice/PhpSpreadsheet/issues/2415) [PR #2416](https://github.com/PHPOffice/PhpSpreadsheet/pull/2416)
+- Mpdf with very many styles [Issue #2432](https://github.com/PHPOffice/PhpSpreadsheet/issues/2432) [PR #2434](https://github.com/PHPOffice/PhpSpreadsheet/pull/2434)
+- Name clashes between parsed and unparsed drawings [Issue #1767](https://github.com/PHPOffice/PhpSpreadsheet/issues/1767) [Issue #2396](https://github.com/PHPOffice/PhpSpreadsheet/issues/2396) [PR #2423](https://github.com/PHPOffice/PhpSpreadsheet/pull/2423)
+- Fill pattern start and end colors [Issue #2441](https://github.com/PHPOffice/PhpSpreadsheet/issues/2441) [PR #2444](https://github.com/PHPOffice/PhpSpreadsheet/pull/2444)
+- General style specified in wrong case [Issue #2450](https://github.com/PHPOffice/PhpSpreadsheet/issues/2450) [PR #2451](https://github.com/PHPOffice/PhpSpreadsheet/pull/2451)
+- Null passed to `AutoFilter::setRange()` [Issue #2281](https://github.com/PHPOffice/PhpSpreadsheet/issues/2281) [PR #2454](https://github.com/PHPOffice/PhpSpreadsheet/pull/2454)
+- Another undefined index in Xls reader (#2470) [Issue #2463](https://github.com/PHPOffice/PhpSpreadsheet/issues/2463) [PR #2470](https://github.com/PHPOffice/PhpSpreadsheet/pull/2470)
+- Allow single-cell checks on conditional styles, even when the style is configured for a range of cells (#) [PR #2483](https://github.com/PHPOffice/PhpSpreadsheet/pull/2483)
+
+## 1.20.0 - 2021-11-23
+
+### Added
+
+- Xlsx Writer Support for WMF Files [#2339](https://github.com/PHPOffice/PhpSpreadsheet/issues/2339)
+- Use standard temporary file for internal use of HTMLPurifier [#2383](https://github.com/PHPOffice/PhpSpreadsheet/issues/2383)
+
+### Changed
+
+- Drop support for PHP 7.2, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support
+- Use native typing for objects that were already documented as such
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Fixed null conversation for strToUpper [#2292](https://github.com/PHPOffice/PhpSpreadsheet/issues/2292)
+- Fixed Trying to access array offset on value of type null (Xls Reader) [#2315](https://github.com/PHPOffice/PhpSpreadsheet/issues/2315)
+- Don't corrupt XLSX files containing data validation [#2377](https://github.com/PHPOffice/PhpSpreadsheet/issues/2377)
+- Non-fixed cells were not updated if shared formula has a fixed cell [#2354](https://github.com/PHPOffice/PhpSpreadsheet/issues/2354)
+- Declare key of generic ArrayObject
+- CSV reader better support for boolean values [#2374](https://github.com/PHPOffice/PhpSpreadsheet/pull/2374)
+- Some ZIP file could not be read [#2376](https://github.com/PHPOffice/PhpSpreadsheet/pull/2376)
+- Fix regression were hyperlinks could not be read [#2391](https://github.com/PHPOffice/PhpSpreadsheet/pull/2391)
+- AutoFilter Improvements [#2393](https://github.com/PHPOffice/PhpSpreadsheet/pull/2393)
+- Don't corrupt file when using chart with fill color [#589](https://github.com/PHPOffice/PhpSpreadsheet/pull/589)
+- Restore imperfect array formula values in xlsx writer [#2343](https://github.com/PHPOffice/PhpSpreadsheet/pull/2343)
+- Restore explicit list of changes to PHPExcel migration document [#1546](https://github.com/PHPOffice/PhpSpreadsheet/issues/1546)
+
+## 1.19.0 - 2021-10-31
+
+### Added
+
+- Ability to set style on named range, and validate input to setSelectedCells [Issue #2279](https://github.com/PHPOffice/PhpSpreadsheet/issues/2279) [PR #2280](https://github.com/PHPOffice/PhpSpreadsheet/pull/2280)
+- Process comments in Sylk file [Issue #2276](https://github.com/PHPOffice/PhpSpreadsheet/issues/2276) [PR #2277](https://github.com/PHPOffice/PhpSpreadsheet/pull/2277)
+- Addition of Custom Properties to Ods Writer, and 32-bit-safe timestamps for Document Properties [PR #2113](https://github.com/PHPOffice/PhpSpreadsheet/pull/2113)
+- Added callback to CSV reader to set user-specified defaults for various properties (especially for escape which has a poor PHP-inherited default of backslash which does not correspond with Excel) [PR #2103](https://github.com/PHPOffice/PhpSpreadsheet/pull/2103)
+- Phase 1 of better namespace handling for Xlsx, resolving many open issues [PR #2173](https://github.com/PHPOffice/PhpSpreadsheet/pull/2173) [PR #2204](https://github.com/PHPOffice/PhpSpreadsheet/pull/2204) [PR #2303](https://github.com/PHPOffice/PhpSpreadsheet/pull/2303)
+- Add ability to extract images if source is a URL [Issue #1997](https://github.com/PHPOffice/PhpSpreadsheet/issues/1997) [PR #2072](https://github.com/PHPOffice/PhpSpreadsheet/pull/2072)
+- Support for passing flags in the Reader `load()` and Writer `save()`methods, and through the IOFactory, to set behaviours [PR #2136](https://github.com/PHPOffice/PhpSpreadsheet/pull/2136)
+ - See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/reading-and-writing-to-file/#readerwriter-flags) for details
+- More flexibility in the StringValueBinder to determine what datatypes should be treated as strings [PR #2138](https://github.com/PHPOffice/PhpSpreadsheet/pull/2138)
+- Helper class for conversion between css size Units of measure (`px`, `pt`, `pc`, `in`, `cm`, `mm`) [PR #2152](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145)
+- Allow Row height and Column Width to be set using different units of measure (`px`, `pt`, `pc`, `in`, `cm`, `mm`), rather than only in points or MS Excel column width units [PR #2152](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145)
+- Ability to stream to an Amazon S3 bucket [Issue #2249](https://github.com/PHPOffice/PhpSpreadsheet/issues/2249)
+- Provided a Size Helper class to validate size values (pt, px, em) [PR #1694](https://github.com/PHPOffice/PhpSpreadsheet/pull/1694)
### Changed
@@ -17,7 +102,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Deprecated
-- Nothing.
+- PHP 8.1 will deprecate auto_detect_line_endings. As a result of this change, Csv Reader using some release after PHP8.1 will no longer be able to handle a Csv with Mac line endings.
### Removed
@@ -25,8 +110,145 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed
+- Unexpected format in Xlsx Timestamp [Issue #2331](https://github.com/PHPOffice/PhpSpreadsheet/issues/2331) [PR #2332](https://github.com/PHPOffice/PhpSpreadsheet/pull/2332)
+- Corrections for HLOOKUP [Issue #2123](https://github.com/PHPOffice/PhpSpreadsheet/issues/2123) [PR #2330](https://github.com/PHPOffice/PhpSpreadsheet/pull/2330)
+- Corrections for Xlsx Read Comments [Issue #2316](https://github.com/PHPOffice/PhpSpreadsheet/issues/2316) [PR #2329](https://github.com/PHPOffice/PhpSpreadsheet/pull/2329)
+- Lowercase Calibri font names [Issue #2273](https://github.com/PHPOffice/PhpSpreadsheet/issues/2273) [PR #2325](https://github.com/PHPOffice/PhpSpreadsheet/pull/2325)
+- isFormula Referencing Sheet with Space in Title [Issue #2304](https://github.com/PHPOffice/PhpSpreadsheet/issues/2304) [PR #2306](https://github.com/PHPOffice/PhpSpreadsheet/pull/2306)
+- Xls Reader Fatal Error due to Undefined Offset [Issue #1114](https://github.com/PHPOffice/PhpSpreadsheet/issues/1114) [PR #2308](https://github.com/PHPOffice/PhpSpreadsheet/pull/2308)
+- Permit Csv Reader delimiter to be set to null [Issue #2287](https://github.com/PHPOffice/PhpSpreadsheet/issues/2287) [PR #2288](https://github.com/PHPOffice/PhpSpreadsheet/pull/2288)
+- Csv Reader did not handle booleans correctly [PR #2232](https://github.com/PHPOffice/PhpSpreadsheet/pull/2232)
+- Problems when deleting sheet with local defined name [Issue #2266](https://github.com/PHPOffice/PhpSpreadsheet/issues/2266) [PR #2284](https://github.com/PHPOffice/PhpSpreadsheet/pull/2284)
+- Worksheet passwords were not always handled correctly [Issue #1897](https://github.com/PHPOffice/PhpSpreadsheet/issues/1897) [PR #2197](https://github.com/PHPOffice/PhpSpreadsheet/pull/2197)
+- Gnumeric Reader will now distinguish between Created and Modified timestamp [PR #2133](https://github.com/PHPOffice/PhpSpreadsheet/pull/2133)
+- Xls Reader will now handle MACCENTRALEUROPE with or without hyphen [Issue #549](https://github.com/PHPOffice/PhpSpreadsheet/issues/549) [PR #2213](https://github.com/PHPOffice/PhpSpreadsheet/pull/2213)
+- Tweaks to input file validation [Issue #1718](https://github.com/PHPOffice/PhpSpreadsheet/issues/1718) [PR #2217](https://github.com/PHPOffice/PhpSpreadsheet/pull/2217)
+- Html Reader did not handle comments correctly [Issue #2234](https://github.com/PHPOffice/PhpSpreadsheet/issues/2234) [PR #2235](https://github.com/PHPOffice/PhpSpreadsheet/pull/2235)
+- Apache OpenOffice Uses Unexpected Case for General format [Issue #2239](https://github.com/PHPOffice/PhpSpreadsheet/issues/2239) [PR #2242](https://github.com/PHPOffice/PhpSpreadsheet/pull/2242)
+- Problems with fraction formatting [Issue #2253](https://github.com/PHPOffice/PhpSpreadsheet/issues/2253) [PR #2254](https://github.com/PHPOffice/PhpSpreadsheet/pull/2254)
+- Xlsx Reader had problems reading file with no styles.xml or empty styles.xml [Issue #2246](https://github.com/PHPOffice/PhpSpreadsheet/issues/2246) [PR #2247](https://github.com/PHPOffice/PhpSpreadsheet/pull/2247)
+- Xlsx Reader did not read Data Validation flags correctly [Issue #2224](https://github.com/PHPOffice/PhpSpreadsheet/issues/2224) [PR #2225](https://github.com/PHPOffice/PhpSpreadsheet/pull/2225)
+- Better handling of empty arguments in Calculation engine [PR #2143](https://github.com/PHPOffice/PhpSpreadsheet/pull/2143)
+- Many fixes for Autofilter [Issue #2216](https://github.com/PHPOffice/PhpSpreadsheet/issues/2216) [PR #2141](https://github.com/PHPOffice/PhpSpreadsheet/pull/2141) [PR #2162](https://github.com/PHPOffice/PhpSpreadsheet/pull/2162) [PR #2218](https://github.com/PHPOffice/PhpSpreadsheet/pull/2218)
+- Locale generator will now use Unix line endings even on Windows [Issue #2172](https://github.com/PHPOffice/PhpSpreadsheet/issues/2172) [PR #2174](https://github.com/PHPOffice/PhpSpreadsheet/pull/2174)
+- Support differences in implementation of Text functions between Excel/Ods/Gnumeric [PR #2151](https://github.com/PHPOffice/PhpSpreadsheet/pull/2151)
+- Fixes to places where PHP8.1 enforces new or previously unenforced restrictions [PR #2137](https://github.com/PHPOffice/PhpSpreadsheet/pull/2137) [PR #2191](https://github.com/PHPOffice/PhpSpreadsheet/pull/2191) [PR #2231](https://github.com/PHPOffice/PhpSpreadsheet/pull/2231)
+- Clone for HashTable was incorrect [PR #2130](https://github.com/PHPOffice/PhpSpreadsheet/pull/2130)
+- Xlsx Reader was not evaluating Document Security Lock correctly [PR #2128](https://github.com/PHPOffice/PhpSpreadsheet/pull/2128)
+- Error in COUPNCD handling end of month [Issue #2116](https://github.com/PHPOffice/PhpSpreadsheet/issues/2116) [PR #2119](https://github.com/PHPOffice/PhpSpreadsheet/pull/2119)
+- Xls Writer Parser did not handle concatenation operator correctly [PR #2080](https://github.com/PHPOffice/PhpSpreadsheet/pull/2080)
+- Xlsx Writer did not handle boolean false correctly [Issue #2082](https://github.com/PHPOffice/PhpSpreadsheet/issues/2082) [PR #2087](https://github.com/PHPOffice/PhpSpreadsheet/pull/2087)
+- SUM needs to treat invalid strings differently depending on whether they come from a cell or are used as literals [Issue #2042](https://github.com/PHPOffice/PhpSpreadsheet/issues/2042) [PR #2045](https://github.com/PHPOffice/PhpSpreadsheet/pull/2045)
+- Html reader could have set illegal coordinates when dealing with embedded tables [Issue #2029](https://github.com/PHPOffice/PhpSpreadsheet/issues/2029) [PR #2032](https://github.com/PHPOffice/PhpSpreadsheet/pull/2032)
+- Documentation for printing gridlines was wrong [PR #2188](https://github.com/PHPOffice/PhpSpreadsheet/pull/2188)
+- Return Value Error - DatabaseAbstruct::buildQuery() return null but must be string [Issue #2158](https://github.com/PHPOffice/PhpSpreadsheet/issues/2158) [PR #2160](https://github.com/PHPOffice/PhpSpreadsheet/pull/2160)
+- Xlsx reader not recognize data validations that references another sheet [Issue #1432](https://github.com/PHPOffice/PhpSpreadsheet/issues/1432) [Issue #2149](https://github.com/PHPOffice/PhpSpreadsheet/issues/2149) [PR #2150](https://github.com/PHPOffice/PhpSpreadsheet/pull/2150) [PR #2265](https://github.com/PHPOffice/PhpSpreadsheet/pull/2265)
+- Don't calculate cell width for autosize columns if a cell contains a null or empty string value [Issue #2165](https://github.com/PHPOffice/PhpSpreadsheet/issues/2165) [PR #2167](https://github.com/PHPOffice/PhpSpreadsheet/pull/2167)
+- Allow negative interest rate values in a number of the Financial functions (`PPMT()`, `PMT()`, `FV()`, `PV()`, `NPER()`, etc) [Issue #2163](https://github.com/PHPOffice/PhpSpreadsheet/issues/2163) [PR #2164](https://github.com/PHPOffice/PhpSpreadsheet/pull/2164)
+- Xls Reader changing grey background to black in Excel template [Issue #2147](https://github.com/PHPOffice/PhpSpreadsheet/issues/2147) [PR #2156](https://github.com/PHPOffice/PhpSpreadsheet/pull/2156)
+- Column width and Row height styles in the Html Reader when the value includes a unit of measure [Issue #2145](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145).
+- Data Validation flags not set correctly when reading XLSX files [Issue #2224](https://github.com/PHPOffice/PhpSpreadsheet/issues/2224) [PR #2225](https://github.com/PHPOffice/PhpSpreadsheet/pull/2225)
+- Reading XLSX files without styles.xml throws an exception [Issue #2246](https://github.com/PHPOffice/PhpSpreadsheet/issues/2246)
+- Improved performance of `Style::applyFromArray()` when applied to several cells [PR #1785](https://github.com/PHPOffice/PhpSpreadsheet/issues/1785).
+- Improve XLSX parsing speed if no readFilter is applied (again) - [#772](https://github.com/PHPOffice/PhpSpreadsheet/issues/772)
+
+## 1.18.0 - 2021-05-31
+
+### Added
+
+- Enhancements to CSV Reader, allowing options to be set when using `IOFactory::load()` with a callback to set delimiter, enclosure, charset etc [PR #2103](https://github.com/PHPOffice/PhpSpreadsheet/pull/2103) - See [documentation](https://github.com/PHPOffice/PhpSpreadsheet/blob/master/docs/topics/reading-and-writing-to-file.md#csv-comma-separated-values) for details.
+- Implemented basic AutoFiltering for Ods Reader and Writer [PR #2053](https://github.com/PHPOffice/PhpSpreadsheet/pull/2053)
+- Implemented basic AutoFiltering for Gnumeric Reader [PR #2055](https://github.com/PHPOffice/PhpSpreadsheet/pull/2055)
+- Improved support for Row and Column ranges in formulae [Issue #1755](https://github.com/PHPOffice/PhpSpreadsheet/issues/1755) [PR #2028](https://github.com/PHPOffice/PhpSpreadsheet/pull/2028)
+- Implemented URLENCODE() Web Function
+- Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions.
+- Support for ActiveSheet and SelectedCells in the ODS Reader and Writer [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908)
+- Support for notContainsText Conditional Style in xlsx [Issue #984](https://github.com/PHPOffice/PhpSpreadsheet/issues/984)
+
+### Changed
+
+- Use of `nb` rather than `no` as the locale code for Norsk Bokmål.
+
+### Deprecated
+
+- All Excel Function implementations in `Calculation\Database`, `Calculation\DateTime`, `Calculation\Engineering`, `Calculation\Financial`, `Calculation\Logical`, `Calculation\LookupRef`, `Calculation\MathTrig`, `Calculation\Statistical`, `Calculation\TextData` and `Calculation\Web` have been moved to dedicated classes for individual functions or groups of related functions. See the docblocks against all the deprecated methods for details of the new methods to call instead. At some point, these old classes will be deleted.
+
+### Removed
+
+- Use of `nb` rather than `no` as the locale language code for Norsk Bokmål.
+
+### Fixed
+
+- Fixed error in COUPNCD() calculation for end of month [Issue #2116](https://github.com/PHPOffice/PhpSpreadsheet/issues/2116) - [PR #2119](https://github.com/PHPOffice/PhpSpreadsheet/pull/2119)
+- Resolve default values when a null argument is passed for HLOOKUP(), VLOOKUP() and ADDRESS() functions [Issue #2120](https://github.com/PHPOffice/PhpSpreadsheet/issues/2120) - [PR #2121](https://github.com/PHPOffice/PhpSpreadsheet/pull/2121)
+- Fixed incorrect R1C1 to A1 subtraction formula conversion (`R[-2]C-R[2]C`) [Issue #2076](https://github.com/PHPOffice/PhpSpreadsheet/pull/2076) [PR #2086](https://github.com/PHPOffice/PhpSpreadsheet/pull/2086)
+- Correctly handle absolute A1 references when converting to R1C1 format [PR #2060](https://github.com/PHPOffice/PhpSpreadsheet/pull/2060)
+- Correct default fill style for conditional without a pattern defined [Issue #2035](https://github.com/PHPOffice/PhpSpreadsheet/issues/2035) [PR #2050](https://github.com/PHPOffice/PhpSpreadsheet/pull/2050)
+- Fixed issue where array key check for existince before accessing arrays in Xlsx.php [PR #1970](https://github.com/PHPOffice/PhpSpreadsheet/pull/1970)
+- Fixed issue with quoted strings in number format mask rendered with toFormattedString() [Issue 1972#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1972) [PR #1978](https://github.com/PHPOffice/PhpSpreadsheet/pull/1978)
+- Fixed issue with percentage formats in number format mask rendered with toFormattedString() [Issue 1929#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1929) [PR #1928](https://github.com/PHPOffice/PhpSpreadsheet/pull/1928)
+- Fixed issue with _ spacing character in number format mask corrupting output from toFormattedString() [Issue 1924#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1924) [PR #1927](https://github.com/PHPOffice/PhpSpreadsheet/pull/1927)
+- Fix for [Issue #1887](https://github.com/PHPOffice/PhpSpreadsheet/issues/1887) - Lose Track of Selected Cells After Save
+- Fixed issue with Xlsx@listWorksheetInfo not returning any data
+- Fixed invalid arguments triggering mb_substr() error in LEFT(), MID() and RIGHT() text functions [Issue #640](https://github.com/PHPOffice/PhpSpreadsheet/issues/640)
+- Fix for [Issue #1916](https://github.com/PHPOffice/PhpSpreadsheet/issues/1916) - Invalid signature check for XML files
+- Fix change in `Font::setSize()` behavior for PHP8 [PR #2100](https://github.com/PHPOffice/PhpSpreadsheet/pull/2100)
+
+## 1.17.1 - 2021-03-01
+
+### Added
+
+- Implementation of the Excel `AVERAGEIFS()` functions as part of a restructuring of Database functions and Conditional Statistical functions.
+- Support for date values and percentages in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1875](https://github.com/PHPOffice/PhpSpreadsheet/pull/1875)
+- Support for booleans, and for wildcard text search in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1876](https://github.com/PHPOffice/PhpSpreadsheet/pull/1876)
+- Implemented DataBar for conditional formatting in Xlsx, providing read/write and creation of (type, value, direction, fills, border, axis position, color settings) as DataBar options in Excel. [#1754](https://github.com/PHPOffice/PhpSpreadsheet/pull/1754)
+- Alignment for ODS Writer [#1796](https://github.com/PHPOffice/PhpSpreadsheet/issues/1796)
+- Basic implementation of the PERMUTATIONA() Statistical Function
+
+### Changed
+
+- Formula functions that previously called PHP functions directly are now processed through the Excel Functions classes; resolving issues with PHP8 stricter typing. [#1789](https://github.com/PHPOffice/PhpSpreadsheet/issues/1789)
+
+ The following MathTrig functions are affected:
+ `ABS()`, `ACOS()`, `ACOSH()`, `ASIN()`, `ASINH()`, `ATAN()`, `ATANH()`,
+ `COS()`, `COSH()`, `DEGREES()` (rad2deg), `EXP()`, `LN()` (log), `LOG10()`,
+ `RADIANS()` (deg2rad), `SIN()`, `SINH()`, `SQRT()`, `TAN()`, `TANH()`.
+
+ One TextData function is also affected: `REPT()` (str_repeat).
+- `formatAsDate` correctly matches language metadata, reverting c55272e
+- Formulae that previously crashed on sub function call returning excel error value now return said value.
+ The following functions are affected `CUMPRINC()`, `CUMIPMT()`, `AMORLINC()`,
+ `AMORDEGRC()`.
+- Adapt some function error return value to match excel's error.
+ The following functions are affected `PPMT()`, `IPMT()`.
+
+### Deprecated
+
+- Calling many of the Excel formula functions directly rather than through the Calculation Engine.
+
+ The logic for these Functions is now being moved out of the categorised `Database`, `DateTime`, `Engineering`, `Financial`, `Logical`, `LookupRef`, `MathTrig`, `Statistical`, `TextData` and `Web` classes into small, dedicated classes for individual functions or related groups of functions.
+
+ This makes the logic in these classes easier to maintain; and will reduce the memory footprint required to execute formulae when calling these functions.
+
+### Removed
+
- Nothing.
+### Fixed
+
+- Avoid Duplicate Titles When Reading Multiple HTML Files.[Issue #1823](https://github.com/PHPOffice/PhpSpreadsheet/issues/1823) [PR #1829](https://github.com/PHPOffice/PhpSpreadsheet/pull/1829)
+- Fixed issue with Worksheet's `getCell()` method when trying to get a cell by defined name. [#1858](https://github.com/PHPOffice/PhpSpreadsheet/issues/1858)
+- Fix possible endless loop in NumberFormat Masks [#1792](https://github.com/PHPOffice/PhpSpreadsheet/issues/1792)
+- Fix problem resulting from literal dot inside quotes in number format masks [PR #1830](https://github.com/PHPOffice/PhpSpreadsheet/pull/1830)
+- Resolve Google Sheets Xlsx charts issue. Google Sheets uses oneCellAnchor positioning and does not include *Cache values in the exported Xlsx [PR #1761](https://github.com/PHPOffice/PhpSpreadsheet/pull/1761)
+- Fix for Xlsx Chart axis titles mapping to correct X or Y axis label when only one is present [PR #1760](https://github.com/PHPOffice/PhpSpreadsheet/pull/1760)
+- Fix For Null Exception on ODS Read of Page Settings. [#1772](https://github.com/PHPOffice/PhpSpreadsheet/issues/1772)
+- Fix Xlsx reader overriding manually set number format with builtin number format [PR #1805](https://github.com/PHPOffice/PhpSpreadsheet/pull/1805)
+- Fix Xlsx reader cell alignment [PR #1710](https://github.com/PHPOffice/PhpSpreadsheet/pull/1710)
+- Fix for not yet implemented data-types in Open Document writer [Issue #1674](https://github.com/PHPOffice/PhpSpreadsheet/issues/1674)
+- Fix XLSX reader when having a corrupt numeric cell data type [PR #1664](https://github.com/phpoffice/phpspreadsheet/pull/1664)
+- Fix on `CUMPRINC()`, `CUMIPMT()`, `AMORLINC()`, `AMORDEGRC()` usage. When those functions called one of `YEARFRAC()`, `PPMT()`, `IPMT()` and they would get back an error value (represented as a string), trying to use numeral operands (`+`, `/`, `-`, `*`) on said return value and a number (`float or `int`) would fail.
+
## 1.16.0 - 2020-12-31
### Added
@@ -39,7 +261,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Deprecated
-- Nothing.
+- All Excel Function implementations in `Calculation\Database`, `Calculation\DateTime`, `Calculation\Engineering`, `Calculation\Financial`, `Calculation\Logical`, `Calculation\LookupRef`, `Calculation\MathTrig`, `Calculation\Statistical`, `Calculation\TextData` and `Calculation\Web` have been moved to dedicated classes for individual functions or groups of related functions. See the docblocks against all the deprecated methods for details of the new methods to call instead. At some point, these old classes will be deleted.
### Removed
@@ -47,6 +269,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed
+- Fixed issue with absolute path in worksheets' Target [PR #1769](https://github.com/PHPOffice/PhpSpreadsheet/pull/1769)
- Fix for Xls Reader when SST has a bad length [#1592](https://github.com/PHPOffice/PhpSpreadsheet/issues/1592)
- Resolve Xlsx loader issue whe hyperlinks don't have a destination
- Resolve issues when printer settings resources IDs clash with drawing IDs
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md
index aed13fe2db2..f59535331c1 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md
@@ -9,3 +9,12 @@ If you would like to contribute, here are some notes and guidelines:
- All code changes must be validated by `composer check`
- [Helpful article about forking](https://help.github.com/articles/fork-a-repo/ "Forking a GitHub repository")
- [Helpful article about pull requests](https://help.github.com/articles/using-pull-requests/ "Pull Requests")
+
+## How to release
+
+1. Complete CHANGELOG.md and commit
+2. Create an annotated tag
+ 1. `git tag -a 1.2.3`
+ 2. Tag subject must be the version number, eg: `1.2.3`
+ 3. Tag body must be a copy-paste of the changelog entries
+3. Push tag with `git push --tags`, GitHub Actions will create a GitHub release automatically
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.json b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.json
index c6f8e30e890..d80fc62ffab 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.json
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.json
@@ -1,7 +1,19 @@
{
"name": "phpoffice/phpspreadsheet",
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
- "keywords": ["PHP", "OpenXML", "Excel", "xlsx", "xls", "ods", "gnumeric", "spreadsheet"],
+ "keywords": [
+ "PHP",
+ "OpenXML",
+ "Excel",
+ "xlsx",
+ "xls",
+ "ods",
+ "gnumeric",
+ "spreadsheet"
+ ],
+ "config": {
+ "sort-packages": true
+ },
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
"type": "library",
"license": "MIT",
@@ -29,47 +41,51 @@
"check": [
"php-cs-fixer fix --ansi --dry-run --diff",
"phpcs",
- "phpunit --color=always"
+ "phpunit --color=always",
+ "phpstan analyse --ansi"
],
"fix": [
"php-cs-fixer fix --ansi"
],
"versions": [
- "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 7.2- -n"
+ "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 7.3- -n"
]
},
"require": {
- "php": "^7.2||^8.0",
+ "php": "^7.3 || ^8.0",
"ext-ctype": "*",
"ext-dom": "*",
+ "ext-fileinfo": "*",
"ext-gd": "*",
"ext-iconv": "*",
- "ext-fileinfo": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
- "ext-SimpleXML": "*",
+ "ext-simplexml": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zip": "*",
"ext-zlib": "*",
+ "ezyang/htmlpurifier": "^4.13",
"maennchen/zipstream-php": "^2.1",
- "markbaker/complex": "^1.5||^2.0",
- "markbaker/matrix": "^1.2||^2.0",
- "psr/simple-cache": "^1.0",
+ "markbaker/complex": "^3.0",
+ "markbaker/matrix": "^3.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
- "ezyang/htmlpurifier": "^4.13"
+ "psr/simple-cache": "^1.0"
},
"require-dev": {
- "dompdf/dompdf": "^0.8.5",
- "friendsofphp/php-cs-fixer": "^2.16",
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "dompdf/dompdf": "^1.0",
+ "friendsofphp/php-cs-fixer": "^3.2",
"jpgraph/jpgraph": "^4.0",
"mpdf/mpdf": "^8.0",
"phpcompatibility/php-compatibility": "^9.3",
- "phpunit/phpunit": "^8.5||^9.3",
- "squizlabs/php_codesniffer": "^3.5",
- "tecnickcom/tcpdf": "^6.3"
+ "phpstan/phpstan": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^8.5 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.6",
+ "tecnickcom/tcpdf": "^6.4"
},
"suggest": {
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
@@ -84,7 +100,8 @@
},
"autoload-dev": {
"psr-4": {
- "PhpOffice\\PhpSpreadsheetTests\\": "tests/PhpSpreadsheetTests"
+ "PhpOffice\\PhpSpreadsheetTests\\": "tests/PhpSpreadsheetTests",
+ "PhpOffice\\PhpSpreadsheetInfra\\": "infra"
}
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
index 99260e3bf15..25f7695904c 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
@@ -12,7 +12,9 @@ use PhpOffice\PhpSpreadsheet\ReferenceHelper;
use PhpOffice\PhpSpreadsheet\Shared;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use ReflectionClassConstant;
use ReflectionMethod;
+use ReflectionParameter;
class Calculation
{
@@ -30,6 +32,9 @@ class Calculation
const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
// Cell reference (with or without a sheet reference) ensuring absolute/relative
const CALCULATION_REGEXP_CELLREF_RELATIVE = '((([^\s\(,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?(\$?\b[a-z]{1,3})(\$?\d{1,7})(?![\w.])';
+ const CALCULATION_REGEXP_COLUMN_RANGE = '(((([^\s\(,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?(\$?[a-z]{1,3})):(?![.*])';
+ const CALCULATION_REGEXP_ROW_RANGE = '(((([^\s\(,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?(\$?[1-9][0-9]{0,6})):(?![.*])';
+ // Cell reference (with or without a sheet reference) ensuring absolute/relative
// Cell ranges ensuring absolute/relative
const CALCULATION_REGEXP_COLUMNRANGE_RELATIVE = '(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
const CALCULATION_REGEXP_ROWRANGE_RELATIVE = '(\$?\d{1,7}):(\$?\d{1,7})';
@@ -130,7 +135,7 @@ class Calculation
/**
* Error message for any error that was raised/thrown by the calculation engine.
*
- * @var string
+ * @var null|string
*/
public $formulaError;
@@ -204,7 +209,7 @@ class Calculation
/**
* Locale-specific translations for Excel constants (True, False and Null).
*
- * @var string[]
+ * @var array
*/
public static $localeBoolean = [
'TRUE' => 'TRUE',
@@ -216,7 +221,7 @@ class Calculation
* Excel constant string translations to their PHP equivalents
* Constant conversion from text name/value to actual (datatyped) value.
*
- * @var string[]
+ * @var array
*/
private static $excelConstants = [
'TRUE' => true,
@@ -228,42 +233,42 @@ class Calculation
private static $phpSpreadsheetFunctions = [
'ABS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'abs',
+ 'functionCall' => [MathTrig\Absolute::class, 'evaluate'],
'argumentCount' => '1',
],
'ACCRINT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ACCRINT'],
- 'argumentCount' => '4-7',
+ 'functionCall' => [Financial\Securities\AccruedInterest::class, 'periodic'],
+ 'argumentCount' => '4-8',
],
'ACCRINTM' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ACCRINTM'],
+ 'functionCall' => [Financial\Securities\AccruedInterest::class, 'atMaturity'],
'argumentCount' => '3-5',
],
'ACOS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'acos',
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'acos'],
'argumentCount' => '1',
],
'ACOSH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'acosh',
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'acosh'],
'argumentCount' => '1',
],
'ACOT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ACOT'],
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'acot'],
'argumentCount' => '1',
],
'ACOTH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ACOTH'],
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'acoth'],
'argumentCount' => '1',
],
'ADDRESS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'cellAddress'],
+ 'functionCall' => [LookupRef\Address::class, 'cell'],
'argumentCount' => '2-5',
],
'AGGREGATE' => [
@@ -273,22 +278,22 @@ class Calculation
],
'AMORDEGRC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'AMORDEGRC'],
+ 'functionCall' => [Financial\Amortization::class, 'AMORDEGRC'],
'argumentCount' => '6,7',
],
'AMORLINC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'AMORLINC'],
+ 'functionCall' => [Financial\Amortization::class, 'AMORLINC'],
'argumentCount' => '6,7',
],
'AND' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalAnd'],
+ 'functionCall' => [Logical\Operations::class, 'logicalAnd'],
'argumentCount' => '1+',
],
'ARABIC' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ARABIC'],
+ 'functionCall' => [MathTrig\Arabic::class, 'evaluate'],
'argumentCount' => '1',
],
'AREAS' => [
@@ -296,6 +301,11 @@ class Calculation
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '1',
],
+ 'ARRAYTOTEXT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'ASC' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
'functionCall' => [Functions::class, 'DUMMY'],
@@ -303,52 +313,52 @@ class Calculation
],
'ASIN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'asin',
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'asin'],
'argumentCount' => '1',
],
'ASINH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'asinh',
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'asinh'],
'argumentCount' => '1',
],
'ATAN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'atan',
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atan'],
'argumentCount' => '1',
],
'ATAN2' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ATAN2'],
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atan2'],
'argumentCount' => '2',
],
'ATANH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'atanh',
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atanh'],
'argumentCount' => '1',
],
'AVEDEV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVEDEV'],
+ 'functionCall' => [Statistical\Averages::class, 'averageDeviations'],
'argumentCount' => '1+',
],
'AVERAGE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGE'],
+ 'functionCall' => [Statistical\Averages::class, 'average'],
'argumentCount' => '1+',
],
'AVERAGEA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGEA'],
+ 'functionCall' => [Statistical\Averages::class, 'averageA'],
'argumentCount' => '1+',
],
'AVERAGEIF' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGEIF'],
+ 'functionCall' => [Statistical\Conditional::class, 'AVERAGEIF'],
'argumentCount' => '2,3',
],
'AVERAGEIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Conditional::class, 'AVERAGEIFS'],
'argumentCount' => '3+',
],
'BAHTTEXT' => [
@@ -358,32 +368,32 @@ class Calculation
],
'BASE' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'BASE'],
+ 'functionCall' => [MathTrig\Base::class, 'evaluate'],
'argumentCount' => '2,3',
],
'BESSELI' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELI'],
+ 'functionCall' => [Engineering\BesselI::class, 'BESSELI'],
'argumentCount' => '2',
],
'BESSELJ' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELJ'],
+ 'functionCall' => [Engineering\BesselJ::class, 'BESSELJ'],
'argumentCount' => '2',
],
'BESSELK' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELK'],
+ 'functionCall' => [Engineering\BesselK::class, 'BESSELK'],
'argumentCount' => '2',
],
'BESSELY' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELY'],
+ 'functionCall' => [Engineering\BesselY::class, 'BESSELY'],
'argumentCount' => '2',
],
'BETADIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BETADIST'],
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'distribution'],
'argumentCount' => '3-5',
],
'BETA.DIST' => [
@@ -393,88 +403,88 @@ class Calculation
],
'BETAINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BETAINV'],
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'inverse'],
'argumentCount' => '3-5',
],
'BETA.INV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BETAINV'],
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'inverse'],
'argumentCount' => '3-5',
],
'BIN2DEC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTODEC'],
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toDecimal'],
'argumentCount' => '1',
],
'BIN2HEX' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTOHEX'],
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toHex'],
'argumentCount' => '1,2',
],
'BIN2OCT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTOOCT'],
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toOctal'],
'argumentCount' => '1,2',
],
'BINOMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BINOMDIST'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'distribution'],
'argumentCount' => '4',
],
'BINOM.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BINOMDIST'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'distribution'],
'argumentCount' => '4',
],
'BINOM.DIST.RANGE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'range'],
'argumentCount' => '3,4',
],
'BINOM.INV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'inverse'],
'argumentCount' => '3',
],
'BITAND' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITAND'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITAND'],
'argumentCount' => '2',
],
'BITOR' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITOR'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITOR'],
'argumentCount' => '2',
],
'BITXOR' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITOR'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITXOR'],
'argumentCount' => '2',
],
'BITLSHIFT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITLSHIFT'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITLSHIFT'],
'argumentCount' => '2',
],
'BITRSHIFT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITRSHIFT'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITRSHIFT'],
'argumentCount' => '2',
],
'CEILING' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CEILING'],
- 'argumentCount' => '2',
+ 'functionCall' => [MathTrig\Ceiling::class, 'ceiling'],
+ 'argumentCount' => '1-2', // 2 for Excel, 1-2 for Ods/Gnumeric
],
'CEILING.MATH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
+ 'functionCall' => [MathTrig\Ceiling::class, 'math'],
+ 'argumentCount' => '1-3',
],
'CEILING.PRECISE' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
+ 'functionCall' => [MathTrig\Ceiling::class, 'precise'],
+ 'argumentCount' => '1,2',
],
'CELL' => [
'category' => Category::CATEGORY_INFORMATION,
@@ -483,108 +493,109 @@ class Calculation
],
'CHAR' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CHARACTER'],
+ 'functionCall' => [TextData\CharacterConvert::class, 'character'],
'argumentCount' => '1',
],
'CHIDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIDIST'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
'argumentCount' => '2',
],
'CHISQ.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionLeftTail'],
'argumentCount' => '3',
],
'CHISQ.DIST.RT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIDIST'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
'argumentCount' => '2',
],
'CHIINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIINV'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
'argumentCount' => '2',
],
'CHISQ.INV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseLeftTail'],
'argumentCount' => '2',
],
'CHISQ.INV.RT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIINV'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
'argumentCount' => '2',
],
'CHITEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'test'],
'argumentCount' => '2',
],
'CHISQ.TEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'test'],
'argumentCount' => '2',
],
'CHOOSE' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'CHOOSE'],
+ 'functionCall' => [LookupRef\Selection::class, 'CHOOSE'],
'argumentCount' => '2+',
],
'CLEAN' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TRIMNONPRINTABLE'],
+ 'functionCall' => [TextData\Trim::class, 'nonPrintable'],
'argumentCount' => '1',
],
'CODE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'ASCIICODE'],
+ 'functionCall' => [TextData\CharacterConvert::class, 'code'],
'argumentCount' => '1',
],
'COLUMN' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'COLUMN'],
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'COLUMN'],
'argumentCount' => '-1',
+ 'passCellReference' => true,
'passByReference' => [true],
],
'COLUMNS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'COLUMNS'],
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'COLUMNS'],
'argumentCount' => '1',
],
'COMBIN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COMBIN'],
+ 'functionCall' => [MathTrig\Combinations::class, 'withoutRepetition'],
'argumentCount' => '2',
],
'COMBINA' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [MathTrig\Combinations::class, 'withRepetition'],
'argumentCount' => '2',
],
'COMPLEX' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'COMPLEX'],
+ 'functionCall' => [Engineering\Complex::class, 'COMPLEX'],
'argumentCount' => '2,3',
],
'CONCAT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CONCATENATE'],
+ 'functionCall' => [TextData\Concatenate::class, 'CONCATENATE'],
'argumentCount' => '1+',
],
'CONCATENATE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CONCATENATE'],
+ 'functionCall' => [TextData\Concatenate::class, 'CONCATENATE'],
'argumentCount' => '1+',
],
'CONFIDENCE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CONFIDENCE'],
+ 'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'],
'argumentCount' => '3',
],
'CONFIDENCE.NORM' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CONFIDENCE'],
+ 'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'],
'argumentCount' => '3',
],
'CONFIDENCE.T' => [
@@ -594,97 +605,97 @@ class Calculation
],
'CONVERT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'CONVERTUOM'],
+ 'functionCall' => [Engineering\ConvertUOM::class, 'CONVERT'],
'argumentCount' => '3',
],
'CORREL' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CORREL'],
+ 'functionCall' => [Statistical\Trends::class, 'CORREL'],
'argumentCount' => '2',
],
'COS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'cos',
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'cos'],
'argumentCount' => '1',
],
'COSH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'cosh',
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'cosh'],
'argumentCount' => '1',
],
'COT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COT'],
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'cot'],
'argumentCount' => '1',
],
'COTH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COTH'],
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'coth'],
'argumentCount' => '1',
],
'COUNT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNT'],
+ 'functionCall' => [Statistical\Counts::class, 'COUNT'],
'argumentCount' => '1+',
],
'COUNTA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTA'],
+ 'functionCall' => [Statistical\Counts::class, 'COUNTA'],
'argumentCount' => '1+',
],
'COUNTBLANK' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTBLANK'],
+ 'functionCall' => [Statistical\Counts::class, 'COUNTBLANK'],
'argumentCount' => '1',
],
'COUNTIF' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTIF'],
+ 'functionCall' => [Statistical\Conditional::class, 'COUNTIF'],
'argumentCount' => '2',
],
'COUNTIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTIFS'],
+ 'functionCall' => [Statistical\Conditional::class, 'COUNTIFS'],
'argumentCount' => '2+',
],
'COUPDAYBS' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYBS'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYBS'],
'argumentCount' => '3,4',
],
'COUPDAYS' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYS'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYS'],
'argumentCount' => '3,4',
],
'COUPDAYSNC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYSNC'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYSNC'],
'argumentCount' => '3,4',
],
'COUPNCD' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPNCD'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPNCD'],
'argumentCount' => '3,4',
],
'COUPNUM' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPNUM'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPNUM'],
'argumentCount' => '3,4',
],
'COUPPCD' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPPCD'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPPCD'],
'argumentCount' => '3,4',
],
'COVAR' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COVAR'],
+ 'functionCall' => [Statistical\Trends::class, 'COVAR'],
'argumentCount' => '2',
],
'COVARIANCE.P' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COVAR'],
+ 'functionCall' => [Statistical\Trends::class, 'COVAR'],
'argumentCount' => '2',
],
'COVARIANCE.S' => [
@@ -694,17 +705,17 @@ class Calculation
],
'CRITBINOM' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CRITBINOM'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'inverse'],
'argumentCount' => '3',
],
'CSC' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CSC'],
+ 'functionCall' => [MathTrig\Trig\Cosecant::class, 'csc'],
'argumentCount' => '1',
],
'CSCH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CSCH'],
+ 'functionCall' => [MathTrig\Trig\Cosecant::class, 'csch'],
'argumentCount' => '1',
],
'CUBEKPIMEMBER' => [
@@ -744,52 +755,57 @@ class Calculation
],
'CUMIPMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'CUMIPMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Cumulative::class, 'interest'],
'argumentCount' => '6',
],
'CUMPRINC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'CUMPRINC'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Cumulative::class, 'principal'],
'argumentCount' => '6',
],
'DATE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATE'],
+ 'functionCall' => [DateTimeExcel\Date::class, 'fromYMD'],
'argumentCount' => '3',
],
'DATEDIF' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATEDIF'],
+ 'functionCall' => [DateTimeExcel\Difference::class, 'interval'],
'argumentCount' => '2,3',
],
+ 'DATESTRING' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'DATEVALUE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATEVALUE'],
+ 'functionCall' => [DateTimeExcel\DateValue::class, 'fromString'],
'argumentCount' => '1',
],
'DAVERAGE' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DAVERAGE'],
+ 'functionCall' => [Database\DAverage::class, 'evaluate'],
'argumentCount' => '3',
],
'DAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYOFMONTH'],
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'day'],
'argumentCount' => '1',
],
'DAYS' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYS'],
+ 'functionCall' => [DateTimeExcel\Days::class, 'between'],
'argumentCount' => '2',
],
'DAYS360' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYS360'],
+ 'functionCall' => [DateTimeExcel\Days360::class, 'between'],
'argumentCount' => '2,3',
],
'DB' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DB'],
+ 'functionCall' => [Financial\Depreciation::class, 'DB'],
'argumentCount' => '4,5',
],
'DBCS' => [
@@ -799,32 +815,32 @@ class Calculation
],
'DCOUNT' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DCOUNT'],
+ 'functionCall' => [Database\DCount::class, 'evaluate'],
'argumentCount' => '3',
],
'DCOUNTA' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DCOUNTA'],
+ 'functionCall' => [Database\DCountA::class, 'evaluate'],
'argumentCount' => '3',
],
'DDB' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DDB'],
+ 'functionCall' => [Financial\Depreciation::class, 'DDB'],
'argumentCount' => '4,5',
],
'DEC2BIN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOBIN'],
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toBinary'],
'argumentCount' => '1,2',
],
'DEC2HEX' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOHEX'],
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toHex'],
'argumentCount' => '1,2',
],
'DEC2OCT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOOCT'],
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toOctal'],
'argumentCount' => '1,2',
],
'DECIMAL' => [
@@ -834,72 +850,72 @@ class Calculation
],
'DEGREES' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'rad2deg',
+ 'functionCall' => [MathTrig\Angle::class, 'toDegrees'],
'argumentCount' => '1',
],
'DELTA' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DELTA'],
+ 'functionCall' => [Engineering\Compare::class, 'DELTA'],
'argumentCount' => '1,2',
],
'DEVSQ' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'DEVSQ'],
+ 'functionCall' => [Statistical\Deviations::class, 'sumSquares'],
'argumentCount' => '1+',
],
'DGET' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DGET'],
+ 'functionCall' => [Database\DGet::class, 'evaluate'],
'argumentCount' => '3',
],
'DISC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DISC'],
+ 'functionCall' => [Financial\Securities\Rates::class, 'discount'],
'argumentCount' => '4,5',
],
'DMAX' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DMAX'],
+ 'functionCall' => [Database\DMax::class, 'evaluate'],
'argumentCount' => '3',
],
'DMIN' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DMIN'],
+ 'functionCall' => [Database\DMin::class, 'evaluate'],
'argumentCount' => '3',
],
'DOLLAR' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'DOLLAR'],
+ 'functionCall' => [TextData\Format::class, 'DOLLAR'],
'argumentCount' => '1,2',
],
'DOLLARDE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DOLLARDE'],
+ 'functionCall' => [Financial\Dollar::class, 'decimal'],
'argumentCount' => '2',
],
'DOLLARFR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DOLLARFR'],
+ 'functionCall' => [Financial\Dollar::class, 'fractional'],
'argumentCount' => '2',
],
'DPRODUCT' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DPRODUCT'],
+ 'functionCall' => [Database\DProduct::class, 'evaluate'],
'argumentCount' => '3',
],
'DSTDEV' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSTDEV'],
+ 'functionCall' => [Database\DStDev::class, 'evaluate'],
'argumentCount' => '3',
],
'DSTDEVP' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSTDEVP'],
+ 'functionCall' => [Database\DStDevP::class, 'evaluate'],
'argumentCount' => '3',
],
'DSUM' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSUM'],
+ 'functionCall' => [Database\DSum::class, 'evaluate'],
'argumentCount' => '3',
],
'DURATION' => [
@@ -909,52 +925,57 @@ class Calculation
],
'DVAR' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DVAR'],
+ 'functionCall' => [Database\DVar::class, 'evaluate'],
'argumentCount' => '3',
],
'DVARP' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DVARP'],
+ 'functionCall' => [Database\DVarP::class, 'evaluate'],
'argumentCount' => '3',
],
+ 'ECMA.CEILING' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1,2',
+ ],
'EDATE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'EDATE'],
+ 'functionCall' => [DateTimeExcel\Month::class, 'adjust'],
'argumentCount' => '2',
],
'EFFECT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'EFFECT'],
+ 'functionCall' => [Financial\InterestRate::class, 'effective'],
'argumentCount' => '2',
],
'ENCODEURL' => [
'category' => Category::CATEGORY_WEB,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Web\Service::class, 'urlEncode'],
'argumentCount' => '1',
],
'EOMONTH' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'EOMONTH'],
+ 'functionCall' => [DateTimeExcel\Month::class, 'lastDay'],
'argumentCount' => '2',
],
'ERF' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERF'],
+ 'functionCall' => [Engineering\Erf::class, 'ERF'],
'argumentCount' => '1,2',
],
'ERF.PRECISE' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFPRECISE'],
+ 'functionCall' => [Engineering\Erf::class, 'ERFPRECISE'],
'argumentCount' => '1',
],
'ERFC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFC'],
+ 'functionCall' => [Engineering\ErfC::class, 'ERFC'],
'argumentCount' => '1',
],
'ERFC.PRECISE' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFC'],
+ 'functionCall' => [Engineering\ErfC::class, 'ERFC'],
'argumentCount' => '1',
],
'ERROR.TYPE' => [
@@ -964,42 +985,42 @@ class Calculation
],
'EVEN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'EVEN'],
+ 'functionCall' => [MathTrig\Round::class, 'even'],
'argumentCount' => '1',
],
'EXACT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'EXACT'],
+ 'functionCall' => [TextData\Text::class, 'exact'],
'argumentCount' => '2',
],
'EXP' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'exp',
+ 'functionCall' => [MathTrig\Exp::class, 'evaluate'],
'argumentCount' => '1',
],
'EXPONDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'EXPONDIST'],
+ 'functionCall' => [Statistical\Distributions\Exponential::class, 'distribution'],
'argumentCount' => '3',
],
'EXPON.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'EXPONDIST'],
+ 'functionCall' => [Statistical\Distributions\Exponential::class, 'distribution'],
'argumentCount' => '3',
],
'FACT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FACT'],
+ 'functionCall' => [MathTrig\Factorial::class, 'fact'],
'argumentCount' => '1',
],
'FACTDOUBLE' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FACTDOUBLE'],
+ 'functionCall' => [MathTrig\Factorial::class, 'factDouble'],
'argumentCount' => '1',
],
'FALSE' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'FALSE'],
+ 'functionCall' => [Logical\Boolean::class, 'FALSE'],
'argumentCount' => '0',
],
'FDIST' => [
@@ -1009,7 +1030,7 @@ class Calculation
],
'F.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FDIST2'],
+ 'functionCall' => [Statistical\Distributions\F::class, 'distribution'],
'argumentCount' => '4',
],
'F.DIST.RT' => [
@@ -1029,12 +1050,12 @@ class Calculation
],
'FIND' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'],
+ 'functionCall' => [TextData\Search::class, 'sensitive'],
'argumentCount' => '2,3',
],
'FINDB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'],
+ 'functionCall' => [TextData\Search::class, 'sensitive'],
'argumentCount' => '2,3',
],
'FINV' => [
@@ -1054,37 +1075,37 @@ class Calculation
],
'FISHER' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FISHER'],
+ 'functionCall' => [Statistical\Distributions\Fisher::class, 'distribution'],
'argumentCount' => '1',
],
'FISHERINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FISHERINV'],
+ 'functionCall' => [Statistical\Distributions\Fisher::class, 'inverse'],
'argumentCount' => '1',
],
'FIXED' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'FIXEDFORMAT'],
+ 'functionCall' => [TextData\Format::class, 'FIXEDFORMAT'],
'argumentCount' => '1-3',
],
'FLOOR' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FLOOR'],
- 'argumentCount' => '2',
+ 'functionCall' => [MathTrig\Floor::class, 'floor'],
+ 'argumentCount' => '1-2', // Excel requries 2, Ods/Gnumeric 1-2
],
'FLOOR.MATH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FLOORMATH'],
- 'argumentCount' => '3',
+ 'functionCall' => [MathTrig\Floor::class, 'math'],
+ 'argumentCount' => '1-3',
],
'FLOOR.PRECISE' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FLOORPRECISE'],
- 'argumentCount' => '2',
+ 'functionCall' => [MathTrig\Floor::class, 'precise'],
+ 'argumentCount' => '1-2',
],
'FORECAST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FORECAST'],
+ 'functionCall' => [Statistical\Trends::class, 'FORECAST'],
'argumentCount' => '3',
],
'FORECAST.ETS' => [
@@ -1109,12 +1130,12 @@ class Calculation
],
'FORECAST.LINEAR' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FORECAST'],
+ 'functionCall' => [Statistical\Trends::class, 'FORECAST'],
'argumentCount' => '3',
],
'FORMULATEXT' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'FORMULATEXT'],
+ 'functionCall' => [LookupRef\Formula::class, 'text'],
'argumentCount' => '1',
'passCellReference' => true,
'passByReference' => [true],
@@ -1136,67 +1157,67 @@ class Calculation
],
'FV' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'FV'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'futureValue'],
'argumentCount' => '3-5',
],
'FVSCHEDULE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'FVSCHEDULE'],
+ 'functionCall' => [Financial\CashFlow\Single::class, 'futureValue'],
'argumentCount' => '2',
],
'GAMMA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMAFunction'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'gamma'],
'argumentCount' => '1',
],
'GAMMADIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMADIST'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'distribution'],
'argumentCount' => '4',
],
'GAMMA.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMADIST'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'distribution'],
'argumentCount' => '4',
],
'GAMMAINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMAINV'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'inverse'],
'argumentCount' => '3',
],
'GAMMA.INV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMAINV'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'inverse'],
'argumentCount' => '3',
],
'GAMMALN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMALN'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'ln'],
'argumentCount' => '1',
],
'GAMMALN.PRECISE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMALN'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'ln'],
'argumentCount' => '1',
],
'GAUSS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAUSS'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'gauss'],
'argumentCount' => '1',
],
'GCD' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'GCD'],
+ 'functionCall' => [MathTrig\Gcd::class, 'evaluate'],
'argumentCount' => '1+',
],
'GEOMEAN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GEOMEAN'],
+ 'functionCall' => [Statistical\Averages\Mean::class, 'geometric'],
'argumentCount' => '1+',
],
'GESTEP' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'GESTEP'],
+ 'functionCall' => [Engineering\Compare::class, 'GESTEP'],
'argumentCount' => '1,2',
],
'GETPIVOTDATA' => [
@@ -1206,48 +1227,48 @@ class Calculation
],
'GROWTH' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GROWTH'],
+ 'functionCall' => [Statistical\Trends::class, 'GROWTH'],
'argumentCount' => '1-4',
],
'HARMEAN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'HARMEAN'],
+ 'functionCall' => [Statistical\Averages\Mean::class, 'harmonic'],
'argumentCount' => '1+',
],
'HEX2BIN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTOBIN'],
+ 'functionCall' => [Engineering\ConvertHex::class, 'toBinary'],
'argumentCount' => '1,2',
],
'HEX2DEC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTODEC'],
+ 'functionCall' => [Engineering\ConvertHex::class, 'toDecimal'],
'argumentCount' => '1',
],
'HEX2OCT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTOOCT'],
+ 'functionCall' => [Engineering\ConvertHex::class, 'toOctal'],
'argumentCount' => '1,2',
],
'HLOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'HLOOKUP'],
+ 'functionCall' => [LookupRef\HLookup::class, 'lookup'],
'argumentCount' => '3,4',
],
'HOUR' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'HOUROFDAY'],
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'hour'],
'argumentCount' => '1',
],
'HYPERLINK' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'HYPERLINK'],
+ 'functionCall' => [LookupRef\Hyperlink::class, 'set'],
'argumentCount' => '1,2',
'passCellReference' => true,
],
'HYPGEOMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'HYPGEOMDIST'],
+ 'functionCall' => [Statistical\Distributions\HyperGeometric::class, 'distribution'],
'argumentCount' => '4',
],
'HYPGEOM.DIST' => [
@@ -1257,157 +1278,157 @@ class Calculation
],
'IF' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'statementIf'],
+ 'functionCall' => [Logical\Conditional::class, 'statementIf'],
'argumentCount' => '1-3',
],
'IFERROR' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'IFERROR'],
+ 'functionCall' => [Logical\Conditional::class, 'IFERROR'],
'argumentCount' => '2',
],
'IFNA' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'IFNA'],
+ 'functionCall' => [Logical\Conditional::class, 'IFNA'],
'argumentCount' => '2',
],
'IFS' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'IFS'],
+ 'functionCall' => [Logical\Conditional::class, 'IFS'],
'argumentCount' => '2+',
],
'IMABS' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMABS'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMABS'],
'argumentCount' => '1',
],
'IMAGINARY' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMAGINARY'],
+ 'functionCall' => [Engineering\Complex::class, 'IMAGINARY'],
'argumentCount' => '1',
],
'IMARGUMENT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMARGUMENT'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMARGUMENT'],
'argumentCount' => '1',
],
'IMCONJUGATE' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCONJUGATE'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCONJUGATE'],
'argumentCount' => '1',
],
'IMCOS' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOS'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOS'],
'argumentCount' => '1',
],
'IMCOSH' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOSH'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOSH'],
'argumentCount' => '1',
],
'IMCOT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOT'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOT'],
'argumentCount' => '1',
],
'IMCSC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCSC'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCSC'],
'argumentCount' => '1',
],
'IMCSCH' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCSCH'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCSCH'],
'argumentCount' => '1',
],
'IMDIV' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMDIV'],
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMDIV'],
'argumentCount' => '2',
],
'IMEXP' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMEXP'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMEXP'],
'argumentCount' => '1',
],
'IMLN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLN'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLN'],
'argumentCount' => '1',
],
'IMLOG10' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLOG10'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLOG10'],
'argumentCount' => '1',
],
'IMLOG2' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLOG2'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLOG2'],
'argumentCount' => '1',
],
'IMPOWER' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMPOWER'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMPOWER'],
'argumentCount' => '2',
],
'IMPRODUCT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMPRODUCT'],
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMPRODUCT'],
'argumentCount' => '1+',
],
'IMREAL' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMREAL'],
+ 'functionCall' => [Engineering\Complex::class, 'IMREAL'],
'argumentCount' => '1',
],
'IMSEC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSEC'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSEC'],
'argumentCount' => '1',
],
'IMSECH' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSECH'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSECH'],
'argumentCount' => '1',
],
'IMSIN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSIN'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSIN'],
'argumentCount' => '1',
],
'IMSINH' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSINH'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSINH'],
'argumentCount' => '1',
],
'IMSQRT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSQRT'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSQRT'],
'argumentCount' => '1',
],
'IMSUB' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSUB'],
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMSUB'],
'argumentCount' => '2',
],
'IMSUM' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSUM'],
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMSUM'],
'argumentCount' => '1+',
],
'IMTAN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMTAN'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMTAN'],
'argumentCount' => '1',
],
'INDEX' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'INDEX'],
+ 'functionCall' => [LookupRef\Matrix::class, 'index'],
'argumentCount' => '1-4',
],
'INDIRECT' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'INDIRECT'],
+ 'functionCall' => [LookupRef\Indirect::class, 'INDIRECT'],
'argumentCount' => '1,2',
'passCellReference' => true,
],
@@ -1418,27 +1439,27 @@ class Calculation
],
'INT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'INT'],
+ 'functionCall' => [MathTrig\IntClass::class, 'evaluate'],
'argumentCount' => '1',
],
'INTERCEPT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'INTERCEPT'],
+ 'functionCall' => [Statistical\Trends::class, 'INTERCEPT'],
'argumentCount' => '2',
],
'INTRATE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'INTRATE'],
+ 'functionCall' => [Financial\Securities\Rates::class, 'interest'],
'argumentCount' => '4,5',
],
'IPMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'IPMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'payment'],
'argumentCount' => '4-6',
],
'IRR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'IRR'],
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'rate'],
'argumentCount' => '1,2',
],
'ISBLANK' => [
@@ -1500,12 +1521,12 @@ class Calculation
],
'ISOWEEKNUM' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'ISOWEEKNUM'],
+ 'functionCall' => [DateTimeExcel\Week::class, 'isoWeekNumber'],
'argumentCount' => '1',
],
'ISPMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ISPMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'schedulePayment'],
'argumentCount' => '4',
],
'ISREF' => [
@@ -1518,6 +1539,11 @@ class Calculation
'functionCall' => [Functions::class, 'isText'],
'argumentCount' => '1',
],
+ 'ISTHAIDIGIT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'JIS' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
'functionCall' => [Functions::class, 'DUMMY'],
@@ -1525,117 +1551,117 @@ class Calculation
],
'KURT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'KURT'],
+ 'functionCall' => [Statistical\Deviations::class, 'kurtosis'],
'argumentCount' => '1+',
],
'LARGE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LARGE'],
+ 'functionCall' => [Statistical\Size::class, 'large'],
'argumentCount' => '2',
],
'LCM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'LCM'],
+ 'functionCall' => [MathTrig\Lcm::class, 'evaluate'],
'argumentCount' => '1+',
],
'LEFT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LEFT'],
+ 'functionCall' => [TextData\Extract::class, 'left'],
'argumentCount' => '1,2',
],
'LEFTB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LEFT'],
+ 'functionCall' => [TextData\Extract::class, 'left'],
'argumentCount' => '1,2',
],
'LEN' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'STRINGLENGTH'],
+ 'functionCall' => [TextData\Text::class, 'length'],
'argumentCount' => '1',
],
'LENB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'STRINGLENGTH'],
+ 'functionCall' => [TextData\Text::class, 'length'],
'argumentCount' => '1',
],
'LINEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LINEST'],
+ 'functionCall' => [Statistical\Trends::class, 'LINEST'],
'argumentCount' => '1-4',
],
'LN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'log',
+ 'functionCall' => [MathTrig\Logarithms::class, 'natural'],
'argumentCount' => '1',
],
'LOG' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'logBase'],
+ 'functionCall' => [MathTrig\Logarithms::class, 'withBase'],
'argumentCount' => '1,2',
],
'LOG10' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'log10',
+ 'functionCall' => [MathTrig\Logarithms::class, 'base10'],
'argumentCount' => '1',
],
'LOGEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGEST'],
+ 'functionCall' => [Statistical\Trends::class, 'LOGEST'],
'argumentCount' => '1-4',
],
'LOGINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGINV'],
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'inverse'],
'argumentCount' => '3',
],
'LOGNORMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGNORMDIST'],
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'cumulative'],
'argumentCount' => '3',
],
'LOGNORM.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGNORMDIST2'],
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'distribution'],
'argumentCount' => '4',
],
'LOGNORM.INV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGINV'],
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'inverse'],
'argumentCount' => '3',
],
'LOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'LOOKUP'],
+ 'functionCall' => [LookupRef\Lookup::class, 'lookup'],
'argumentCount' => '2,3',
],
'LOWER' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LOWERCASE'],
+ 'functionCall' => [TextData\CaseConvert::class, 'lower'],
'argumentCount' => '1',
],
'MATCH' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'MATCH'],
+ 'functionCall' => [LookupRef\ExcelMatch::class, 'MATCH'],
'argumentCount' => '2,3',
],
'MAX' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAX'],
+ 'functionCall' => [Statistical\Maximum::class, 'max'],
'argumentCount' => '1+',
],
'MAXA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAXA'],
+ 'functionCall' => [Statistical\Maximum::class, 'maxA'],
'argumentCount' => '1+',
],
'MAXIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAXIFS'],
+ 'functionCall' => [Statistical\Conditional::class, 'MAXIFS'],
'argumentCount' => '3+',
],
'MDETERM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MDETERM'],
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'determinant'],
'argumentCount' => '1',
],
'MDURATION' => [
@@ -1645,7 +1671,7 @@ class Calculation
],
'MEDIAN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MEDIAN'],
+ 'functionCall' => [Statistical\Averages::class, 'median'],
'argumentCount' => '1+',
],
'MEDIANIF' => [
@@ -1655,57 +1681,57 @@ class Calculation
],
'MID' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'MID'],
+ 'functionCall' => [TextData\Extract::class, 'mid'],
'argumentCount' => '3',
],
'MIDB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'MID'],
+ 'functionCall' => [TextData\Extract::class, 'mid'],
'argumentCount' => '3',
],
'MIN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MIN'],
+ 'functionCall' => [Statistical\Minimum::class, 'min'],
'argumentCount' => '1+',
],
'MINA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MINA'],
+ 'functionCall' => [Statistical\Minimum::class, 'minA'],
'argumentCount' => '1+',
],
'MINIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MINIFS'],
+ 'functionCall' => [Statistical\Conditional::class, 'MINIFS'],
'argumentCount' => '3+',
],
'MINUTE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'MINUTE'],
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'minute'],
'argumentCount' => '1',
],
'MINVERSE' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MINVERSE'],
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'inverse'],
'argumentCount' => '1',
],
'MIRR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'MIRR'],
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'modifiedRate'],
'argumentCount' => '3',
],
'MMULT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MMULT'],
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'multiply'],
'argumentCount' => '2',
],
'MOD' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MOD'],
+ 'functionCall' => [MathTrig\Operations::class, 'mod'],
'argumentCount' => '2',
],
'MODE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MODE'],
+ 'functionCall' => [Statistical\Averages::class, 'mode'],
'argumentCount' => '1+',
],
'MODE.MULT' => [
@@ -1715,27 +1741,27 @@ class Calculation
],
'MODE.SNGL' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MODE'],
+ 'functionCall' => [Statistical\Averages::class, 'mode'],
'argumentCount' => '1+',
],
'MONTH' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'MONTHOFYEAR'],
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'month'],
'argumentCount' => '1',
],
'MROUND' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MROUND'],
+ 'functionCall' => [MathTrig\Round::class, 'multiple'],
'argumentCount' => '2',
],
'MULTINOMIAL' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MULTINOMIAL'],
+ 'functionCall' => [MathTrig\Factorial::class, 'multinomial'],
'argumentCount' => '1+',
],
'MUNIT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'identity'],
'argumentCount' => '1',
],
'N' => [
@@ -1750,7 +1776,7 @@ class Calculation
],
'NEGBINOMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NEGBINOMDIST'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'negative'],
'argumentCount' => '3',
],
'NEGBINOM.DIST' => [
@@ -1760,7 +1786,7 @@ class Calculation
],
'NETWORKDAYS' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'NETWORKDAYS'],
+ 'functionCall' => [DateTimeExcel\NetworkDays::class, 'count'],
'argumentCount' => '2-3',
],
'NETWORKDAYS.INTL' => [
@@ -1770,92 +1796,97 @@ class Calculation
],
'NOMINAL' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NOMINAL'],
+ 'functionCall' => [Financial\InterestRate::class, 'nominal'],
'argumentCount' => '2',
],
'NORMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMDIST'],
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'distribution'],
'argumentCount' => '4',
],
'NORM.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMDIST'],
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'distribution'],
'argumentCount' => '4',
],
'NORMINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMINV'],
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'inverse'],
'argumentCount' => '3',
],
'NORM.INV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMINV'],
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'inverse'],
'argumentCount' => '3',
],
'NORMSDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSDIST'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'cumulative'],
'argumentCount' => '1',
],
'NORM.S.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSDIST2'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'distribution'],
'argumentCount' => '1,2',
],
'NORMSINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSINV'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'inverse'],
'argumentCount' => '1',
],
'NORM.S.INV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSINV'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'inverse'],
'argumentCount' => '1',
],
'NOT' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'NOT'],
+ 'functionCall' => [Logical\Operations::class, 'NOT'],
'argumentCount' => '1',
],
'NOW' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATETIMENOW'],
+ 'functionCall' => [DateTimeExcel\Current::class, 'now'],
'argumentCount' => '0',
],
'NPER' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NPER'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'periods'],
'argumentCount' => '3-5',
],
'NPV' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NPV'],
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'presentValue'],
'argumentCount' => '2+',
],
+ 'NUMBERSTRING' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'NUMBERVALUE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'NUMBERVALUE'],
+ 'functionCall' => [TextData\Format::class, 'NUMBERVALUE'],
'argumentCount' => '1+',
],
'OCT2BIN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTOBIN'],
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toBinary'],
'argumentCount' => '1,2',
],
'OCT2DEC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTODEC'],
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toDecimal'],
'argumentCount' => '1',
],
'OCT2HEX' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTOHEX'],
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toHex'],
'argumentCount' => '1,2',
],
'ODD' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ODD'],
+ 'functionCall' => [MathTrig\Round::class, 'odd'],
'argumentCount' => '1',
],
'ODDFPRICE' => [
@@ -1880,29 +1911,29 @@ class Calculation
],
'OFFSET' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'OFFSET'],
+ 'functionCall' => [LookupRef\Offset::class, 'OFFSET'],
'argumentCount' => '3-5',
'passCellReference' => true,
'passByReference' => [true],
],
'OR' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalOr'],
+ 'functionCall' => [Logical\Operations::class, 'logicalOr'],
'argumentCount' => '1+',
],
'PDURATION' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PDURATION'],
+ 'functionCall' => [Financial\CashFlow\Single::class, 'periods'],
'argumentCount' => '3',
],
'PEARSON' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CORREL'],
+ 'functionCall' => [Statistical\Trends::class, 'CORREL'],
'argumentCount' => '2',
],
'PERCENTILE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTILE'],
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTILE'],
'argumentCount' => '2',
],
'PERCENTILE.EXC' => [
@@ -1912,12 +1943,12 @@ class Calculation
],
'PERCENTILE.INC' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTILE'],
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTILE'],
'argumentCount' => '2',
],
'PERCENTRANK' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTRANK'],
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTRANK'],
'argumentCount' => '2,3',
],
'PERCENTRANK.EXC' => [
@@ -1927,17 +1958,17 @@ class Calculation
],
'PERCENTRANK.INC' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTRANK'],
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTRANK'],
'argumentCount' => '2,3',
],
'PERMUT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERMUT'],
+ 'functionCall' => [Statistical\Permutations::class, 'PERMUT'],
'argumentCount' => '2',
],
'PERMUTATIONA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Permutations::class, 'PERMUTATIONA'],
'argumentCount' => '2',
],
'PHONETIC' => [
@@ -1957,42 +1988,42 @@ class Calculation
],
'PMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Payments::class, 'annuity'],
'argumentCount' => '3-5',
],
'POISSON' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'POISSON'],
+ 'functionCall' => [Statistical\Distributions\Poisson::class, 'distribution'],
'argumentCount' => '3',
],
'POISSON.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'POISSON'],
+ 'functionCall' => [Statistical\Distributions\Poisson::class, 'distribution'],
'argumentCount' => '3',
],
'POWER' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'POWER'],
+ 'functionCall' => [MathTrig\Operations::class, 'power'],
'argumentCount' => '2',
],
'PPMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PPMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Payments::class, 'interestPayment'],
'argumentCount' => '4-6',
],
'PRICE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICE'],
+ 'functionCall' => [Financial\Securities\Price::class, 'price'],
'argumentCount' => '6,7',
],
'PRICEDISC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICEDISC'],
+ 'functionCall' => [Financial\Securities\Price::class, 'priceDiscounted'],
'argumentCount' => '4,5',
],
'PRICEMAT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICEMAT'],
+ 'functionCall' => [Financial\Securities\Price::class, 'priceAtMaturity'],
'argumentCount' => '5,6',
],
'PROB' => [
@@ -2002,22 +2033,22 @@ class Calculation
],
'PRODUCT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'PRODUCT'],
+ 'functionCall' => [MathTrig\Operations::class, 'product'],
'argumentCount' => '1+',
],
'PROPER' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'PROPERCASE'],
+ 'functionCall' => [TextData\CaseConvert::class, 'proper'],
'argumentCount' => '1',
],
'PV' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PV'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'presentValue'],
'argumentCount' => '3-5',
],
'QUARTILE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'QUARTILE'],
+ 'functionCall' => [Statistical\Percentiles::class, 'QUARTILE'],
'argumentCount' => '2',
],
'QUARTILE.EXC' => [
@@ -2027,22 +2058,22 @@ class Calculation
],
'QUARTILE.INC' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'QUARTILE'],
+ 'functionCall' => [Statistical\Percentiles::class, 'QUARTILE'],
'argumentCount' => '2',
],
'QUOTIENT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'QUOTIENT'],
+ 'functionCall' => [MathTrig\Operations::class, 'quotient'],
'argumentCount' => '2',
],
'RADIANS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'deg2rad',
+ 'functionCall' => [MathTrig\Angle::class, 'toRadians'],
'argumentCount' => '1',
],
'RAND' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'RAND'],
+ 'functionCall' => [MathTrig\Random::class, 'rand'],
'argumentCount' => '0',
],
'RANDARRAY' => [
@@ -2052,12 +2083,12 @@ class Calculation
],
'RANDBETWEEN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'RAND'],
+ 'functionCall' => [MathTrig\Random::class, 'randBetween'],
'argumentCount' => '2',
],
'RANK' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'RANK'],
+ 'functionCall' => [Statistical\Percentiles::class, 'RANK'],
'argumentCount' => '2,3',
],
'RANK.AVG' => [
@@ -2067,83 +2098,94 @@ class Calculation
],
'RANK.EQ' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'RANK'],
+ 'functionCall' => [Statistical\Percentiles::class, 'RANK'],
'argumentCount' => '2,3',
],
'RATE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RATE'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'rate'],
'argumentCount' => '3-6',
],
'RECEIVED' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RECEIVED'],
+ 'functionCall' => [Financial\Securities\Price::class, 'received'],
'argumentCount' => '4-5',
],
'REPLACE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'REPLACE'],
+ 'functionCall' => [TextData\Replace::class, 'replace'],
'argumentCount' => '4',
],
'REPLACEB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'REPLACE'],
+ 'functionCall' => [TextData\Replace::class, 'replace'],
'argumentCount' => '4',
],
'REPT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => 'str_repeat',
+ 'functionCall' => [TextData\Concatenate::class, 'builtinREPT'],
'argumentCount' => '2',
],
'RIGHT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RIGHT'],
+ 'functionCall' => [TextData\Extract::class, 'right'],
'argumentCount' => '1,2',
],
'RIGHTB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RIGHT'],
+ 'functionCall' => [TextData\Extract::class, 'right'],
'argumentCount' => '1,2',
],
'ROMAN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROMAN'],
+ 'functionCall' => [MathTrig\Roman::class, 'evaluate'],
'argumentCount' => '1,2',
],
'ROUND' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'round',
+ 'functionCall' => [MathTrig\Round::class, 'round'],
'argumentCount' => '2',
],
+ 'ROUNDBAHTDOWN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'ROUNDBAHTUP' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'ROUNDDOWN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROUNDDOWN'],
+ 'functionCall' => [MathTrig\Round::class, 'down'],
'argumentCount' => '2',
],
'ROUNDUP' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROUNDUP'],
+ 'functionCall' => [MathTrig\Round::class, 'up'],
'argumentCount' => '2',
],
'ROW' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'ROW'],
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'ROW'],
'argumentCount' => '-1',
+ 'passCellReference' => true,
'passByReference' => [true],
],
'ROWS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'ROWS'],
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'ROWS'],
'argumentCount' => '1',
],
'RRI' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RRI'],
+ 'functionCall' => [Financial\CashFlow\Single::class, 'interestRate'],
'argumentCount' => '3',
],
'RSQ' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'RSQ'],
+ 'functionCall' => [Statistical\Trends::class, 'RSQ'],
'argumentCount' => '2',
],
'RTD' => [
@@ -2153,27 +2195,27 @@ class Calculation
],
'SEARCH' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHINSENSITIVE'],
+ 'functionCall' => [TextData\Search::class, 'insensitive'],
'argumentCount' => '2,3',
],
'SEARCHB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHINSENSITIVE'],
+ 'functionCall' => [TextData\Search::class, 'insensitive'],
'argumentCount' => '2,3',
],
'SEC' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SEC'],
+ 'functionCall' => [MathTrig\Trig\Secant::class, 'sec'],
'argumentCount' => '1',
],
'SECH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SECH'],
+ 'functionCall' => [MathTrig\Trig\Secant::class, 'sech'],
'argumentCount' => '1',
],
'SECOND' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'SECOND'],
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'second'],
'argumentCount' => '1',
],
'SEQUENCE' => [
@@ -2183,7 +2225,7 @@ class Calculation
],
'SERIESSUM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SERIESSUM'],
+ 'functionCall' => [MathTrig\SeriesSum::class, 'evaluate'],
'argumentCount' => '4',
],
'SHEET' => [
@@ -2198,22 +2240,22 @@ class Calculation
],
'SIGN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SIGN'],
+ 'functionCall' => [MathTrig\Sign::class, 'evaluate'],
'argumentCount' => '1',
],
'SIN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sin',
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'sin'],
'argumentCount' => '1',
],
'SINH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sinh',
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'sinh'],
'argumentCount' => '1',
],
'SKEW' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SKEW'],
+ 'functionCall' => [Statistical\Deviations::class, 'skew'],
'argumentCount' => '1+',
],
'SKEW.P' => [
@@ -2223,17 +2265,17 @@ class Calculation
],
'SLN' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'SLN'],
+ 'functionCall' => [Financial\Depreciation::class, 'SLN'],
'argumentCount' => '3',
],
'SLOPE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SLOPE'],
+ 'functionCall' => [Statistical\Trends::class, 'SLOPE'],
'argumentCount' => '2',
],
'SMALL' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SMALL'],
+ 'functionCall' => [Statistical\Size::class, 'small'],
'argumentCount' => '2',
],
'SORT' => [
@@ -2248,148 +2290,148 @@ class Calculation
],
'SQRT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sqrt',
+ 'functionCall' => [MathTrig\Sqrt::class, 'sqrt'],
'argumentCount' => '1',
],
'SQRTPI' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SQRTPI'],
+ 'functionCall' => [MathTrig\Sqrt::class, 'pi'],
'argumentCount' => '1',
],
'STANDARDIZE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STANDARDIZE'],
+ 'functionCall' => [Statistical\Standardize::class, 'execute'],
'argumentCount' => '3',
],
'STDEV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEV'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEV'],
'argumentCount' => '1+',
],
'STDEV.S' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEV'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEV'],
'argumentCount' => '1+',
],
'STDEV.P' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVP'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVP'],
'argumentCount' => '1+',
],
'STDEVA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVA'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVA'],
'argumentCount' => '1+',
],
'STDEVP' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVP'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVP'],
'argumentCount' => '1+',
],
'STDEVPA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVPA'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVPA'],
'argumentCount' => '1+',
],
'STEYX' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STEYX'],
+ 'functionCall' => [Statistical\Trends::class, 'STEYX'],
'argumentCount' => '2',
],
'SUBSTITUTE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SUBSTITUTE'],
+ 'functionCall' => [TextData\Replace::class, 'substitute'],
'argumentCount' => '3,4',
],
'SUBTOTAL' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUBTOTAL'],
+ 'functionCall' => [MathTrig\Subtotal::class, 'evaluate'],
'argumentCount' => '2+',
'passCellReference' => true,
],
'SUM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUM'],
+ 'functionCall' => [MathTrig\Sum::class, 'sumErroringStrings'],
'argumentCount' => '1+',
],
'SUMIF' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMIF'],
+ 'functionCall' => [Statistical\Conditional::class, 'SUMIF'],
'argumentCount' => '2,3',
],
'SUMIFS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMIFS'],
+ 'functionCall' => [Statistical\Conditional::class, 'SUMIFS'],
'argumentCount' => '3+',
],
'SUMPRODUCT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMPRODUCT'],
+ 'functionCall' => [MathTrig\Sum::class, 'product'],
'argumentCount' => '1+',
],
'SUMSQ' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMSQ'],
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumSquare'],
'argumentCount' => '1+',
],
'SUMX2MY2' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMX2MY2'],
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXSquaredMinusYSquared'],
'argumentCount' => '2',
],
'SUMX2PY2' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMX2PY2'],
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXSquaredPlusYSquared'],
'argumentCount' => '2',
],
'SUMXMY2' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMXMY2'],
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXMinusYSquared'],
'argumentCount' => '2',
],
'SWITCH' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'statementSwitch'],
+ 'functionCall' => [Logical\Conditional::class, 'statementSwitch'],
'argumentCount' => '3+',
],
'SYD' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'SYD'],
+ 'functionCall' => [Financial\Depreciation::class, 'SYD'],
'argumentCount' => '4',
],
'T' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RETURNSTRING'],
+ 'functionCall' => [TextData\Text::class, 'test'],
'argumentCount' => '1',
],
'TAN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'tan',
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'tan'],
'argumentCount' => '1',
],
'TANH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'tanh',
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'tanh'],
'argumentCount' => '1',
],
'TBILLEQ' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLEQ'],
+ 'functionCall' => [Financial\TreasuryBill::class, 'bondEquivalentYield'],
'argumentCount' => '3',
],
'TBILLPRICE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLPRICE'],
+ 'functionCall' => [Financial\TreasuryBill::class, 'price'],
'argumentCount' => '3',
],
'TBILLYIELD' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLYIELD'],
+ 'functionCall' => [Financial\TreasuryBill::class, 'yield'],
'argumentCount' => '3',
],
'TDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TDIST'],
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'distribution'],
'argumentCount' => '3',
],
'T.DIST' => [
@@ -2409,32 +2451,67 @@ class Calculation
],
'TEXT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TEXTFORMAT'],
+ 'functionCall' => [TextData\Format::class, 'TEXTFORMAT'],
'argumentCount' => '2',
],
'TEXTJOIN' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TEXTJOIN'],
+ 'functionCall' => [TextData\Concatenate::class, 'TEXTJOIN'],
'argumentCount' => '3+',
],
+ 'THAIDAYOFWEEK' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIDIGIT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIMONTHOFYEAR' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAINUMSOUND' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAINUMSTRING' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAISTRINGLENGTH' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIYEAR' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'TIME' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'TIME'],
+ 'functionCall' => [DateTimeExcel\Time::class, 'fromHMS'],
'argumentCount' => '3',
],
'TIMEVALUE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'TIMEVALUE'],
+ 'functionCall' => [DateTimeExcel\TimeValue::class, 'fromString'],
'argumentCount' => '1',
],
'TINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TINV'],
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'inverse'],
'argumentCount' => '2',
],
'T.INV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TINV'],
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'inverse'],
'argumentCount' => '2',
],
'T.INV.2T' => [
@@ -2444,37 +2521,37 @@ class Calculation
],
'TODAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATENOW'],
+ 'functionCall' => [DateTimeExcel\Current::class, 'today'],
'argumentCount' => '0',
],
'TRANSPOSE' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'TRANSPOSE'],
+ 'functionCall' => [LookupRef\Matrix::class, 'transpose'],
'argumentCount' => '1',
],
'TREND' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TREND'],
+ 'functionCall' => [Statistical\Trends::class, 'TREND'],
'argumentCount' => '1-4',
],
'TRIM' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TRIMSPACES'],
+ 'functionCall' => [TextData\Trim::class, 'spaces'],
'argumentCount' => '1',
],
'TRIMMEAN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TRIMMEAN'],
+ 'functionCall' => [Statistical\Averages\Mean::class, 'trim'],
'argumentCount' => '2',
],
'TRUE' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'TRUE'],
+ 'functionCall' => [Logical\Boolean::class, 'TRUE'],
'argumentCount' => '0',
],
'TRUNC' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'TRUNC'],
+ 'functionCall' => [MathTrig\Trunc::class, 'evaluate'],
'argumentCount' => '1,2',
],
'TTEST' => [
@@ -2494,12 +2571,12 @@ class Calculation
],
'UNICHAR' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CHARACTER'],
+ 'functionCall' => [TextData\CharacterConvert::class, 'character'],
'argumentCount' => '1',
],
'UNICODE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'ASCIICODE'],
+ 'functionCall' => [TextData\CharacterConvert::class, 'code'],
'argumentCount' => '1',
],
'UNIQUE' => [
@@ -2509,47 +2586,52 @@ class Calculation
],
'UPPER' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'UPPERCASE'],
+ 'functionCall' => [TextData\CaseConvert::class, 'upper'],
'argumentCount' => '1',
],
'USDOLLAR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Financial\Dollar::class, 'format'],
'argumentCount' => '2',
],
'VALUE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'VALUE'],
+ 'functionCall' => [TextData\Format::class, 'VALUE'],
'argumentCount' => '1',
],
+ 'VALUETOTEXT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'VAR' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARFunc'],
+ 'functionCall' => [Statistical\Variances::class, 'VAR'],
'argumentCount' => '1+',
],
'VAR.P' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARP'],
+ 'functionCall' => [Statistical\Variances::class, 'VARP'],
'argumentCount' => '1+',
],
'VAR.S' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARFunc'],
+ 'functionCall' => [Statistical\Variances::class, 'VAR'],
'argumentCount' => '1+',
],
'VARA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARA'],
+ 'functionCall' => [Statistical\Variances::class, 'VARA'],
'argumentCount' => '1+',
],
'VARP' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARP'],
+ 'functionCall' => [Statistical\Variances::class, 'VARP'],
'argumentCount' => '1+',
],
'VARPA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARPA'],
+ 'functionCall' => [Statistical\Variances::class, 'VARPA'],
'argumentCount' => '1+',
],
'VDB' => [
@@ -2559,37 +2641,37 @@ class Calculation
],
'VLOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'VLOOKUP'],
+ 'functionCall' => [LookupRef\VLookup::class, 'lookup'],
'argumentCount' => '3,4',
],
'WEBSERVICE' => [
'category' => Category::CATEGORY_WEB,
- 'functionCall' => [Web::class, 'WEBSERVICE'],
+ 'functionCall' => [Web\Service::class, 'webService'],
'argumentCount' => '1',
],
'WEEKDAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WEEKDAY'],
+ 'functionCall' => [DateTimeExcel\Week::class, 'day'],
'argumentCount' => '1,2',
],
'WEEKNUM' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WEEKNUM'],
+ 'functionCall' => [DateTimeExcel\Week::class, 'number'],
'argumentCount' => '1,2',
],
'WEIBULL' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'WEIBULL'],
+ 'functionCall' => [Statistical\Distributions\Weibull::class, 'distribution'],
'argumentCount' => '4',
],
'WEIBULL.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'WEIBULL'],
+ 'functionCall' => [Statistical\Distributions\Weibull::class, 'distribution'],
'argumentCount' => '4',
],
'WORKDAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WORKDAY'],
+ 'functionCall' => [DateTimeExcel\WorkDay::class, 'date'],
'argumentCount' => '2-3',
],
'WORKDAY.INTL' => [
@@ -2599,7 +2681,7 @@ class Calculation
],
'XIRR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'XIRR'],
+ 'functionCall' => [Financial\CashFlow\Variable\NonPeriodic::class, 'rate'],
'argumentCount' => '2,3',
],
'XLOOKUP' => [
@@ -2609,7 +2691,7 @@ class Calculation
],
'XNPV' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'XNPV'],
+ 'functionCall' => [Financial\CashFlow\Variable\NonPeriodic::class, 'presentValue'],
'argumentCount' => '3',
],
'XMATCH' => [
@@ -2619,17 +2701,17 @@ class Calculation
],
'XOR' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalXor'],
+ 'functionCall' => [Logical\Operations::class, 'logicalXor'],
'argumentCount' => '1+',
],
'YEAR' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'YEAR'],
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'year'],
'argumentCount' => '1',
],
'YEARFRAC' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'YEARFRAC'],
+ 'functionCall' => [DateTimeExcel\YearFrac::class, 'fraction'],
'argumentCount' => '2,3',
],
'YIELD' => [
@@ -2639,22 +2721,22 @@ class Calculation
],
'YIELDDISC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'YIELDDISC'],
+ 'functionCall' => [Financial\Securities\Yields::class, 'yieldDiscounted'],
'argumentCount' => '4,5',
],
'YIELDMAT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'YIELDMAT'],
+ 'functionCall' => [Financial\Securities\Yields::class, 'yieldAtMaturity'],
'argumentCount' => '5,6',
],
'ZTEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'ZTEST'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'zTest'],
'argumentCount' => '2-3',
],
'Z.TEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'ZTEST'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'zTest'],
'argumentCount' => '2-3',
],
];
@@ -2663,12 +2745,16 @@ class Calculation
private static $controlFunctions = [
'MKMATRIX' => [
'argumentCount' => '*',
- 'functionCall' => [__CLASS__, 'mkMatrix'],
+ 'functionCall' => [Internal\MakeMatrix::class, 'make'],
],
'NAME.ERROR' => [
'argumentCount' => '*',
'functionCall' => [Functions::class, 'NAME'],
],
+ 'WILDCARDMATCH' => [
+ 'argumentCount' => '2',
+ 'functionCall' => [Internal\WildcardMatch::class, 'compare'],
+ ],
];
public function __construct(?Spreadsheet $spreadsheet = null)
@@ -2695,12 +2781,10 @@ class Calculation
/**
* Get an instance of this class.
*
- * @param Spreadsheet $spreadsheet Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object,
- * or NULL to create a standalone claculation engine
- *
- * @return Calculation
+ * @param ?Spreadsheet $spreadsheet Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object,
+ * or NULL to create a standalone calculation engine
*/
- public static function getInstance(?Spreadsheet $spreadsheet = null)
+ public static function getInstance(?Spreadsheet $spreadsheet = null): self
{
if ($spreadsheet !== null) {
$instance = $spreadsheet->getCalculationEngine();
@@ -2749,7 +2833,7 @@ class Calculation
*
* @return string locale-specific translation of TRUE
*/
- public static function getTRUE()
+ public static function getTRUE(): string
{
return self::$localeBoolean['TRUE'];
}
@@ -2759,7 +2843,7 @@ class Calculation
*
* @return string locale-specific translation of FALSE
*/
- public static function getFALSE()
+ public static function getFALSE(): string
{
return self::$localeBoolean['FALSE'];
}
@@ -2809,11 +2893,11 @@ class Calculation
/**
* Enable/disable calculation cache.
*
- * @param bool $pValue
+ * @param bool $calculationCacheEnabled
*/
- public function setCalculationCacheEnabled($pValue): void
+ public function setCalculationCacheEnabled($calculationCacheEnabled): void
{
- $this->calculationCacheEnabled = $pValue;
+ $this->calculationCacheEnabled = $calculationCacheEnabled;
$this->clearCalculationCache();
}
@@ -2902,6 +2986,21 @@ class Calculation
return self::$localeLanguage;
}
+ private function getLocaleFile(string $localeDir, string $locale, string $language, string $file): string
+ {
+ $localeFileName = $localeDir . str_replace('_', DIRECTORY_SEPARATOR, $locale) .
+ DIRECTORY_SEPARATOR . $file;
+ if (!file_exists($localeFileName)) {
+ // If there isn't a locale specific file, look for a language specific file
+ $localeFileName = $localeDir . $language . DIRECTORY_SEPARATOR . $file;
+ if (!file_exists($localeFileName)) {
+ throw new Exception('Locale file not found');
+ }
+ }
+
+ return $localeFileName;
+ }
+
/**
* Set the locale code.
*
@@ -2909,7 +3008,7 @@ class Calculation
*
* @return bool
*/
- public function setLocale($locale)
+ public function setLocale(string $locale)
{
// Identify our locale and language
$language = $locale = strtolower($locale);
@@ -2919,31 +3018,30 @@ class Calculation
if (count(self::$validLocaleLanguages) == 1) {
self::loadLocales();
}
+
// Test whether we have any language data for this language (any locale)
if (in_array($language, self::$validLocaleLanguages)) {
// initialise language/locale settings
self::$localeFunctions = [];
self::$localeArgumentSeparator = ',';
self::$localeBoolean = ['TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL'];
- // Default is English, if user isn't requesting english, then read the necessary data from the locale files
- if ($locale != 'en_us') {
+
+ // Default is US English, if user isn't requesting US english, then read the necessary data from the locale files
+ if ($locale !== 'en_us') {
+ $localeDir = implode(DIRECTORY_SEPARATOR, [__DIR__, 'locale', null]);
// Search for a file with a list of function names for locale
- $functionNamesFile = __DIR__ . '/locale/' . str_replace('_', DIRECTORY_SEPARATOR, $locale) . DIRECTORY_SEPARATOR . 'functions';
- if (!file_exists($functionNamesFile)) {
- // If there isn't a locale specific function file, look for a language specific function file
- $functionNamesFile = __DIR__ . '/locale/' . $language . DIRECTORY_SEPARATOR . 'functions';
- if (!file_exists($functionNamesFile)) {
- return false;
- }
+ try {
+ $functionNamesFile = $this->getLocaleFile($localeDir, $locale, $language, 'functions');
+ } catch (Exception $e) {
+ return false;
}
+
// Retrieve the list of locale or language specific function names
$localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($localeFunctions as $localeFunction) {
[$localeFunction] = explode('##', $localeFunction); // Strip out comments
if (strpos($localeFunction, '=') !== false) {
- [$fName, $lfName] = explode('=', $localeFunction);
- $fName = trim($fName);
- $lfName = trim($lfName);
+ [$fName, $lfName] = array_map('trim', explode('=', $localeFunction));
if ((isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
self::$localeFunctions[$fName] = $lfName;
}
@@ -2957,20 +3055,22 @@ class Calculation
self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE'];
}
- $configFile = __DIR__ . '/locale/' . str_replace('_', DIRECTORY_SEPARATOR, $locale) . DIRECTORY_SEPARATOR . 'config';
- if (!file_exists($configFile)) {
- $configFile = __DIR__ . '/locale/' . $language . DIRECTORY_SEPARATOR . 'config';
+ try {
+ $configFile = $this->getLocaleFile($localeDir, $locale, $language, 'config');
+ } catch (Exception $e) {
+ return false;
}
- if (file_exists($configFile)) {
- $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
- foreach ($localeSettings as $localeSetting) {
- [$localeSetting] = explode('##', $localeSetting); // Strip out comments
- if (strpos($localeSetting, '=') !== false) {
- [$settingName, $settingValue] = explode('=', $localeSetting);
- $settingName = strtoupper(trim($settingName));
+
+ $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ foreach ($localeSettings as $localeSetting) {
+ [$localeSetting] = explode('##', $localeSetting); // Strip out comments
+ if (strpos($localeSetting, '=') !== false) {
+ [$settingName, $settingValue] = array_map('trim', explode('=', $localeSetting));
+ $settingName = strtoupper($settingName);
+ if ($settingValue !== '') {
switch ($settingName) {
case 'ARGUMENTSEPARATOR':
- self::$localeArgumentSeparator = trim($settingValue);
+ self::$localeArgumentSeparator = $settingValue;
break;
}
@@ -3061,9 +3161,9 @@ class Calculation
return $formula;
}
- private static $functionReplaceFromExcel = null;
+ private static $functionReplaceFromExcel;
- private static $functionReplaceToLocale = null;
+ private static $functionReplaceToLocale;
public function _translateFormulaToLocale($formula)
{
@@ -3090,9 +3190,9 @@ class Calculation
return self::translateFormula(self::$functionReplaceFromExcel, self::$functionReplaceToLocale, $formula, ',', self::$localeArgumentSeparator);
}
- private static $functionReplaceFromLocale = null;
+ private static $functionReplaceFromLocale;
- private static $functionReplaceToExcel = null;
+ private static $functionReplaceToExcel;
public function _translateFormulaToEnglish($formula)
{
@@ -3150,6 +3250,7 @@ class Calculation
// Return Excel errors "as is"
return $value;
}
+
// Return strings wrapped in quotes
return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
} elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
@@ -3185,14 +3286,14 @@ class Calculation
* Calculate cell value (using formula from a cell ID)
* Retained for backward compatibility.
*
- * @param Cell $pCell Cell to calculate
+ * @param Cell $cell Cell to calculate
*
* @return mixed
*/
- public function calculate(?Cell $pCell = null)
+ public function calculate(?Cell $cell = null)
{
try {
- return $this->calculateCellValue($pCell);
+ return $this->calculateCellValue($cell);
} catch (\Exception $e) {
throw new Exception($e->getMessage());
}
@@ -3201,14 +3302,14 @@ class Calculation
/**
* Calculate the value of a cell formula.
*
- * @param Cell $pCell Cell to calculate
+ * @param Cell $cell Cell to calculate
* @param bool $resetLog Flag indicating whether the debug log should be reset or not
*
* @return mixed
*/
- public function calculateCellValue(?Cell $pCell = null, $resetLog = true)
+ public function calculateCellValue(?Cell $cell = null, $resetLog = true)
{
- if ($pCell === null) {
+ if ($cell === null) {
return null;
}
@@ -3225,12 +3326,12 @@ class Calculation
// Execute the calculation for the cell formula
$this->cellStack[] = [
- 'sheet' => $pCell->getWorksheet()->getTitle(),
- 'cell' => $pCell->getCoordinate(),
+ 'sheet' => $cell->getWorksheet()->getTitle(),
+ 'cell' => $cell->getCoordinate(),
];
try {
- $result = self::unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
+ $result = self::unwrapResult($this->_calculateFormulaValue($cell->getValue(), $cell->getCoordinate(), $cell));
$cellAddress = array_pop($this->cellStack);
$this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
} catch (\Exception $e) {
@@ -3266,7 +3367,7 @@ class Calculation
}
self::$returnArrayAsType = $returnArrayAsType;
- if ($result === null && $pCell->getWorksheet()->getSheetView()->getShowZeros()) {
+ if ($result === null && $cell->getWorksheet()->getSheetView()->getShowZeros()) {
return 0;
} elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
return Functions::NAN();
@@ -3304,11 +3405,11 @@ class Calculation
*
* @param string $formula Formula to parse
* @param string $cellID Address of the cell to calculate
- * @param Cell $pCell Cell to calculate
+ * @param Cell $cell Cell to calculate
*
* @return mixed
*/
- public function calculateFormula($formula, $cellID = null, ?Cell $pCell = null)
+ public function calculateFormula($formula, $cellID = null, ?Cell $cell = null)
{
// Initialise the logging settings
$this->formulaError = null;
@@ -3316,9 +3417,9 @@ class Calculation
$this->cyclicReferenceStack->clear();
$resetCache = $this->getCalculationCacheEnabled();
- if ($this->spreadsheet !== null && $cellID === null && $pCell === null) {
+ if ($this->spreadsheet !== null && $cellID === null && $cell === null) {
$cellID = 'A1';
- $pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
+ $cell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
} else {
// Disable calculation cacheing because it only applies to cell calculations, not straight formulae
// But don't actually flush any cache
@@ -3327,7 +3428,7 @@ class Calculation
// Execute the calculation
try {
- $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
+ $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $cell));
} catch (\Exception $e) {
throw new Exception($e->getMessage());
}
@@ -3341,18 +3442,15 @@ class Calculation
}
/**
- * @param string $cellReference
* @param mixed $cellValue
- *
- * @return bool
*/
- public function getValueFromCache($cellReference, &$cellValue)
+ public function getValueFromCache(string $cellReference, &$cellValue): bool
{
+ $this->debugLog->writeDebugLog("Testing cache value for cell {$cellReference}");
// Is calculation cacheing enabled?
- // Is the value present in calculation cache?
- $this->debugLog->writeDebugLog('Testing cache value for cell ', $cellReference);
+ // If so, is the required value present in calculation cache?
if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
- $this->debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
+ $this->debugLog->writeDebugLog("Retrieving value for cell {$cellReference} from cache");
// Return the cached result
$cellValue = $this->calculationCache[$cellReference];
@@ -3379,16 +3477,16 @@ class Calculation
*
* @param string $formula The formula to parse and calculate
* @param string $cellID The ID (e.g. A3) of the cell that we are calculating
- * @param Cell $pCell Cell to calculate
+ * @param Cell $cell Cell to calculate
*
* @return mixed
*/
- public function _calculateFormulaValue($formula, $cellID = null, ?Cell $pCell = null)
+ public function _calculateFormulaValue($formula, $cellID = null, ?Cell $cell = null)
{
$cellValue = null;
// Quote-Prefixed cell values cannot be formulae, but are treated as strings
- if ($pCell !== null && $pCell->getStyle()->getQuotePrefix() === true) {
+ if ($cell !== null && $cell->getStyle()->getQuotePrefix() === true) {
return self::wrapResult((string) $formula);
}
@@ -3407,14 +3505,14 @@ class Calculation
return self::wrapResult($formula);
}
- $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
+ $pCellParent = ($cell !== null) ? $cell->getWorksheet() : null;
$wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk";
$wsCellReference = $wsTitle . '!' . $cellID;
if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
return $cellValue;
}
- $this->debugLog->writeDebugLog('Evaluating formula for cell ', $wsCellReference);
+ $this->debugLog->writeDebugLog("Evaluating formula for cell {$wsCellReference}");
if (($wsTitle[0] !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) {
if ($this->cyclicFormulaCount <= 0) {
@@ -3436,10 +3534,11 @@ class Calculation
}
}
- $this->debugLog->writeDebugLog('Formula for cell ', $wsCellReference, ' is ', $formula);
+ $this->debugLog->writeDebugLog("Formula for cell {$wsCellReference} is {$formula}");
// Parse the formula onto the token stack and calculate the value
$this->cyclicReferenceStack->push($wsCellReference);
- $cellValue = $this->processTokenStack($this->internalParseFormula($formula, $pCell), $cellID, $pCell);
+
+ $cellValue = $this->processTokenStack($this->internalParseFormula($formula, $cell), $cellID, $cell);
$this->cyclicReferenceStack->pop();
// Save to calculation cache
@@ -3454,8 +3553,8 @@ class Calculation
/**
* Ensure that paired matrix operands are both matrices and of the same size.
*
- * @param mixed &$operand1 First matrix operand
- * @param mixed &$operand2 Second matrix operand
+ * @param mixed $operand1 First matrix operand
+ * @param mixed $operand2 Second matrix operand
* @param int $resize Flag indicating whether the matrices should be resized to match
* and (if so), whether the smaller dimension should grow or the
* larger should shrink.
@@ -3499,7 +3598,7 @@ class Calculation
/**
* Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0.
*
- * @param array &$matrix matrix operand
+ * @param array $matrix matrix operand
*
* @return int[] An array comprising the number of rows, and number of columns
*/
@@ -3524,8 +3623,8 @@ class Calculation
/**
* Ensure that paired matrix operands are both matrices of the same size.
*
- * @param mixed &$matrix1 First matrix operand
- * @param mixed &$matrix2 Second matrix operand
+ * @param mixed $matrix1 First matrix operand
+ * @param mixed $matrix2 Second matrix operand
* @param int $matrix1Rows Row size of first matrix operand
* @param int $matrix1Columns Column size of first matrix operand
* @param int $matrix2Rows Row size of second matrix operand
@@ -3567,8 +3666,8 @@ class Calculation
/**
* Ensure that paired matrix operands are both matrices of the same size.
*
- * @param mixed &$matrix1 First matrix operand
- * @param mixed &$matrix2 Second matrix operand
+ * @param mixed $matrix1 First matrix operand
+ * @param mixed $matrix2 Second matrix operand
* @param int $matrix1Rows Row size of first matrix operand
* @param int $matrix1Columns Column size of first matrix operand
* @param int $matrix2Rows Row size of second matrix operand
@@ -3685,6 +3784,8 @@ class Calculation
return $typeString . ' with a value of ' . $this->showValue($value);
}
+
+ return null;
}
/**
@@ -3743,11 +3844,6 @@ class Calculation
return $formula;
}
- private static function mkMatrix(...$args)
- {
- return $args;
- }
-
// Binary Operators
// These operators always work on two values
// Array key is the operator, the value indicates whether this is a left or right associative operator
@@ -3784,9 +3880,9 @@ class Calculation
/**
* @param string $formula
*
- * @return bool
+ * @return array|false
*/
- private function internalParseFormula($formula, ?Cell $pCell = null)
+ private function internalParseFormula($formula, ?Cell $cell = null)
{
if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
return false;
@@ -3794,10 +3890,12 @@ class Calculation
// If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
// so we store the parent worksheet so that we can re-attach it when necessary
- $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
+ $pCellParent = ($cell !== null) ? $cell->getWorksheet() : null;
$regexpMatchString = '/^(' . self::CALCULATION_REGEXP_FUNCTION .
'|' . self::CALCULATION_REGEXP_CELLREF .
+ '|' . self::CALCULATION_REGEXP_COLUMN_RANGE .
+ '|' . self::CALCULATION_REGEXP_ROW_RANGE .
'|' . self::CALCULATION_REGEXP_NUMBER .
'|' . self::CALCULATION_REGEXP_STRING .
'|' . self::CALCULATION_REGEXP_OPENBRACE .
@@ -3866,7 +3964,8 @@ class Calculation
$opCharacter .= $formula[++$index];
}
// Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
- $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
+ $isOperandOrFunction = (bool) preg_match($regexpMatchString, substr($formula, $index), $match);
+
if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
// Put a negation on the stack
$stack->push('Unary Operator', '~', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
@@ -3942,6 +4041,7 @@ class Calculation
}
// Check the argument count
$argumentCountError = false;
+ $expectedArgumentCountString = null;
if (is_numeric($expectedArgumentCount)) {
if ($expectedArgumentCount < 0) {
if ($argumentCount > abs($expectedArgumentCount)) {
@@ -4010,7 +4110,7 @@ class Calculation
// If we've a comma when we're expecting an operand, then what we actually have is a null operand;
// so push a null onto the stack
if (($expectingOperand) || (!$expectingOperator)) {
- $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null];
+ $output[] = ['type' => 'Empty Argument', 'value' => self::$excelConstants['NULL'], 'reference' => null];
}
// make sure there was a function
$d = $stack->last(2);
@@ -4037,6 +4137,7 @@ class Calculation
$expectingOperand = false;
$val = $match[1];
$length = strlen($val);
+
if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) {
$val = preg_replace('/\s/u', '', $val);
if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
@@ -4073,7 +4174,7 @@ class Calculation
// Should only be applied to the actual cell column, not the worksheet name
// If the last entry on the stack was a : operator, then we have a cell range reference
$testPrevOp = $stack->last(1);
- if ($testPrevOp !== null && $testPrevOp['value'] == ':') {
+ if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
// If we have a worksheet reference, then we're playing with a 3D reference
if ($matches[2] == '') {
// Otherwise, we 'inherit' the worksheet reference from the start cell reference
@@ -4090,62 +4191,59 @@ class Calculation
return $this->raiseFormulaError('3D Range references are not yet supported');
}
}
+ } elseif (strpos($val, '!') === false && $pCellParent !== null) {
+ $worksheet = $pCellParent->getTitle();
+ $val = "'{$worksheet}'!{$val}";
}
$outputItem = $stack->getStackItem('Cell Reference', $val, $val, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
$output[] = $outputItem;
} else { // it's a variable, constant, string, number or boolean
+ $localeConstant = false;
+ $stackItemType = 'Value';
+ $stackItemReference = null;
+
// If the last entry on the stack was a : operator, then we may have a row or column range reference
$testPrevOp = $stack->last(1);
if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
+ $stackItemType = 'Cell Reference';
$startRowColRef = $output[count($output) - 1]['value'];
[$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true);
$rangeSheetRef = $rangeWS1;
- if ($rangeWS1 != '') {
+ if ($rangeWS1 !== '') {
$rangeWS1 .= '!';
}
+ $rangeSheetRef = trim($rangeSheetRef, "'");
[$rangeWS2, $val] = Worksheet::extractSheetTitle($val, true);
- if ($rangeWS2 != '') {
+ if ($rangeWS2 !== '') {
$rangeWS2 .= '!';
} else {
$rangeWS2 = $rangeWS1;
}
+
$refSheet = $pCellParent;
- if ($pCellParent !== null && $rangeSheetRef !== $pCellParent->getTitle()) {
+ if ($pCellParent !== null && $rangeSheetRef !== '' && $rangeSheetRef !== $pCellParent->getTitle()) {
$refSheet = $pCellParent->getParent()->getSheetByName($rangeSheetRef);
}
- if (
- (is_int($startRowColRef)) && (ctype_digit($val)) &&
- ($startRowColRef <= 1048576) && ($val <= 1048576)
- ) {
- // Row range
- $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
- $output[count($output) - 1]['value'] = $rangeWS1 . 'A' . $startRowColRef;
- $val = $rangeWS2 . $endRowColRef . $val;
- } elseif (
- (ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
- (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)
- ) {
- // Column range
- $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
- $output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1';
- $val = $rangeWS2 . $val . $endRowColRef;
- }
- }
- $localeConstant = false;
- $stackItemType = 'Value';
- $stackItemReference = null;
- if ($opCharacter == self::FORMULA_STRING_QUOTE) {
+ if (ctype_digit($val) && $val <= 1048576) {
+ // Row range
+ $stackItemType = 'Row Reference';
+ /** @var int $valx */
+ $valx = $val;
+ $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataColumn($valx) : 'XFD'; // Max 16,384 columns for Excel2007
+ $val = "{$rangeWS2}{$endRowColRef}{$val}";
+ } elseif (ctype_alpha($val) && strlen($val) <= 3) {
+ // Column range
+ $stackItemType = 'Column Reference';
+ $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataRow($val) : 1048576; // Max 1,048,576 rows for Excel2007
+ $val = "{$rangeWS2}{$val}{$endRowColRef}";
+ }
+ $stackItemReference = $val;
+ } elseif ($opCharacter == self::FORMULA_STRING_QUOTE) {
// UnEscape any quotes within the string
$val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val)));
- } elseif (is_numeric($val)) {
- if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
- $val = (float) $val;
- } else {
- $val = (int) $val;
- }
} elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
$stackItemType = 'Constant';
$excelConstant = trim(strtoupper($val));
@@ -4153,10 +4251,41 @@ class Calculation
} elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
$stackItemType = 'Constant';
$val = self::$excelConstants[$localeConstant];
+ } elseif (
+ preg_match('/^' . self::CALCULATION_REGEXP_ROW_RANGE . '/miu', substr($formula, $index), $rowRangeReference)
+ ) {
+ $val = $rowRangeReference[1];
+ $length = strlen($rowRangeReference[1]);
+ $stackItemType = 'Row Reference';
+ $column = 'A';
+ if (($testPrevOp !== null && $testPrevOp['value'] === ':') && $pCellParent !== null) {
+ $column = $pCellParent->getHighestDataColumn($val);
+ }
+ $val = "{$rowRangeReference[2]}{$column}{$rowRangeReference[7]}";
+ $stackItemReference = $val;
+ } elseif (
+ preg_match('/^' . self::CALCULATION_REGEXP_COLUMN_RANGE . '/miu', substr($formula, $index), $columnRangeReference)
+ ) {
+ $val = $columnRangeReference[1];
+ $length = strlen($val);
+ $stackItemType = 'Column Reference';
+ $row = '1';
+ if (($testPrevOp !== null && $testPrevOp['value'] === ':') && $pCellParent !== null) {
+ $row = $pCellParent->getHighestDataRow($val);
+ }
+ $val = "{$val}{$row}";
+ $stackItemReference = $val;
} elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', $val, $match)) {
$stackItemType = 'Defined Name';
$stackItemReference = $val;
+ } elseif (is_numeric($val)) {
+ if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
+ $val = (float) $val;
+ } else {
+ $val = (int) $val;
+ }
}
+
$details = $stack->getStackItem($stackItemType, $val, $stackItemReference, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
if ($localeConstant) {
$details['localeValue'] = $localeConstant;
@@ -4168,7 +4297,7 @@ class Calculation
++$index;
} elseif ($opCharacter == ')') { // miscellaneous error checking
if ($expectingOperand) {
- $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null];
+ $output[] = ['type' => 'Empty Argument', 'value' => self::$excelConstants['NULL'], 'reference' => null];
$expectingOperand = false;
$expectingOperator = true;
} else {
@@ -4203,7 +4332,8 @@ class Calculation
// Cell References) then we have an INTERSECTION operator
if (
($expectingOperator) &&
- ((preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) &&
+ (
+ (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) &&
($output[count($output) - 1]['type'] == 'Cell Reference') ||
(preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', substr($formula, $index), $match)) &&
($output[count($output) - 1]['type'] == 'Defined Name' || $output[count($output) - 1]['type'] == 'Value')
@@ -4241,7 +4371,7 @@ class Calculation
$rowKey = array_shift($rKeys);
$cKeys = array_keys(array_keys($operand[$rowKey]));
$colKey = array_shift($cKeys);
- if (ctype_upper($colKey)) {
+ if (ctype_upper("$colKey")) {
$operandData['reference'] = $colKey . $rowKey;
}
}
@@ -4255,9 +4385,9 @@ class Calculation
* @param mixed $tokens
* @param null|string $cellID
*
- * @return bool
+ * @return array|false
*/
- private function processTokenStack($tokens, $cellID = null, ?Cell $pCell = null)
+ private function processTokenStack($tokens, $cellID = null, ?Cell $cell = null)
{
if ($tokens == false) {
return false;
@@ -4265,8 +4395,8 @@ class Calculation
// If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
// so we store the parent cell collection so that we can re-attach it when necessary
- $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null;
- $pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
+ $pCellWorksheet = ($cell !== null) ? $cell->getWorksheet() : null;
+ $pCellParent = ($cell !== null) ? $cell->getParent() : null;
$stack = new Stack();
// Stores branches that have been pruned
@@ -4329,7 +4459,8 @@ class Calculation
&& (
$storeValueAsBool
|| Functions::isError($storeValue)
- || ($storeValue === 'Pruned branch'))
+ || ($storeValue === 'Pruned branch')
+ )
) {
// If branching value is true, we don't need to compute
if (!isset($fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey])) {
@@ -4350,7 +4481,7 @@ class Calculation
}
// if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
- if (isset(self::$binaryOperators[$token])) {
+ if (!is_numeric($token) && isset(self::$binaryOperators[$token])) {
// We must have two operands, error if we don't
if (($operand2Data = $stack->pop()) === null) {
return $this->raiseFormulaError('Internal error - Operand value missing from stack');
@@ -4397,23 +4528,23 @@ class Calculation
$sheet2 = $sheet1;
}
- if ($sheet1 == $sheet2) {
+ if (trim($sheet1, "'") === trim($sheet2, "'")) {
if ($operand1Data['reference'] === null) {
if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
- $operand1Data['reference'] = $pCell->getColumn() . $operand1Data['value'];
+ $operand1Data['reference'] = $cell->getColumn() . $operand1Data['value'];
} elseif (trim($operand1Data['reference']) == '') {
- $operand1Data['reference'] = $pCell->getCoordinate();
+ $operand1Data['reference'] = $cell->getCoordinate();
} else {
- $operand1Data['reference'] = $operand1Data['value'] . $pCell->getRow();
+ $operand1Data['reference'] = $operand1Data['value'] . $cell->getRow();
}
}
if ($operand2Data['reference'] === null) {
if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
- $operand2Data['reference'] = $pCell->getColumn() . $operand2Data['value'];
+ $operand2Data['reference'] = $cell->getColumn() . $operand2Data['value'];
} elseif (trim($operand2Data['reference']) == '') {
- $operand2Data['reference'] = $pCell->getCoordinate();
+ $operand2Data['reference'] = $cell->getCoordinate();
} else {
- $operand2Data['reference'] = $operand2Data['value'] . $pCell->getRow();
+ $operand2Data['reference'] = $operand2Data['value'] . $cell->getRow();
}
}
@@ -4430,6 +4561,7 @@ class Calculation
} else {
return $this->raiseFormulaError('Unable to access Cell Reference');
}
+
$stack->push('Cell Reference', $cellValue, $cellRef);
} else {
$stack->push('Error', Functions::REF(), null);
@@ -4561,10 +4693,11 @@ class Calculation
} else {
$this->executeNumericBinaryOperation($multiplier, $arg, '*', 'arrayTimesEquals', $stack);
}
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token, $matches)) {
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token ?? '', $matches)) {
$cellRef = null;
+
if (isset($matches[8])) {
- if ($pCell === null) {
+ if ($cell === null) {
// We can't access the range, so return a REF error
$cellValue = Functions::REF();
} else {
@@ -4594,8 +4727,8 @@ class Calculation
}
}
} else {
- if ($pCell === null) {
- // We can't access the cell, so return a REF error
+ if ($cell === null) {
+ // We can't access the cell, so return a REF error
$cellValue = Functions::REF();
} else {
$cellRef = $matches[6] . $matches[7];
@@ -4610,8 +4743,9 @@ class Calculation
$cellSheet = $this->spreadsheet->getSheetByName($matches[2]);
if ($cellSheet && $cellSheet->cellExists($cellRef)) {
$cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
- $pCell->attach($pCellParent);
+ $cell->attach($pCellParent);
} else {
+ $cellRef = ($cellSheet !== null) ? "'{$matches[2]}'!{$cellRef}" : $cellRef;
$cellValue = null;
}
} else {
@@ -4622,7 +4756,7 @@ class Calculation
$this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
if ($pCellParent->has($cellRef)) {
$cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
- $pCell->attach($pCellParent);
+ $cell->attach($pCellParent);
} else {
$cellValue = null;
}
@@ -4630,24 +4764,28 @@ class Calculation
}
}
}
- $stack->push('Value', $cellValue, $cellRef);
+
+ $stack->push('Cell Value', $cellValue, $cellRef);
if (isset($storeKey)) {
$branchStore[$storeKey] = $cellValue;
}
// if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $token, $matches)) {
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $token ?? '', $matches)) {
if ($pCellParent) {
- $pCell->attach($pCellParent);
+ $cell->attach($pCellParent);
}
$functionName = $matches[1];
$argCount = $stack->pop();
$argCount = $argCount['value'];
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
$this->debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
}
if ((isset(self::$phpSpreadsheetFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) { // function
+ $passByReference = false;
+ $passCellReference = false;
+ $functionCall = null;
if (isset(self::$phpSpreadsheetFunctions[$functionName])) {
$functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
$passByReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference']);
@@ -4657,8 +4795,10 @@ class Calculation
$passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
$passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
}
+
// get the arguments for this function
$args = $argArrayVals = [];
+ $emptyArguments = [];
for ($i = 0; $i < $argCount; ++$i) {
$arg = $stack->pop();
$a = $argCount - $i - 1;
@@ -4669,18 +4809,19 @@ class Calculation
) {
if ($arg['reference'] === null) {
$args[] = $cellID;
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
$argArrayVals[] = $this->showValue($cellID);
}
} else {
$args[] = $arg['reference'];
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
$argArrayVals[] = $this->showValue($arg['reference']);
}
}
} else {
+ $emptyArguments[] = ($arg['type'] === 'Empty Argument');
$args[] = self::unwrapResult($arg['value']);
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
$argArrayVals[] = $this->showValue($arg['value']);
}
}
@@ -4688,13 +4829,18 @@ class Calculation
// Reverse the order of the arguments
krsort($args);
+ krsort($emptyArguments);
+
+ if ($argCount > 0) {
+ $args = $this->addDefaultArgumentValues($functionCall, $args, $emptyArguments);
+ }
if (($passByReference) && ($argCount == 0)) {
$args[] = $cellID;
$argArrayVals[] = $this->showValue($cellID);
}
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
if ($this->debugLog->getWriteDebugLog()) {
krsort($argArrayVals);
$this->debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator . ' ', Functions::flattenArray($argArrayVals)), ' )');
@@ -4702,7 +4848,7 @@ class Calculation
}
// Process the argument with the appropriate function call
- $args = $this->addCellReference($args, $passCellReference, $functionCall, $pCell);
+ $args = $this->addCellReference($args, $passCellReference, $functionCall, $cell);
if (!is_array($functionCall)) {
foreach ($args as &$arg) {
@@ -4713,7 +4859,7 @@ class Calculation
$result = call_user_func_array($functionCall, $args);
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
$this->debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
}
$stack->push('Value', self::wrapResult($result));
@@ -4723,7 +4869,7 @@ class Calculation
}
} else {
// if the token is a number, boolean, string or an Excel error, push it onto the stack
- if (isset(self::$excelConstants[strtoupper($token)])) {
+ if (isset(self::$excelConstants[strtoupper($token ?? '')])) {
$excelConstant = strtoupper($token);
$stack->push('Constant Value', self::$excelConstants[$excelConstant]);
if (isset($storeKey)) {
@@ -4731,14 +4877,14 @@ class Calculation
}
$this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
} elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == self::FORMULA_STRING_QUOTE) || ($token[0] == '#')) {
- $stack->push('Value', $token);
+ $stack->push($tokenData['type'], $token, $tokenData['reference']);
if (isset($storeKey)) {
$branchStore[$storeKey] = $token;
}
// if the token is a named range or formula, evaluate it and push the result onto the stack
} elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) {
$definedName = $matches[6];
- if ($pCell === null || $pCellWorksheet === null) {
+ if ($cell === null || $pCellWorksheet === null) {
return $this->raiseFormulaError("undefined name '$token'");
}
@@ -4748,7 +4894,7 @@ class Calculation
return $this->raiseFormulaError("undefined name '$definedName'");
}
- $result = $this->evaluateDefinedName($pCell, $namedRange, $pCellWorksheet, $stack);
+ $result = $this->evaluateDefinedName($cell, $namedRange, $pCellWorksheet, $stack);
if (isset($storeKey)) {
$branchStore[$storeKey] = $result;
}
@@ -4805,6 +4951,53 @@ class Calculation
return true;
}
+ /**
+ * @param null|string $cellID
+ * @param mixed $operand1
+ * @param mixed $operand2
+ * @param string $operation
+ *
+ * @return array
+ */
+ private function executeArrayComparison($cellID, $operand1, $operand2, $operation, Stack &$stack, bool $recursingArrays)
+ {
+ $result = [];
+ if (!is_array($operand2)) {
+ // Operand 1 is an array, Operand 2 is a scalar
+ foreach ($operand1 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2));
+ $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } elseif (!is_array($operand1)) {
+ // Operand 1 is a scalar, Operand 2 is an array
+ foreach ($operand2 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->showValue($operandData));
+ $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } else {
+ // Operand 1 and Operand 2 are both arrays
+ if (!$recursingArrays) {
+ self::checkMatrixOperands($operand1, $operand2, 2);
+ }
+ foreach ($operand1 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x]));
+ $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ }
+ // Log the result details
+ $this->debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Array', $result);
+
+ return $result;
+ }
+
/**
* @param null|string $cellID
* @param mixed $operand1
@@ -4818,38 +5011,7 @@ class Calculation
{
// If we're dealing with matrix operations, we want a matrix result
if ((is_array($operand1)) || (is_array($operand2))) {
- $result = [];
- if ((is_array($operand1)) && (!is_array($operand2))) {
- foreach ($operand1 as $x => $operandData) {
- $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2));
- $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack);
- $r = $stack->pop();
- $result[$x] = $r['value'];
- }
- } elseif ((!is_array($operand1)) && (is_array($operand2))) {
- foreach ($operand2 as $x => $operandData) {
- $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->showValue($operandData));
- $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack);
- $r = $stack->pop();
- $result[$x] = $r['value'];
- }
- } else {
- if (!$recursingArrays) {
- self::checkMatrixOperands($operand1, $operand2, 2);
- }
- foreach ($operand1 as $x => $operandData) {
- $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x]));
- $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
- $r = $stack->pop();
- $result[$x] = $r['value'];
- }
- }
- // Log the result details
- $this->debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result));
- // And push the result onto the stack
- $stack->push('Array', $result);
-
- return $result;
+ return $this->executeArrayComparison($cellID, $operand1, $operand2, $operation, $stack, $recursingArrays);
}
// Simple validate the two operands if they are string values
@@ -4863,10 +5025,10 @@ class Calculation
// Use case insensitive comparaison if not OpenOffice mode
if (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) {
if (is_string($operand1)) {
- $operand1 = strtoupper($operand1);
+ $operand1 = Shared\StringHelper::strToUpper($operand1);
}
if (is_string($operand2)) {
- $operand2 = strtoupper($operand2);
+ $operand2 = Shared\StringHelper::strToUpper($operand2);
}
}
@@ -4897,7 +5059,7 @@ class Calculation
if (is_numeric($operand1) && is_numeric($operand2)) {
$result = (abs($operand1 - $operand2) < $this->delta);
} else {
- $result = strcmp($operand1, $operand2) == 0;
+ $result = $this->strcmpAllowNull($operand1, $operand2) == 0;
}
break;
@@ -4908,7 +5070,7 @@ class Calculation
} elseif ($useLowercaseFirstComparison) {
$result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
} else {
- $result = strcmp($operand1, $operand2) >= 0;
+ $result = $this->strcmpAllowNull($operand1, $operand2) >= 0;
}
break;
@@ -4919,7 +5081,7 @@ class Calculation
} elseif ($useLowercaseFirstComparison) {
$result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
} else {
- $result = strcmp($operand1, $operand2) <= 0;
+ $result = $this->strcmpAllowNull($operand1, $operand2) <= 0;
}
break;
@@ -4928,10 +5090,13 @@ class Calculation
if (is_numeric($operand1) && is_numeric($operand2)) {
$result = (abs($operand1 - $operand2) > 1E-14);
} else {
- $result = strcmp($operand1, $operand2) != 0;
+ $result = $this->strcmpAllowNull($operand1, $operand2) != 0;
}
break;
+
+ default:
+ throw new Exception('Unsupported binary comparison operation');
}
// Log the result details
@@ -4945,8 +5110,8 @@ class Calculation
/**
* Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters.
*
- * @param string $str1 First string value for the comparison
- * @param string $str2 Second string value for the comparison
+ * @param null|string $str1 First string value for the comparison
+ * @param null|string $str2 Second string value for the comparison
*
* @return int
*/
@@ -4955,7 +5120,20 @@ class Calculation
$inversedStr1 = Shared\StringHelper::strCaseReverse($str1);
$inversedStr2 = Shared\StringHelper::strCaseReverse($str2);
- return strcmp($inversedStr1, $inversedStr2);
+ return strcmp($inversedStr1 ?? '', $inversedStr2 ?? '');
+ }
+
+ /**
+ * PHP8.1 deprecates passing null to strcmp.
+ *
+ * @param null|string $str1 First string value for the comparison
+ * @param null|string $str2 Second string value for the comparison
+ *
+ * @return int
+ */
+ private function strcmpAllowNull($str1, $str2)
+ {
+ return strcmp($str1 ?? '', $str2 ?? '');
}
/**
@@ -5036,6 +5214,9 @@ class Calculation
$result = $operand1 ** $operand2;
break;
+
+ default:
+ throw new Exception('Unsupported numeric binary operation');
}
}
}
@@ -5048,15 +5229,22 @@ class Calculation
return $result;
}
- // trigger an error, but nicely, if need be
- protected function raiseFormulaError($errorMessage)
+ /**
+ * Trigger an error, but nicely, if need be.
+ *
+ * @return false
+ */
+ protected function raiseFormulaError(string $errorMessage)
{
$this->formulaError = $errorMessage;
$this->cyclicReferenceStack->clear();
if (!$this->suppressFormulaErrors) {
throw new Exception($errorMessage);
}
- trigger_error($errorMessage, E_USER_ERROR);
+
+ if (strlen($errorMessage) > 0) {
+ trigger_error($errorMessage, E_USER_ERROR);
+ }
return false;
}
@@ -5064,34 +5252,35 @@ class Calculation
/**
* Extract range values.
*
- * @param string &$pRange String based range representation
- * @param Worksheet $pSheet Worksheet
+ * @param string $range String based range representation
+ * @param Worksheet $worksheet Worksheet
* @param bool $resetLog Flag indicating whether calculation log should be reset or not
*
* @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
*/
- public function extractCellRange(&$pRange = 'A1', ?Worksheet $pSheet = null, $resetLog = true)
+ public function extractCellRange(&$range = 'A1', ?Worksheet $worksheet = null, $resetLog = true)
{
// Return value
$returnValue = [];
- if ($pSheet !== null) {
- $pSheetName = $pSheet->getTitle();
- if (strpos($pRange, '!') !== false) {
- [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
- $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
+ if ($worksheet !== null) {
+ $worksheetName = $worksheet->getTitle();
+
+ if (strpos($range, '!') !== false) {
+ [$worksheetName, $range] = Worksheet::extractSheetTitle($range, true);
+ $worksheet = $this->spreadsheet->getSheetByName($worksheetName);
}
// Extract range
- $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
- $pRange = $pSheetName . '!' . $pRange;
+ $aReferences = Coordinate::extractAllCellReferencesInRange($range);
+ $range = "'" . $worksheetName . "'" . '!' . $range;
if (!isset($aReferences[1])) {
$currentCol = '';
$currentRow = 0;
// Single cell in range
sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow);
- if ($pSheet->cellExists($aReferences[0])) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ if ($worksheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
} else {
$returnValue[$currentRow][$currentCol] = null;
}
@@ -5102,8 +5291,8 @@ class Calculation
$currentRow = 0;
// Extract range
sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
- if ($pSheet->cellExists($reference)) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ if ($worksheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($reference)->getCalculatedValue($resetLog);
} else {
$returnValue[$currentRow][$currentCol] = null;
}
@@ -5117,47 +5306,46 @@ class Calculation
/**
* Extract range values.
*
- * @param string &$pRange String based range representation
- * @param Worksheet $pSheet Worksheet
+ * @param string $range String based range representation
+ * @param null|Worksheet $worksheet Worksheet
* @param bool $resetLog Flag indicating whether calculation log should be reset or not
*
* @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
*/
- public function extractNamedRange(&$pRange = 'A1', ?Worksheet $pSheet = null, $resetLog = true)
+ public function extractNamedRange(string &$range = 'A1', ?Worksheet $worksheet = null, $resetLog = true)
{
// Return value
$returnValue = [];
- if ($pSheet !== null) {
- $pSheetName = $pSheet->getTitle();
- if (strpos($pRange, '!') !== false) {
- [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
- $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
+ if ($worksheet !== null) {
+ if (strpos($range, '!') !== false) {
+ [$worksheetName, $range] = Worksheet::extractSheetTitle($range, true);
+ $worksheet = $this->spreadsheet->getSheetByName($worksheetName);
}
// Named range?
- $namedRange = DefinedName::resolveName($pRange, $pSheet);
+ $namedRange = DefinedName::resolveName($range, $worksheet);
if ($namedRange === null) {
return Functions::REF();
}
- $pSheet = $namedRange->getWorksheet();
- $pRange = $namedRange->getValue();
- $splitRange = Coordinate::splitRange($pRange);
+ $worksheet = $namedRange->getWorksheet();
+ $range = $namedRange->getValue();
+ $splitRange = Coordinate::splitRange($range);
// Convert row and column references
if (ctype_alpha($splitRange[0][0])) {
- $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
+ $range = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
} elseif (ctype_digit($splitRange[0][0])) {
- $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
+ $range = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
}
// Extract range
- $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
+ $aReferences = Coordinate::extractAllCellReferencesInRange($range);
if (!isset($aReferences[1])) {
// Single cell (or single column or row) in range
[$currentCol, $currentRow] = Coordinate::coordinateFromString($aReferences[0]);
- if ($pSheet->cellExists($aReferences[0])) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ if ($worksheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
} else {
$returnValue[$currentRow][$currentCol] = null;
}
@@ -5166,8 +5354,8 @@ class Calculation
foreach ($aReferences as $reference) {
// Extract range
[$currentCol, $currentRow] = Coordinate::coordinateFromString($reference);
- if ($pSheet->cellExists($reference)) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ if ($worksheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($reference)->getCalculatedValue($resetLog);
} else {
$returnValue[$currentRow][$currentCol] = null;
}
@@ -5181,24 +5369,22 @@ class Calculation
/**
* Is a specific function implemented?
*
- * @param string $pFunction Function Name
+ * @param string $function Function Name
*
* @return bool
*/
- public function isImplemented($pFunction)
+ public function isImplemented($function)
{
- $pFunction = strtoupper($pFunction);
- $notImplemented = !isset(self::$phpSpreadsheetFunctions[$pFunction]) || (is_array(self::$phpSpreadsheetFunctions[$pFunction]['functionCall']) && self::$phpSpreadsheetFunctions[$pFunction]['functionCall'][1] === 'DUMMY');
+ $function = strtoupper($function);
+ $notImplemented = !isset(self::$phpSpreadsheetFunctions[$function]) || (is_array(self::$phpSpreadsheetFunctions[$function]['functionCall']) && self::$phpSpreadsheetFunctions[$function]['functionCall'][1] === 'DUMMY');
return !$notImplemented;
}
/**
* Get a list of all implemented functions as an array of function objects.
- *
- * @return array of Category
*/
- public function getFunctions()
+ public function getFunctions(): array
{
return self::$phpSpreadsheetFunctions;
}
@@ -5220,6 +5406,57 @@ class Calculation
return $returnValue;
}
+ private function addDefaultArgumentValues(array $functionCall, array $args, array $emptyArguments): array
+ {
+ $reflector = new ReflectionMethod(implode('::', $functionCall));
+ $methodArguments = $reflector->getParameters();
+
+ if (count($methodArguments) > 0) {
+ // Apply any defaults for empty argument values
+ foreach ($emptyArguments as $argumentId => $isArgumentEmpty) {
+ if ($isArgumentEmpty === true) {
+ $reflectedArgumentId = count($args) - (int) $argumentId - 1;
+ if (
+ !array_key_exists($reflectedArgumentId, $methodArguments) ||
+ $methodArguments[$reflectedArgumentId]->isVariadic()
+ ) {
+ break;
+ }
+
+ $args[$argumentId] = $this->getArgumentDefaultValue($methodArguments[$reflectedArgumentId]);
+ }
+ }
+ }
+
+ return $args;
+ }
+
+ /**
+ * @return null|mixed
+ */
+ private function getArgumentDefaultValue(ReflectionParameter $methodArgument)
+ {
+ $defaultValue = null;
+
+ if ($methodArgument->isDefaultValueAvailable()) {
+ $defaultValue = $methodArgument->getDefaultValue();
+ if ($methodArgument->isDefaultValueConstant()) {
+ $constantName = $methodArgument->getDefaultValueConstantName() ?? '';
+ // read constant value
+ if (strpos($constantName, '::') !== false) {
+ [$className, $constantName] = explode('::', $constantName);
+ $constantReflector = new ReflectionClassConstant($className, $constantName);
+
+ return $constantReflector->getValue();
+ }
+
+ return constant($constantName);
+ }
+ }
+
+ return $defaultValue;
+ }
+
/**
* Add cell reference if needed while making sure that it is the last argument.
*
@@ -5228,7 +5465,7 @@ class Calculation
*
* @return array
*/
- private function addCellReference(array $args, $passCellReference, $functionCall, ?Cell $pCell = null)
+ private function addCellReference(array $args, $passCellReference, $functionCall, ?Cell $cell = null)
{
if ($passCellReference) {
if (is_array($functionCall)) {
@@ -5242,7 +5479,7 @@ class Calculation
}
}
- $args[] = $pCell;
+ $args[] = $cell;
}
return $args;
@@ -5273,10 +5510,10 @@ class Calculation
/**
* @return mixed|string
*/
- private function evaluateDefinedName(Cell $pCell, DefinedName $namedRange, Worksheet $pCellWorksheet, Stack $stack)
+ private function evaluateDefinedName(Cell $cell, DefinedName $namedRange, Worksheet $cellWorksheet, Stack $stack)
{
$definedNameScope = $namedRange->getScope();
- if ($definedNameScope !== null && $definedNameScope !== $pCellWorksheet) {
+ if ($definedNameScope !== null && $definedNameScope !== $cellWorksheet) {
// The defined name isn't in our current scope, so #REF
$result = Functions::REF();
$stack->push('Error', $result, $namedRange->getName());
@@ -5294,18 +5531,16 @@ class Calculation
$this->debugLog->writeDebugLog("Defined Name is a {$definedNameType} with a value of {$definedNameValue}");
- $recursiveCalculationCell = ($definedNameWorksheet !== null && $definedNameWorksheet !== $pCellWorksheet)
+ $recursiveCalculationCell = ($definedNameWorksheet !== null && $definedNameWorksheet !== $cellWorksheet)
? $definedNameWorksheet->getCell('A1')
- : $pCell;
- $recursiveCalculationCellAddress = $recursiveCalculationCell !== null
- ? $recursiveCalculationCell->getCoordinate()
- : null;
+ : $cell;
+ $recursiveCalculationCellAddress = $recursiveCalculationCell->getCoordinate();
// Adjust relative references in ranges and formulae so that we execute the calculation for the correct rows and columns
$definedNameValue = self::$referenceHelper->updateFormulaReferencesAnyWorksheet(
$definedNameValue,
- Coordinate::columnIndexFromString($pCell->getColumn()) - 1,
- $pCell->getRow() - 1
+ Coordinate::columnIndexFromString($cell->getColumn()) - 1,
+ $cell->getRow() - 1
);
$this->debugLog->writeDebugLog("Value adjusted for relative references is {$definedNameValue}");
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php
index 2ba4af2dc4c..65031674756 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php
@@ -2,126 +2,11 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
+/**
+ * @deprecated 1.17.0
+ */
class Database
{
- /**
- * fieldExtract.
- *
- * Extracts the column ID to use for the data field.
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param mixed $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- *
- * @return null|string
- */
- private static function fieldExtract($database, $field)
- {
- $field = strtoupper(Functions::flattenSingleValue($field));
- $fieldNames = array_map('strtoupper', array_shift($database));
-
- if (is_numeric($field)) {
- $keys = array_keys($fieldNames);
-
- return $keys[$field - 1];
- }
- $key = array_search($field, $fieldNames);
-
- return ($key) ? $key : null;
- }
-
- /**
- * filter.
- *
- * Parses the selection criteria, extracts the database rows that match those criteria, and
- * returns that subset of rows.
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return array of mixed
- */
- private static function filter($database, $criteria)
- {
- $fieldNames = array_shift($database);
- $criteriaNames = array_shift($criteria);
-
- // Convert the criteria into a set of AND/OR conditions with [:placeholders]
- $testConditions = $testValues = [];
- $testConditionsCount = 0;
- foreach ($criteriaNames as $key => $criteriaName) {
- $testCondition = [];
- $testConditionCount = 0;
- foreach ($criteria as $row => $criterion) {
- if ($criterion[$key] > '') {
- $testCondition[] = '[:' . $criteriaName . ']' . Functions::ifCondition($criterion[$key]);
- ++$testConditionCount;
- }
- }
- if ($testConditionCount > 1) {
- $testConditions[] = 'OR(' . implode(',', $testCondition) . ')';
- ++$testConditionsCount;
- } elseif ($testConditionCount == 1) {
- $testConditions[] = $testCondition[0];
- ++$testConditionsCount;
- }
- }
-
- if ($testConditionsCount > 1) {
- $testConditionSet = 'AND(' . implode(',', $testConditions) . ')';
- } elseif ($testConditionsCount == 1) {
- $testConditionSet = $testConditions[0];
- }
-
- // Loop through each row of the database
- foreach ($database as $dataRow => $dataValues) {
- // Substitute actual values from the database row for our [:placeholders]
- $testConditionList = $testConditionSet;
- foreach ($criteriaNames as $key => $criteriaName) {
- $k = array_search($criteriaName, $fieldNames);
- if (isset($dataValues[$k])) {
- $dataValue = $dataValues[$k];
- $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
- $testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList);
- }
- }
- // evaluate the criteria against the row data
- $result = Calculation::getInstance()->_calculateFormulaValue('=' . $testConditionList);
- // If the row failed to meet the criteria, remove it from the database
- if (!$result) {
- unset($database[$dataRow]);
- }
- }
-
- return $database;
- }
-
- private static function getFilteredColumn($database, $field, $criteria)
- {
- // reduce the database to a set of rows that match all the criteria
- $database = self::filter($database, $criteria);
- // extract an array of values for the requested column
- $colData = [];
- foreach ($database as $row) {
- $colData[] = $row[$field];
- }
-
- return $colData;
- }
-
/**
* DAVERAGE.
*
@@ -130,6 +15,11 @@ class Database
* Excel Function:
* DAVERAGE(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DAverage::evaluate()
+ * Use the evaluate() method in the Database\DAverage class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -145,19 +35,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float|string
+ * @return null|float|string
*/
public static function DAVERAGE($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::AVERAGE(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DAverage::evaluate($database, $field, $criteria);
}
/**
@@ -169,14 +51,16 @@ class Database
* Excel Function:
* DCOUNT(database,[field],criteria)
*
- * Excel Function:
- * DAVERAGE(database,field,criteria)
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DCount::evaluate()
+ * Use the evaluate() method in the Database\DCount class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
+ * @param null|int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
@@ -194,15 +78,7 @@ class Database
*/
public static function DCOUNT($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::COUNT(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DCount::evaluate($database, $field, $criteria);
}
/**
@@ -213,11 +89,16 @@ class Database
* Excel Function:
* DCOUNTA(database,[field],criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DCountA::evaluate()
+ * Use the evaluate() method in the Database\DCountA class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
+ * @param null|int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
@@ -229,29 +110,10 @@ class Database
* column.
*
* @return int
- *
- * @TODO The field argument is optional. If field is omitted, DCOUNTA counts all records in the
- * database that match the criteria.
*/
public static function DCOUNTA($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // reduce the database to a set of rows that match all the criteria
- $database = self::filter($database, $criteria);
- // extract an array of values for the requested column
- $colData = [];
- foreach ($database as $row) {
- $colData[] = $row[$field];
- }
-
- // Return
- return Statistical::COUNTA(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DCountA::evaluate($database, $field, $criteria);
}
/**
@@ -263,6 +125,11 @@ class Database
* Excel Function:
* DGET(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DGet::evaluate()
+ * Use the evaluate() method in the Database\DGet class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -282,18 +149,7 @@ class Database
*/
public static function DGET($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- $colData = self::getFilteredColumn($database, $field, $criteria);
- if (count($colData) > 1) {
- return Functions::NAN();
- }
-
- return $colData[0];
+ return Database\DGet::evaluate($database, $field, $criteria);
}
/**
@@ -305,6 +161,11 @@ class Database
* Excel Function:
* DMAX(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DMax::evaluate()
+ * Use the evaluate() method in the Database\DMax class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -324,15 +185,7 @@ class Database
*/
public static function DMAX($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::MAX(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DMax::evaluate($database, $field, $criteria);
}
/**
@@ -344,6 +197,11 @@ class Database
* Excel Function:
* DMIN(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DMin::evaluate()
+ * Use the evaluate() method in the Database\DMin class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -363,15 +221,7 @@ class Database
*/
public static function DMIN($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::MIN(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DMin::evaluate($database, $field, $criteria);
}
/**
@@ -382,6 +232,11 @@ class Database
* Excel Function:
* DPRODUCT(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DProduct::evaluate()
+ * Use the evaluate() method in the Database\DProduct class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -397,19 +252,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return float|string
*/
public static function DPRODUCT($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return MathTrig::PRODUCT(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DProduct::evaluate($database, $field, $criteria);
}
/**
@@ -421,6 +268,11 @@ class Database
* Excel Function:
* DSTDEV(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DStDev::evaluate()
+ * Use the evaluate() method in the Database\DStDev class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -440,15 +292,7 @@ class Database
*/
public static function DSTDEV($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::STDEV(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DStDev::evaluate($database, $field, $criteria);
}
/**
@@ -460,6 +304,11 @@ class Database
* Excel Function:
* DSTDEVP(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DStDevP::evaluate()
+ * Use the evaluate() method in the Database\DStDevP class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -479,15 +328,7 @@ class Database
*/
public static function DSTDEVP($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::STDEVP(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DStDevP::evaluate($database, $field, $criteria);
}
/**
@@ -498,6 +339,11 @@ class Database
* Excel Function:
* DSUM(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DSum::evaluate()
+ * Use the evaluate() method in the Database\DSum class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -513,19 +359,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return float|string
*/
public static function DSUM($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return MathTrig::SUM(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DSum::evaluate($database, $field, $criteria);
}
/**
@@ -537,6 +375,11 @@ class Database
* Excel Function:
* DVAR(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DVar::evaluate()
+ * Use the evaluate() method in the Database\DVar class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -556,15 +399,7 @@ class Database
*/
public static function DVAR($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::VARFunc(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DVar::evaluate($database, $field, $criteria);
}
/**
@@ -576,6 +411,11 @@ class Database
* Excel Function:
* DVARP(database,field,criteria)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Database\DVarP::evaluate()
+ * Use the evaluate() method in the Database\DVarP class instead
+ *
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@@ -595,14 +435,6 @@ class Database
*/
public static function DVARP($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::VARP(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DVarP::evaluate($database, $field, $criteria);
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php
new file mode 100644
index 00000000000..e30842dc5e9
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php
@@ -0,0 +1,45 @@
+ 1) {
+ return Functions::NAN();
+ }
+
+ $row = array_pop($columnData);
+
+ return array_pop($row);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php
new file mode 100644
index 00000000000..9c5c7301e9d
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php
@@ -0,0 +1,46 @@
+ $row) {
+ $keys = array_keys($row);
+ $key = $keys[$field] ?? null;
+ $columnKey = $key ?? 'A';
+ $columnData[$rowKey][$columnKey] = $row[$key] ?? $defaultReturnColumnValue;
+ }
+
+ return $columnData;
+ }
+
+ private static function buildQuery(array $criteriaNames, array $criteria): string
+ {
+ $baseQuery = [];
+ foreach ($criteria as $key => $criterion) {
+ foreach ($criterion as $field => $value) {
+ $criterionName = $criteriaNames[$field];
+ if ($value !== null) {
+ $condition = self::buildCondition($value, $criterionName);
+ $baseQuery[$key][] = $condition;
+ }
+ }
+ }
+
+ $rowQuery = array_map(
+ function ($rowValue) {
+ return (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? '');
+ },
+ $baseQuery
+ );
+
+ return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : ($rowQuery[0] ?? '');
+ }
+
+ private static function buildCondition($criterion, string $criterionName): string
+ {
+ $ifCondition = Functions::ifCondition($criterion);
+
+ // Check for wildcard characters used in the condition
+ $result = preg_match('/(?[^"]*)(?".*[*?].*")/ui', $ifCondition, $matches);
+ if ($result !== 1) {
+ return "[:{$criterionName}]{$ifCondition}";
+ }
+
+ $trueFalse = ($matches['operator'] !== '<>');
+ $wildcard = WildcardMatch::wildcard($matches['operand']);
+ $condition = "WILDCARDMATCH([:{$criterionName}],{$wildcard})";
+ if ($trueFalse === false) {
+ $condition = "NOT({$condition})";
+ }
+
+ return $condition;
+ }
+
+ private static function executeQuery(array $database, string $query, array $criteria, array $fields): array
+ {
+ foreach ($database as $dataRow => $dataValues) {
+ // Substitute actual values from the database row for our [:placeholders]
+ $conditions = $query;
+ foreach ($criteria as $criterion) {
+ $conditions = self::processCondition($criterion, $fields, $dataValues, $conditions);
+ }
+
+ // evaluate the criteria against the row data
+ $result = Calculation::getInstance()->_calculateFormulaValue('=' . $conditions);
+
+ // If the row failed to meet the criteria, remove it from the database
+ if ($result !== true) {
+ unset($database[$dataRow]);
+ }
+ }
+
+ return $database;
+ }
+
+ private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions)
+ {
+ $key = array_search($criterion, $fields, true);
+
+ $dataValue = 'NULL';
+ if (is_bool($dataValues[$key])) {
+ $dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
+ } elseif ($dataValues[$key] !== null) {
+ $dataValue = $dataValues[$key];
+ // escape quotes if we have a string containing quotes
+ if (is_string($dataValue) && strpos($dataValue, '"') !== false) {
+ $dataValue = str_replace('"', '""', $dataValue);
+ }
+ $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
+ }
+
+ return str_replace('[:' . $criterion . ']', $dataValue, $conditions);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
index 4c2b108ad9f..44a38c19e01 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
@@ -2,127 +2,49 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use DateTimeImmutable;
use DateTimeInterface;
-use PhpOffice\PhpSpreadsheet\Shared\Date;
-use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+/**
+ * @deprecated 1.18.0
+ */
class DateTime
{
/**
* Identify if a year is a leap year or not.
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Helpers::isLeapYear()
+ * Use the isLeapYear method in the DateTimeExcel\Helpers class instead
+ *
* @param int|string $year The year to test
*
* @return bool TRUE if the year is a leap year, otherwise FALSE
*/
public static function isLeapYear($year)
{
- return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
- }
-
- /**
- * Return the number of days between two dates based on a 360 day calendar.
- *
- * @param int $startDay Day of month of the start date
- * @param int $startMonth Month of the start date
- * @param int $startYear Year of the start date
- * @param int $endDay Day of month of the start date
- * @param int $endMonth Month of the start date
- * @param int $endYear Year of the start date
- * @param bool $methodUS Whether to use the US method or the European method of calculation
- *
- * @return int Number of days between the start date and the end date
- */
- private static function dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS)
- {
- if ($startDay == 31) {
- --$startDay;
- } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::isLeapYear($startYear))))) {
- $startDay = 30;
- }
- if ($endDay == 31) {
- if ($methodUS && $startDay != 30) {
- $endDay = 1;
- if ($endMonth == 12) {
- ++$endYear;
- $endMonth = 1;
- } else {
- ++$endMonth;
- }
- } else {
- $endDay = 30;
- }
- }
-
- return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
+ return DateTimeExcel\Helpers::isLeapYear($year);
}
/**
* getDateValue.
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Helpers::getDateValue()
+ * Use the getDateValue method in the DateTimeExcel\Helpers class instead
+ *
* @param mixed $dateValue
*
* @return mixed Excel date/time serial value, or string if error
*/
public static function getDateValue($dateValue)
{
- if (!is_numeric($dateValue)) {
- if ((is_object($dateValue)) && ($dateValue instanceof DateTimeInterface)) {
- $dateValue = Date::PHPToExcel($dateValue);
- } else {
- $saveReturnDateType = Functions::getReturnDateType();
- Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
- $dateValue = self::DATEVALUE($dateValue);
- Functions::setReturnDateType($saveReturnDateType);
- }
+ try {
+ return DateTimeExcel\Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
}
-
- return $dateValue;
- }
-
- /**
- * getTimeValue.
- *
- * @param string $timeValue
- *
- * @return mixed Excel date/time serial value, or string if error
- */
- private static function getTimeValue($timeValue)
- {
- $saveReturnDateType = Functions::getReturnDateType();
- Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
- $timeValue = self::TIMEVALUE($timeValue);
- Functions::setReturnDateType($saveReturnDateType);
-
- return $timeValue;
- }
-
- private static function adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0)
- {
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
- $oMonth = (int) $PHPDateObject->format('m');
- $oYear = (int) $PHPDateObject->format('Y');
-
- $adjustmentMonthsString = (string) $adjustmentMonths;
- if ($adjustmentMonths > 0) {
- $adjustmentMonthsString = '+' . $adjustmentMonths;
- }
- if ($adjustmentMonths != 0) {
- $PHPDateObject->modify($adjustmentMonthsString . ' months');
- }
- $nMonth = (int) $PHPDateObject->format('m');
- $nYear = (int) $PHPDateObject->format('Y');
-
- $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
- if ($monthDiff != $adjustmentMonths) {
- $adjustDays = (int) $PHPDateObject->format('d');
- $adjustDaysString = '-' . $adjustDays . ' days';
- $PHPDateObject->modify($adjustDaysString);
- }
-
- return $PHPDateObject;
}
/**
@@ -139,31 +61,17 @@ class DateTime
* Excel Function:
* NOW()
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Current::now()
+ * Use the now method in the DateTimeExcel\Current class instead
+ *
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function DATETIMENOW()
{
- $saveTimeZone = date_default_timezone_get();
- date_default_timezone_set('UTC');
- $retValue = false;
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $retValue = (float) Date::PHPToExcel(time());
-
- break;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- $retValue = (int) time();
-
- break;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- $retValue = new \DateTime();
-
- break;
- }
- date_default_timezone_set($saveTimeZone);
-
- return $retValue;
+ return DateTimeExcel\Current::now();
}
/**
@@ -180,32 +88,17 @@ class DateTime
* Excel Function:
* TODAY()
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Current::today()
+ * Use the today method in the DateTimeExcel\Current class instead
+ *
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function DATENOW()
{
- $saveTimeZone = date_default_timezone_get();
- date_default_timezone_set('UTC');
- $retValue = false;
- $excelDateTime = floor(Date::PHPToExcel(time()));
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $retValue = (float) $excelDateTime;
-
- break;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- $retValue = (int) Date::excelToTimestamp($excelDateTime);
-
- break;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- $retValue = Date::excelToDateTimeObject($excelDateTime);
-
- break;
- }
- date_default_timezone_set($saveTimeZone);
-
- return $retValue;
+ return DateTimeExcel\Current::today();
}
/**
@@ -216,9 +109,15 @@ class DateTime
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
+ *
* Excel Function:
* DATE(year,month,day)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Date::fromYMD()
+ * Use the fromYMD method in the DateTimeExcel\Date class instead
+ *
* PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
* A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
* as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
@@ -259,71 +158,7 @@ class DateTime
*/
public static function DATE($year = 0, $month = 1, $day = 1)
{
- $year = Functions::flattenSingleValue($year);
- $month = Functions::flattenSingleValue($month);
- $day = Functions::flattenSingleValue($day);
-
- if (($month !== null) && (!is_numeric($month))) {
- $month = Date::monthStringToNumber($month);
- }
-
- if (($day !== null) && (!is_numeric($day))) {
- $day = Date::dayStringToNumber($day);
- }
-
- $year = ($year !== null) ? StringHelper::testStringAsNumeric($year) : 0;
- $month = ($month !== null) ? StringHelper::testStringAsNumeric($month) : 0;
- $day = ($day !== null) ? StringHelper::testStringAsNumeric($day) : 0;
- if (
- (!is_numeric($year)) ||
- (!is_numeric($month)) ||
- (!is_numeric($day))
- ) {
- return Functions::VALUE();
- }
- $year = (int) $year;
- $month = (int) $month;
- $day = (int) $day;
-
- $baseYear = Date::getExcelCalendar();
- // Validate parameters
- if ($year < ($baseYear - 1900)) {
- return Functions::NAN();
- }
- if ((($baseYear - 1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
- return Functions::NAN();
- }
-
- if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
- $year += 1900;
- }
-
- if ($month < 1) {
- // Handle year/month adjustment if month < 1
- --$month;
- $year += ceil($month / 12) - 1;
- $month = 13 - abs($month % 12);
- } elseif ($month > 12) {
- // Handle year/month adjustment if month > 12
- $year += floor($month / 12);
- $month = ($month % 12);
- }
-
- // Re-validate the year parameter after adjustments
- if (($year < $baseYear) || ($year >= 10000)) {
- return Functions::NAN();
- }
-
- // Execute function
- $excelDateValue = Date::formattedPHPToExcel($year, $month, $day);
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($excelDateValue);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return Date::excelToDateTimeObject($excelDateValue);
- }
+ return DateTimeExcel\Date::fromYMD($year, $month, $day);
}
/**
@@ -337,6 +172,11 @@ class DateTime
* Excel Function:
* TIME(hour,minute,second)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Time::fromHMS()
+ * Use the fromHMS method in the DateTimeExcel\Time class instead
+ *
* @param int $hour A number from 0 (zero) to 32767 representing the hour.
* Any value greater than 23 will be divided by 24 and the remainder
* will be treated as the hour value. For example, TIME(27,0,0) =
@@ -354,85 +194,7 @@ class DateTime
*/
public static function TIME($hour = 0, $minute = 0, $second = 0)
{
- $hour = Functions::flattenSingleValue($hour);
- $minute = Functions::flattenSingleValue($minute);
- $second = Functions::flattenSingleValue($second);
-
- if ($hour == '') {
- $hour = 0;
- }
- if ($minute == '') {
- $minute = 0;
- }
- if ($second == '') {
- $second = 0;
- }
-
- if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
- return Functions::VALUE();
- }
- $hour = (int) $hour;
- $minute = (int) $minute;
- $second = (int) $second;
-
- if ($second < 0) {
- $minute += floor($second / 60);
- $second = 60 - abs($second % 60);
- if ($second == 60) {
- $second = 0;
- }
- } elseif ($second >= 60) {
- $minute += floor($second / 60);
- $second = $second % 60;
- }
- if ($minute < 0) {
- $hour += floor($minute / 60);
- $minute = 60 - abs($minute % 60);
- if ($minute == 60) {
- $minute = 0;
- }
- } elseif ($minute >= 60) {
- $hour += floor($minute / 60);
- $minute = $minute % 60;
- }
-
- if ($hour > 23) {
- $hour = $hour % 24;
- } elseif ($hour < 0) {
- return Functions::NAN();
- }
-
- // Execute function
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $date = 0;
- $calendar = Date::getExcelCalendar();
- if ($calendar != Date::CALENDAR_WINDOWS_1900) {
- $date = 1;
- }
-
- return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- $dayAdjust = 0;
- if ($hour < 0) {
- $dayAdjust = floor($hour / 24);
- $hour = 24 - abs($hour % 24);
- if ($hour == 24) {
- $hour = 0;
- }
- } elseif ($hour >= 24) {
- $dayAdjust = floor($hour / 24);
- $hour = $hour % 24;
- }
- $phpDateObject = new \DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
- if ($dayAdjust != 0) {
- $phpDateObject->modify($dayAdjust . ' days');
- }
-
- return $phpDateObject;
- }
+ return DateTimeExcel\Time::fromHMS($hour, $minute, $second);
}
/**
@@ -448,6 +210,11 @@ class DateTime
* Excel Function:
* DATEVALUE(dateValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\DateValue::fromString()
+ * Use the fromString method in the DateTimeExcel\DateValue class instead
+ *
* @param string $dateValue Text that represents a date in a Microsoft Excel date format.
* For example, "1/30/2008" or "30-Jan-2008" are text strings within
* quotation marks that represent dates. Using the default date
@@ -460,112 +227,9 @@ class DateTime
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
- public static function DATEVALUE($dateValue = 1)
+ public static function DATEVALUE($dateValue)
{
- $dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
- // Strip any ordinals because they're allowed in Excel (English only)
- $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
- // Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
- $dateValue = str_replace(['/', '.', '-', ' '], ' ', $dateValue);
-
- $yearFound = false;
- $t1 = explode(' ', $dateValue);
- foreach ($t1 as &$t) {
- if ((is_numeric($t)) && ($t > 31)) {
- if ($yearFound) {
- return Functions::VALUE();
- }
- if ($t < 100) {
- $t += 1900;
- }
- $yearFound = true;
- }
- }
- if ((count($t1) == 1) && (strpos($t, ':') !== false)) {
- // We've been fed a time value without any date
- return 0.0;
- } elseif (count($t1) == 2) {
- // We only have two parts of the date: either day/month or month/year
- if ($yearFound) {
- array_unshift($t1, 1);
- } else {
- if (is_numeric($t1[1]) && $t1[1] > 29) {
- $t1[1] += 1900;
- array_unshift($t1, 1);
- } else {
- $t1[] = date('Y');
- }
- }
- }
- unset($t);
- $dateValue = implode(' ', $t1);
-
- $PHPDateArray = date_parse($dateValue);
- if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
- $testVal1 = strtok($dateValue, '- ');
- if ($testVal1 !== false) {
- $testVal2 = strtok('- ');
- if ($testVal2 !== false) {
- $testVal3 = strtok('- ');
- if ($testVal3 === false) {
- $testVal3 = strftime('%Y');
- }
- } else {
- return Functions::VALUE();
- }
- } else {
- return Functions::VALUE();
- }
- if ($testVal1 < 31 && $testVal2 < 12 && $testVal3 < 12 && strlen($testVal3) == 2) {
- $testVal3 += 2000;
- }
- $PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
- if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
- $PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
- if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
- return Functions::VALUE();
- }
- }
- }
-
- if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
- // Execute function
- if ($PHPDateArray['year'] == '') {
- $PHPDateArray['year'] = strftime('%Y');
- }
- if ($PHPDateArray['year'] < 1900) {
- return Functions::VALUE();
- }
- if ($PHPDateArray['month'] == '') {
- $PHPDateArray['month'] = strftime('%m');
- }
- if ($PHPDateArray['day'] == '') {
- $PHPDateArray['day'] = strftime('%d');
- }
- if (!checkdate($PHPDateArray['month'], $PHPDateArray['day'], $PHPDateArray['year'])) {
- return Functions::VALUE();
- }
- $excelDateValue = floor(
- Date::formattedPHPToExcel(
- $PHPDateArray['year'],
- $PHPDateArray['month'],
- $PHPDateArray['day'],
- $PHPDateArray['hour'],
- $PHPDateArray['minute'],
- $PHPDateArray['second']
- )
- );
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($excelDateValue);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return new \DateTime($PHPDateArray['year'] . '-' . $PHPDateArray['month'] . '-' . $PHPDateArray['day'] . ' 00:00:00');
- }
- }
-
- return Functions::VALUE();
+ return DateTimeExcel\DateValue::fromString($dateValue);
}
/**
@@ -581,6 +245,11 @@ class DateTime
* Excel Function:
* TIMEVALUE(timeValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\TimeValue::fromString()
+ * Use the fromString method in the DateTimeExcel\TimeValue class instead
+ *
* @param string $timeValue A text string that represents a time in any one of the Microsoft
* Excel time formats; for example, "6:45 PM" and "18:45" text strings
* within quotation marks that represent time.
@@ -591,46 +260,20 @@ class DateTime
*/
public static function TIMEVALUE($timeValue)
{
- $timeValue = trim(Functions::flattenSingleValue($timeValue), '"');
- $timeValue = str_replace(['/', '.'], '-', $timeValue);
-
- $arraySplit = preg_split('/[\/:\-\s]/', $timeValue);
- if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
- $arraySplit[0] = ($arraySplit[0] % 24);
- $timeValue = implode(':', $arraySplit);
- }
-
- $PHPDateArray = date_parse($timeValue);
- if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $excelDateValue = Date::formattedPHPToExcel(
- $PHPDateArray['year'],
- $PHPDateArray['month'],
- $PHPDateArray['day'],
- $PHPDateArray['hour'],
- $PHPDateArray['minute'],
- $PHPDateArray['second']
- );
- } else {
- $excelDateValue = Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
- }
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return new \DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
- }
- }
-
- return Functions::VALUE();
+ return DateTimeExcel\TimeValue::fromString($timeValue);
}
/**
* DATEDIF.
*
+ * Excel Function:
+ * DATEDIF(startdate, enddate, unit)
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Difference::interval()
+ * Use the interval method in the DateTimeExcel\Difference class instead
+ *
* @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
* or a standard date string
* @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
@@ -641,95 +284,7 @@ class DateTime
*/
public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
{
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- $unit = strtoupper(Functions::flattenSingleValue($unit));
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
-
- // Validate parameters
- if ($startDate > $endDate) {
- return Functions::NAN();
- }
-
- // Execute function
- $difference = $endDate - $startDate;
-
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $startDays = $PHPStartDateObject->format('j');
- $startMonths = $PHPStartDateObject->format('n');
- $startYears = $PHPStartDateObject->format('Y');
-
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
- $endDays = $PHPEndDateObject->format('j');
- $endMonths = $PHPEndDateObject->format('n');
- $endYears = $PHPEndDateObject->format('Y');
-
- $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
-
- switch ($unit) {
- case 'D':
- $retVal = (int) $difference;
-
- break;
- case 'M':
- $retVal = (int) 12 * $PHPDiffDateObject->format('%y') + $PHPDiffDateObject->format('%m');
-
- break;
- case 'Y':
- $retVal = (int) $PHPDiffDateObject->format('%y');
-
- break;
- case 'MD':
- if ($endDays < $startDays) {
- $retVal = $endDays;
- $PHPEndDateObject->modify('-' . $endDays . ' days');
- $adjustDays = $PHPEndDateObject->format('j');
- $retVal += ($adjustDays - $startDays);
- } else {
- $retVal = (int) $PHPDiffDateObject->format('%d');
- }
-
- break;
- case 'YM':
- $retVal = (int) $PHPDiffDateObject->format('%m');
-
- break;
- case 'YD':
- $retVal = (int) $difference;
- if ($endYears > $startYears) {
- $isLeapStartYear = $PHPStartDateObject->format('L');
- $wasLeapEndYear = $PHPEndDateObject->format('L');
-
- // Adjust end year to be as close as possible as start year
- while ($PHPEndDateObject >= $PHPStartDateObject) {
- $PHPEndDateObject->modify('-1 year');
- $endYears = $PHPEndDateObject->format('Y');
- }
- $PHPEndDateObject->modify('+1 year');
-
- // Get the result
- $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
-
- // Adjust for leap years cases
- $isLeapEndYear = $PHPEndDateObject->format('L');
- $limit = new \DateTime($PHPEndDateObject->format('Y-02-29'));
- if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
- --$retVal;
- }
- }
-
- break;
- default:
- $retVal = Functions::VALUE();
- }
-
- return $retVal;
+ return DateTimeExcel\Difference::interval($startDate, $endDate, $unit);
}
/**
@@ -740,40 +295,21 @@ class DateTime
* Excel Function:
* DAYS(endDate, startDate)
*
- * @param DateTimeImmutable|float|int|string $endDate Excel date serial value (float),
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Days::between()
+ * Use the between method in the DateTimeExcel\Days class instead
+ *
+ * @param DateTimeInterface|float|int|string $endDate Excel date serial value (float),
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
- * @param DateTimeImmutable|float|int|string $startDate Excel date serial value (float),
+ * @param DateTimeInterface|float|int|string $startDate Excel date serial value (float),
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
*
* @return int|string Number of days between start date and end date or an error
*/
public static function DAYS($endDate = 0, $startDate = 0)
{
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
-
- $startDate = self::getDateValue($startDate);
- if (is_string($startDate)) {
- return Functions::VALUE();
- }
-
- $endDate = self::getDateValue($endDate);
- if (is_string($endDate)) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
-
- $diff = $PHPStartDateObject->diff($PHPEndDateObject);
- $days = $diff->days;
-
- if ($diff->invert) {
- $days = -$days;
- }
-
- return $days;
+ return DateTimeExcel\Days::between($endDate, $startDate);
}
/**
@@ -786,6 +322,11 @@ class DateTime
* Excel Function:
* DAYS360(startDate,endDate[,method])
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Days360::between()
+ * Use the between method in the DateTimeExcel\Days360 class instead
+ *
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
@@ -806,32 +347,7 @@ class DateTime
*/
public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
{
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
-
- if (!is_bool($method)) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $startDay = $PHPStartDateObject->format('j');
- $startMonth = $PHPStartDateObject->format('n');
- $startYear = $PHPStartDateObject->format('Y');
-
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
- $endDay = $PHPEndDateObject->format('j');
- $endMonth = $PHPEndDateObject->format('n');
- $endYear = $PHPEndDateObject->format('Y');
-
- return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
+ return DateTimeExcel\Days360::between($startDate, $endDate, $method);
}
/**
@@ -844,6 +360,12 @@ class DateTime
*
* Excel Function:
* YEARFRAC(startDate,endDate[,method])
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\YearFrac::fraction()
+ * Use the fraction method in the DateTimeExcel\YearFrac class instead
+ *
* See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
* for description of algorithm used in Excel
*
@@ -862,78 +384,7 @@ class DateTime
*/
public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
{
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- $method = Functions::flattenSingleValue($method);
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
- if ($startDate > $endDate) {
- $temp = $startDate;
- $startDate = $endDate;
- $endDate = $temp;
- }
-
- if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
- switch ($method) {
- case 0:
- return self::DAYS360($startDate, $endDate) / 360;
- case 1:
- $days = self::DATEDIF($startDate, $endDate);
- $startYear = self::YEAR($startDate);
- $endYear = self::YEAR($endDate);
- $years = $endYear - $startYear + 1;
- $startMonth = self::MONTHOFYEAR($startDate);
- $startDay = self::DAYOFMONTH($startDate);
- $endMonth = self::MONTHOFYEAR($endDate);
- $endDay = self::DAYOFMONTH($endDate);
- $startMonthDay = 100 * $startMonth + $startDay;
- $endMonthDay = 100 * $endMonth + $endDay;
- if ($years == 1) {
- if (self::isLeapYear($endYear)) {
- $tmpCalcAnnualBasis = 366;
- } else {
- $tmpCalcAnnualBasis = 365;
- }
- } elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
- if (self::isLeapYear($startYear)) {
- if ($startMonthDay <= 229) {
- $tmpCalcAnnualBasis = 366;
- } else {
- $tmpCalcAnnualBasis = 365;
- }
- } elseif (self::isLeapYear($endYear)) {
- if ($endMonthDay >= 229) {
- $tmpCalcAnnualBasis = 366;
- } else {
- $tmpCalcAnnualBasis = 365;
- }
- } else {
- $tmpCalcAnnualBasis = 365;
- }
- } else {
- $tmpCalcAnnualBasis = 0;
- for ($year = $startYear; $year <= $endYear; ++$year) {
- $tmpCalcAnnualBasis += self::isLeapYear($year) ? 366 : 365;
- }
- $tmpCalcAnnualBasis /= $years;
- }
-
- return $days / $tmpCalcAnnualBasis;
- case 2:
- return self::DATEDIF($startDate, $endDate) / 360;
- case 3:
- return self::DATEDIF($startDate, $endDate) / 365;
- case 4:
- return self::DAYS360($startDate, $endDate, true) / 360;
- }
- }
-
- return Functions::VALUE();
+ return DateTimeExcel\YearFrac::fraction($startDate, $endDate, $method);
}
/**
@@ -947,71 +398,22 @@ class DateTime
* Excel Function:
* NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\NetworkDays::count()
+ * Use the count method in the DateTimeExcel\NetworkDays class instead
+ *
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
+ * @param mixed $dateArgs
*
* @return int|string Interval between the dates
*/
public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
{
- // Retrieve the mandatory start and end date that are referenced in the function definition
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- // Get the optional days
- $dateArgs = Functions::flattenArray($dateArgs);
-
- // Validate the start and end dates
- if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- $startDate = (float) floor($startDate);
- if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
- $endDate = (float) floor($endDate);
-
- if ($sDate > $eDate) {
- $startDate = $eDate;
- $endDate = $sDate;
- }
-
- // Execute function
- $startDoW = 6 - self::WEEKDAY($startDate, 2);
- if ($startDoW < 0) {
- $startDoW = 0;
- }
- $endDoW = self::WEEKDAY($endDate, 2);
- if ($endDoW >= 6) {
- $endDoW = 0;
- }
-
- $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
- $partWeekDays = $endDoW + $startDoW;
- if ($partWeekDays > 5) {
- $partWeekDays -= 5;
- }
-
- // Test any extra holiday parameters
- $holidayCountedArray = [];
- foreach ($dateArgs as $holidayDate) {
- if (is_string($holidayDate = self::getDateValue($holidayDate))) {
- return Functions::VALUE();
- }
- if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
- if ((self::WEEKDAY($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
- --$partWeekDays;
- $holidayCountedArray[] = $holidayDate;
- }
- }
- }
-
- if ($sDate > $eDate) {
- return 0 - ($wholeWeekDays + $partWeekDays);
- }
-
- return $wholeWeekDays + $partWeekDays;
+ return DateTimeExcel\NetworkDays::count($startDate, $endDate, ...$dateArgs);
}
/**
@@ -1025,102 +427,24 @@ class DateTime
* Excel Function:
* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\WorkDay::date()
+ * Use the date method in the DateTimeExcel\WorkDay class instead
+ *
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $endDays The number of nonweekend and nonholiday days before or after
* startDate. A positive value for days yields a future date; a
* negative value yields a past date.
+ * @param mixed $dateArgs
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function WORKDAY($startDate, $endDays, ...$dateArgs)
{
- // Retrieve the mandatory start date and days that are referenced in the function definition
- $startDate = Functions::flattenSingleValue($startDate);
- $endDays = Functions::flattenSingleValue($endDays);
- // Get the optional days
- $dateArgs = Functions::flattenArray($dateArgs);
-
- if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
- return Functions::VALUE();
- }
- $startDate = (float) floor($startDate);
- $endDays = (int) floor($endDays);
- // If endDays is 0, we always return startDate
- if ($endDays == 0) {
- return $startDate;
- }
-
- $decrementing = $endDays < 0;
-
- // Adjust the start date if it falls over a weekend
-
- $startDoW = self::WEEKDAY($startDate, 3);
- if (self::WEEKDAY($startDate, 3) >= 5) {
- $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
- ($decrementing) ? $endDays++ : $endDays--;
- }
-
- // Add endDays
- $endDate = (float) $startDate + ((int) ($endDays / 5) * 7) + ($endDays % 5);
-
- // Adjust the calculated end date if it falls over a weekend
- $endDoW = self::WEEKDAY($endDate, 3);
- if ($endDoW >= 5) {
- $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
- }
-
- // Test any extra holiday parameters
- if (!empty($dateArgs)) {
- $holidayCountedArray = $holidayDates = [];
- foreach ($dateArgs as $holidayDate) {
- if (($holidayDate !== null) && (trim($holidayDate) > '')) {
- if (is_string($holidayDate = self::getDateValue($holidayDate))) {
- return Functions::VALUE();
- }
- if (self::WEEKDAY($holidayDate, 3) < 5) {
- $holidayDates[] = $holidayDate;
- }
- }
- }
- if ($decrementing) {
- rsort($holidayDates, SORT_NUMERIC);
- } else {
- sort($holidayDates, SORT_NUMERIC);
- }
- foreach ($holidayDates as $holidayDate) {
- if ($decrementing) {
- if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
- if (!in_array($holidayDate, $holidayCountedArray)) {
- --$endDate;
- $holidayCountedArray[] = $holidayDate;
- }
- }
- } else {
- if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
- if (!in_array($holidayDate, $holidayCountedArray)) {
- ++$endDate;
- $holidayCountedArray[] = $holidayDate;
- }
- }
- }
- // Adjust the calculated end date if it falls over a weekend
- $endDoW = self::WEEKDAY($endDate, 3);
- if ($endDoW >= 5) {
- $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
- }
- }
- }
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $endDate;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($endDate);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return Date::excelToDateTimeObject($endDate);
- }
+ return DateTimeExcel\WorkDay::date($startDate, $endDays, ...$dateArgs);
}
/**
@@ -1132,6 +456,11 @@ class DateTime
* Excel Function:
* DAY(dateValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\DateParts::day()
+ * Use the day method in the DateTimeExcel\DateParts class instead
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
@@ -1139,26 +468,7 @@ class DateTime
*/
public static function DAYOFMONTH($dateValue = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
- if ($dateValue < 0.0) {
- return Functions::NAN();
- } elseif ($dateValue < 1.0) {
- return 0;
- }
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('j');
+ return DateTimeExcel\DateParts::day($dateValue);
}
/**
@@ -1170,7 +480,12 @@ class DateTime
* Excel Function:
* WEEKDAY(dateValue[,style])
*
- * @param int $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Week::day()
+ * Use the day method in the DateTimeExcel\Week class instead
+ *
+ * @param float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $style A number that determines the type of return value
* 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
@@ -1181,79 +496,169 @@ class DateTime
*/
public static function WEEKDAY($dateValue = 1, $style = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
- $style = Functions::flattenSingleValue($style);
-
- if (!is_numeric($style)) {
- return Functions::VALUE();
- } elseif (($style < 1) || ($style > 3)) {
- return Functions::NAN();
- }
- $style = floor($style);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
- $DoW = (int) $PHPDateObject->format('w');
-
- $firstDay = 1;
- switch ($style) {
- case 1:
- ++$DoW;
-
- break;
- case 2:
- if ($DoW === 0) {
- $DoW = 7;
- }
-
- break;
- case 3:
- if ($DoW === 0) {
- $DoW = 7;
- }
- $firstDay = 0;
- --$DoW;
-
- break;
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
- // Test for Excel's 1900 leap year, and introduce the error as required
- if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
- --$DoW;
- if ($DoW < $firstDay) {
- $DoW += 7;
- }
- }
- }
-
- return $DoW;
+ return DateTimeExcel\Week::day($dateValue, $style);
}
+ /**
+ * STARTWEEK_SUNDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_SUNDAY instead
+ */
const STARTWEEK_SUNDAY = 1;
+
+ /**
+ * STARTWEEK_MONDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_MONDAY instead
+ */
const STARTWEEK_MONDAY = 2;
+
+ /**
+ * STARTWEEK_MONDAY_ALT.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_MONDAY_ALT instead
+ */
const STARTWEEK_MONDAY_ALT = 11;
+
+ /**
+ * STARTWEEK_TUESDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_TUESDAY instead
+ */
const STARTWEEK_TUESDAY = 12;
+
+ /**
+ * STARTWEEK_WEDNESDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_WEDNESDAY instead
+ */
const STARTWEEK_WEDNESDAY = 13;
+
+ /**
+ * STARTWEEK_THURSDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_THURSDAY instead
+ */
const STARTWEEK_THURSDAY = 14;
+
+ /**
+ * STARTWEEK_FRIDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_FRIDAY instead
+ */
const STARTWEEK_FRIDAY = 15;
+
+ /**
+ * STARTWEEK_SATURDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_SATURDAY instead
+ */
const STARTWEEK_SATURDAY = 16;
+
+ /**
+ * STARTWEEK_SUNDAY_ALT.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_SUNDAY_ALT instead
+ */
const STARTWEEK_SUNDAY_ALT = 17;
+
+ /**
+ * DOW_SUNDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\DOW_SUNDAY instead
+ */
const DOW_SUNDAY = 1;
+
+ /**
+ * DOW_MONDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\DOW_MONDAY instead
+ */
const DOW_MONDAY = 2;
+
+ /**
+ * DOW_TUESDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\DOW_TUESDAY instead
+ */
const DOW_TUESDAY = 3;
+
+ /**
+ * DOW_WEDNESDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\DOW_WEDNESDAY instead
+ */
const DOW_WEDNESDAY = 4;
+
+ /**
+ * DOW_THURSDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\DOW_THURSDAY instead
+ */
const DOW_THURSDAY = 5;
+
+ /**
+ * DOW_FRIDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\DOW_FRIDAY instead
+ */
const DOW_FRIDAY = 6;
+
+ /**
+ * DOW_SATURDAY.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\DOW_SATURDAY instead
+ */
const DOW_SATURDAY = 7;
+
+ /**
+ * STARTWEEK_MONDAY_ISO.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\STARTWEEK_MONDAY_ISO instead
+ */
const STARTWEEK_MONDAY_ISO = 21;
+
+ /**
+ * METHODARR.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use DateTimeExcel\Constants\METHODARR instead
+ */
const METHODARR = [
self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
self::DOW_MONDAY,
@@ -1280,6 +685,11 @@ class DateTime
* Excel Function:
* WEEKNUM(dateValue[,style])
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Week::number(()
+ * Use the number method in the DateTimeExcel\Week class instead
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $method Week begins on Sunday or Monday
@@ -1298,40 +708,7 @@ class DateTime
*/
public static function WEEKNUM($dateValue = 1, $method = self::STARTWEEK_SUNDAY)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
- $method = Functions::flattenSingleValue($method);
-
- if (!is_numeric($method)) {
- return Functions::VALUE();
- }
- $method = (int) $method;
- if (!array_key_exists($method, self::METHODARR)) {
- return Functions::NaN();
- }
- $method = self::METHODARR[$method];
-
- $dateValue = self::getDateValue($dateValue);
- if (is_string($dateValue)) {
- return Functions::VALUE();
- }
- if ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
- if ($method == self::STARTWEEK_MONDAY_ISO) {
- return (int) $PHPDateObject->format('W');
- }
- $dayOfYear = $PHPDateObject->format('z');
- $PHPDateObject->modify('-' . $dayOfYear . ' days');
- $firstDayOfFirstWeek = $PHPDateObject->format('w');
- $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
- $daysInFirstWeek += 7 * !$daysInFirstWeek;
- $endFirstWeek = $daysInFirstWeek - 1;
- $weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
-
- return (int) $weekOfYear;
+ return DateTimeExcel\Week::number($dateValue, $method);
}
/**
@@ -1342,6 +719,11 @@ class DateTime
* Excel Function:
* ISOWEEKNUM(dateValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Week::isoWeekNumber()
+ * Use the isoWeekNumber method in the DateTimeExcel\Week class instead
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
@@ -1349,20 +731,7 @@ class DateTime
*/
public static function ISOWEEKNUM($dateValue = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('W');
+ return DateTimeExcel\Week::isoWeekNumber($dateValue);
}
/**
@@ -1374,6 +743,11 @@ class DateTime
* Excel Function:
* MONTH(dateValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\DateParts::month()
+ * Use the month method in the DateTimeExcel\DateParts class instead
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
@@ -1381,21 +755,7 @@ class DateTime
*/
public static function MONTHOFYEAR($dateValue = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if (empty($dateValue)) {
- $dateValue = 1;
- }
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('n');
+ return DateTimeExcel\DateParts::month($dateValue);
}
/**
@@ -1407,6 +767,11 @@ class DateTime
* Excel Function:
* YEAR(dateValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\DateParts::year()
+ * Use the ear method in the DateTimeExcel\DateParts class instead
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
@@ -1414,20 +779,7 @@ class DateTime
*/
public static function YEAR($dateValue = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('Y');
+ return DateTimeExcel\DateParts::year($dateValue);
}
/**
@@ -1439,6 +791,11 @@ class DateTime
* Excel Function:
* HOUR(timeValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\TimeParts::hour()
+ * Use the hour method in the DateTimeExcel\TimeParts class instead
+ *
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
@@ -1446,29 +803,7 @@ class DateTime
*/
public static function HOUROFDAY($timeValue = 0)
{
- $timeValue = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('G', $timeValue);
+ return DateTimeExcel\TimeParts::hour($timeValue);
}
/**
@@ -1480,6 +815,11 @@ class DateTime
* Excel Function:
* MINUTE(timeValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\TimeParts::minute()
+ * Use the minute method in the DateTimeExcel\TimeParts class instead
+ *
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
@@ -1487,29 +827,7 @@ class DateTime
*/
public static function MINUTE($timeValue = 0)
{
- $timeValue = $timeTester = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('i', $timeValue);
+ return DateTimeExcel\TimeParts::minute($timeValue);
}
/**
@@ -1521,6 +839,11 @@ class DateTime
* Excel Function:
* SECOND(timeValue)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\TimeParts::second()
+ * Use the second method in the DateTimeExcel\TimeParts class instead
+ *
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
@@ -1528,29 +851,7 @@ class DateTime
*/
public static function SECOND($timeValue = 0)
{
- $timeValue = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('s', $timeValue);
+ return DateTimeExcel\TimeParts::second($timeValue);
}
/**
@@ -1564,6 +865,11 @@ class DateTime
* Excel Function:
* EDATE(dateValue,adjustmentMonths)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Month::adjust()
+ * Use the adjust method in the DateTimeExcel\Edate class instead
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $adjustmentMonths The number of months before or after start_date.
@@ -1575,29 +881,7 @@ class DateTime
*/
public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
- $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
-
- if (!is_numeric($adjustmentMonths)) {
- return Functions::VALUE();
- }
- $adjustmentMonths = floor($adjustmentMonths);
-
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths);
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) Date::PHPToExcel($PHPDateObject);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return $PHPDateObject;
- }
+ return DateTimeExcel\Month::adjust($dateValue, $adjustmentMonths);
}
/**
@@ -1610,6 +894,11 @@ class DateTime
* Excel Function:
* EOMONTH(dateValue,adjustmentMonths)
*
+ * @Deprecated 1.18.0
+ *
+ * @See DateTimeExcel\Month::lastDay()
+ * Use the lastDay method in the DateTimeExcel\EoMonth class instead
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $adjustmentMonths The number of months before or after start_date.
@@ -1621,31 +910,6 @@ class DateTime
*/
public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
- $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
-
- if (!is_numeric($adjustmentMonths)) {
- return Functions::VALUE();
- }
- $adjustmentMonths = floor($adjustmentMonths);
-
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
- $adjustDays = (int) $PHPDateObject->format('d');
- $adjustDaysString = '-' . $adjustDays . ' days';
- $PHPDateObject->modify($adjustDaysString);
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) Date::PHPToExcel($PHPDateObject);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return $PHPDateObject;
- }
+ return DateTimeExcel\Month::lastDay($dateValue, $adjustmentMonths);
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php
new file mode 100644
index 00000000000..1165eb1fee3
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php
@@ -0,0 +1,38 @@
+ self::DOW_SUNDAY,
+ self::DOW_MONDAY,
+ self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
+ self::DOW_TUESDAY,
+ self::DOW_WEDNESDAY,
+ self::DOW_THURSDAY,
+ self::DOW_FRIDAY,
+ self::DOW_SATURDAY,
+ self::DOW_SUNDAY,
+ self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
+ ];
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
new file mode 100644
index 00000000000..d23ce37b739
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
@@ -0,0 +1,59 @@
+format('c'));
+
+ return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : Functions::VALUE();
+ }
+
+ /**
+ * DATETIMENOW.
+ *
+ * Returns the current date and time.
+ * The NOW function is useful when you need to display the current date and time on a worksheet or
+ * calculate a value based on the current date and time, and have that value updated each time you
+ * open the worksheet.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * NOW()
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function now()
+ {
+ $dti = new DateTimeImmutable();
+ $dateArray = Helpers::dateParse($dti->format('c'));
+
+ return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray) : Functions::VALUE();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
new file mode 100644
index 00000000000..d18e2371c3f
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
@@ -0,0 +1,168 @@
+getMessage();
+ }
+
+ // Execute function
+ $excelDateValue = SharedDateHelper::formattedPHPToExcel($year, $month, $day);
+
+ return Helpers::returnIn3FormatsFloat($excelDateValue);
+ }
+
+ /**
+ * Convert year from multiple formats to int.
+ *
+ * @param mixed $year
+ */
+ private static function getYear($year, int $baseYear): int
+ {
+ $year = Functions::flattenSingleValue($year);
+ $year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
+ if (!is_numeric($year)) {
+ throw new Exception(Functions::VALUE());
+ }
+ $year = (int) $year;
+
+ if ($year < ($baseYear - 1900)) {
+ throw new Exception(Functions::NAN());
+ }
+ if ((($baseYear - 1900) !== 0) && ($year < $baseYear) && ($year >= 1900)) {
+ throw new Exception(Functions::NAN());
+ }
+
+ if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
+ $year += 1900;
+ }
+
+ return (int) $year;
+ }
+
+ /**
+ * Convert month from multiple formats to int.
+ *
+ * @param mixed $month
+ */
+ private static function getMonth($month): int
+ {
+ $month = Functions::flattenSingleValue($month);
+
+ if (($month !== null) && (!is_numeric($month))) {
+ $month = SharedDateHelper::monthStringToNumber($month);
+ }
+
+ $month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0;
+ if (!is_numeric($month)) {
+ throw new Exception(Functions::VALUE());
+ }
+
+ return (int) $month;
+ }
+
+ /**
+ * Convert day from multiple formats to int.
+ *
+ * @param mixed $day
+ */
+ private static function getDay($day): int
+ {
+ $day = Functions::flattenSingleValue($day);
+
+ if (($day !== null) && (!is_numeric($day))) {
+ $day = SharedDateHelper::dayStringToNumber($day);
+ }
+
+ $day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0;
+ if (!is_numeric($day)) {
+ throw new Exception(Functions::VALUE());
+ }
+
+ return (int) $day;
+ }
+
+ private static function adjustYearMonth(int &$year, int &$month, int $baseYear): void
+ {
+ if ($month < 1) {
+ // Handle year/month adjustment if month < 1
+ --$month;
+ $year += ceil($month / 12) - 1;
+ $month = 13 - abs($month % 12);
+ } elseif ($month > 12) {
+ // Handle year/month adjustment if month > 12
+ $year += floor($month / 12);
+ $month = ($month % 12);
+ }
+
+ // Re-validate the year parameter after adjustments
+ if (($year < $baseYear) || ($year >= 10000)) {
+ throw new Exception(Functions::NAN());
+ }
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
new file mode 100644
index 00000000000..37ea0315154
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
@@ -0,0 +1,127 @@
+= 0) {
+ return $weirdResult;
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+
+ return (int) $PHPDateObject->format('j');
+ }
+
+ /**
+ * MONTHOFYEAR.
+ *
+ * Returns the month of a date represented by a serial number.
+ * The month is given as an integer, ranging from 1 (January) to 12 (December).
+ *
+ * Excel Function:
+ * MONTH(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ *
+ * @return int|string Month of the year
+ */
+ public static function month($dateValue)
+ {
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) {
+ return 1;
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+
+ return (int) $PHPDateObject->format('n');
+ }
+
+ /**
+ * YEAR.
+ *
+ * Returns the year corresponding to a date.
+ * The year is returned as an integer in the range 1900-9999.
+ *
+ * Excel Function:
+ * YEAR(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ *
+ * @return int|string Year
+ */
+ public static function year($dateValue)
+ {
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) {
+ return 1900;
+ }
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+
+ return (int) $PHPDateObject->format('Y');
+ }
+
+ /**
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ */
+ private static function weirdCondition($dateValue): int
+ {
+ // Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR)
+ if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
+ if (is_bool($dateValue)) {
+ return (int) $dateValue;
+ }
+ if ($dateValue === null) {
+ return 0;
+ }
+ if (is_numeric($dateValue) && $dateValue < 1 && $dateValue >= 0) {
+ return 0;
+ }
+ }
+
+ return -1;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
new file mode 100644
index 00000000000..3b21550626a
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
@@ -0,0 +1,147 @@
+ 31)) {
+ if ($yearFound) {
+ return Functions::VALUE();
+ }
+ if ($t < 100) {
+ $t += 1900;
+ }
+ $yearFound = true;
+ }
+ }
+ if (count($t1) === 1) {
+ // We've been fed a time value without any date
+ return ((strpos((string) $t, ':') === false)) ? Functions::Value() : 0.0;
+ }
+ unset($t);
+
+ $dateValue = self::t1ToString($t1, $dti, $yearFound);
+
+ $PHPDateArray = self::setUpArray($dateValue, $dti);
+
+ return self::finalResults($PHPDateArray, $dti, $baseYear);
+ }
+
+ private static function t1ToString(array $t1, DateTimeImmutable $dti, bool $yearFound): string
+ {
+ if (count($t1) == 2) {
+ // We only have two parts of the date: either day/month or month/year
+ if ($yearFound) {
+ array_unshift($t1, 1);
+ } else {
+ if (is_numeric($t1[1]) && $t1[1] > 29) {
+ $t1[1] += 1900;
+ array_unshift($t1, 1);
+ } else {
+ $t1[] = $dti->format('Y');
+ }
+ }
+ }
+ $dateValue = implode(' ', $t1);
+
+ return $dateValue;
+ }
+
+ /**
+ * Parse date.
+ */
+ private static function setUpArray(string $dateValue, DateTimeImmutable $dti): array
+ {
+ $PHPDateArray = Helpers::dateParse($dateValue);
+ if (!Helpers::dateParseSucceeded($PHPDateArray)) {
+ // If original count was 1, we've already returned.
+ // If it was 2, we added another.
+ // Therefore, neither of the first 2 stroks below can fail.
+ $testVal1 = strtok($dateValue, '- ');
+ $testVal2 = strtok('- ');
+ $testVal3 = strtok('- ') ?: $dti->format('Y');
+ Helpers::adjustYear((string) $testVal1, (string) $testVal2, $testVal3);
+ $PHPDateArray = Helpers::dateParse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
+ if (!Helpers::dateParseSucceeded($PHPDateArray)) {
+ $PHPDateArray = Helpers::dateParse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
+ }
+ }
+
+ return $PHPDateArray;
+ }
+
+ /**
+ * Final results.
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ private static function finalResults(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear)
+ {
+ $retValue = Functions::Value();
+ if (Helpers::dateParseSucceeded($PHPDateArray)) {
+ // Execute function
+ Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y'));
+ if ($PHPDateArray['year'] < $baseYear) {
+ return Functions::VALUE();
+ }
+ Helpers::replaceIfEmpty($PHPDateArray['month'], $dti->format('m'));
+ Helpers::replaceIfEmpty($PHPDateArray['day'], $dti->format('d'));
+ $PHPDateArray['hour'] = 0;
+ $PHPDateArray['minute'] = 0;
+ $PHPDateArray['second'] = 0;
+ $month = (int) $PHPDateArray['month'];
+ $day = (int) $PHPDateArray['day'];
+ $year = (int) $PHPDateArray['year'];
+ if (!checkdate($month, $day, $year)) {
+ return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : Functions::VALUE();
+ }
+ $retValue = Helpers::returnIn3FormatsArray($PHPDateArray, true);
+ }
+
+ return $retValue;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
new file mode 100644
index 00000000000..5a97d8df8ab
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
@@ -0,0 +1,51 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+
+ $days = Functions::VALUE();
+ $diff = $PHPStartDateObject->diff($PHPEndDateObject);
+ if ($diff !== false && !is_bool($diff->days)) {
+ $days = $diff->days;
+ if ($diff->invert) {
+ $days = -$days;
+ }
+ }
+
+ return $days;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
new file mode 100644
index 00000000000..bbc5d16aaa3
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
@@ -0,0 +1,106 @@
+getMessage();
+ }
+
+ if (!is_bool($method)) {
+ return Functions::VALUE();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $startDay = $PHPStartDateObject->format('j');
+ $startMonth = $PHPStartDateObject->format('n');
+ $startYear = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+ $endDay = $PHPEndDateObject->format('j');
+ $endMonth = $PHPEndDateObject->format('n');
+ $endYear = $PHPEndDateObject->format('Y');
+
+ return self::dateDiff360((int) $startDay, (int) $startMonth, (int) $startYear, (int) $endDay, (int) $endMonth, (int) $endYear, !$method);
+ }
+
+ /**
+ * Return the number of days between two dates based on a 360 day calendar.
+ */
+ private static function dateDiff360(int $startDay, int $startMonth, int $startYear, int $endDay, int $endMonth, int $endYear, bool $methodUS): int
+ {
+ $startDay = self::getStartDay($startDay, $startMonth, $startYear, $methodUS);
+ $endDay = self::getEndDay($endDay, $endMonth, $endYear, $startDay, $methodUS);
+
+ return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
+ }
+
+ private static function getStartDay(int $startDay, int $startMonth, int $startYear, bool $methodUS): int
+ {
+ if ($startDay == 31) {
+ --$startDay;
+ } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !Helpers::isLeapYear($startYear))))) {
+ $startDay = 30;
+ }
+
+ return $startDay;
+ }
+
+ private static function getEndDay(int $endDay, int &$endMonth, int &$endYear, int $startDay, bool $methodUS): int
+ {
+ if ($endDay == 31) {
+ if ($methodUS && $startDay != 30) {
+ $endDay = 1;
+ if ($endMonth == 12) {
+ ++$endYear;
+ $endMonth = 1;
+ } else {
+ ++$endMonth;
+ }
+ } else {
+ $endDay = 30;
+ }
+ }
+
+ return $endDay;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
new file mode 100644
index 00000000000..6adeca179a0
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
@@ -0,0 +1,146 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $startDays = (int) $PHPStartDateObject->format('j');
+ $startMonths = (int) $PHPStartDateObject->format('n');
+ $startYears = (int) $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+ $endDays = (int) $PHPEndDateObject->format('j');
+ $endMonths = (int) $PHPEndDateObject->format('n');
+ $endYears = (int) $PHPEndDateObject->format('Y');
+
+ $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
+
+ $retVal = false;
+ $retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
+ $retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
+
+ return is_bool($retVal) ? Functions::VALUE() : $retVal;
+ }
+
+ private static function initialDiff(float $startDate, float $endDate): float
+ {
+ // Validate parameters
+ if ($startDate > $endDate) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $endDate - $startDate;
+ }
+
+ /**
+ * Decide whether it's time to set retVal.
+ *
+ * @param bool|int $retVal
+ *
+ * @return null|bool|int
+ */
+ private static function replaceRetValue($retVal, string $unit, string $compare)
+ {
+ if ($retVal !== false || $unit !== $compare) {
+ return $retVal;
+ }
+
+ return null;
+ }
+
+ private static function datedifD(float $difference): int
+ {
+ return (int) $difference;
+ }
+
+ private static function datedifM(DateInterval $PHPDiffDateObject): int
+ {
+ return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
+ }
+
+ private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
+ {
+ if ($endDays < $startDays) {
+ $retVal = $endDays;
+ $PHPEndDateObject->modify('-' . $endDays . ' days');
+ $adjustDays = (int) $PHPEndDateObject->format('j');
+ $retVal += ($adjustDays - $startDays);
+ } else {
+ $retVal = (int) $PHPDiffDateObject->format('%d');
+ }
+
+ return $retVal;
+ }
+
+ private static function datedifY(DateInterval $PHPDiffDateObject): int
+ {
+ return (int) $PHPDiffDateObject->format('%y');
+ }
+
+ private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
+ {
+ $retVal = (int) $difference;
+ if ($endYears > $startYears) {
+ $isLeapStartYear = $PHPStartDateObject->format('L');
+ $wasLeapEndYear = $PHPEndDateObject->format('L');
+
+ // Adjust end year to be as close as possible as start year
+ while ($PHPEndDateObject >= $PHPStartDateObject) {
+ $PHPEndDateObject->modify('-1 year');
+ $endYears = $PHPEndDateObject->format('Y');
+ }
+ $PHPEndDateObject->modify('+1 year');
+
+ // Get the result
+ $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
+
+ // Adjust for leap years cases
+ $isLeapEndYear = $PHPEndDateObject->format('L');
+ $limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
+ if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
+ --$retVal;
+ }
+ }
+
+ return (int) $retVal;
+ }
+
+ private static function datedifYM(DateInterval $PHPDiffDateObject): int
+ {
+ return (int) $PHPDiffDateObject->format('%m');
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
new file mode 100644
index 00000000000..55ce13f1d2f
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
@@ -0,0 +1,306 @@
+format('m');
+ $oYear = (int) $PHPDateObject->format('Y');
+
+ $adjustmentMonthsString = (string) $adjustmentMonths;
+ if ($adjustmentMonths > 0) {
+ $adjustmentMonthsString = '+' . $adjustmentMonths;
+ }
+ if ($adjustmentMonths != 0) {
+ $PHPDateObject->modify($adjustmentMonthsString . ' months');
+ }
+ $nMonth = (int) $PHPDateObject->format('m');
+ $nYear = (int) $PHPDateObject->format('Y');
+
+ $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
+ if ($monthDiff != $adjustmentMonths) {
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-' . $adjustDays . ' days';
+ $PHPDateObject->modify($adjustDaysString);
+ }
+
+ return $PHPDateObject;
+ }
+
+ /**
+ * Help reduce perceived complexity of some tests.
+ *
+ * @param mixed $value
+ * @param mixed $altValue
+ */
+ public static function replaceIfEmpty(&$value, $altValue): void
+ {
+ $value = $value ?: $altValue;
+ }
+
+ /**
+ * Adjust year in ambiguous situations.
+ */
+ public static function adjustYear(string $testVal1, string $testVal2, string &$testVal3): void
+ {
+ if (!is_numeric($testVal1) || $testVal1 < 31) {
+ if (!is_numeric($testVal2) || $testVal2 < 12) {
+ if (is_numeric($testVal3) && $testVal3 < 12) {
+ $testVal3 += 2000;
+ }
+ }
+ }
+ }
+
+ /**
+ * Return result in one of three formats.
+ *
+ * @return mixed
+ */
+ public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false)
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
+ return new DateTime(
+ $dateArray['year']
+ . '-' . $dateArray['month']
+ . '-' . $dateArray['day']
+ . ' ' . $dateArray['hour']
+ . ':' . $dateArray['minute']
+ . ':' . $dateArray['second']
+ );
+ }
+ $excelDateValue =
+ SharedDateHelper::formattedPHPToExcel(
+ $dateArray['year'],
+ $dateArray['month'],
+ $dateArray['day'],
+ $dateArray['hour'],
+ $dateArray['minute'],
+ $dateArray['second']
+ );
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return $noFrac ? floor($excelDateValue) : (float) $excelDateValue;
+ }
+ // RETURNDATE_UNIX_TIMESTAMP)
+
+ return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
+ }
+
+ /**
+ * Return result in one of three formats.
+ *
+ * @return mixed
+ */
+ public static function returnIn3FormatsFloat(float $excelDateValue)
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return $excelDateValue;
+ }
+ if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
+ }
+ // RETURNDATE_PHP_DATETIME_OBJECT
+
+ return SharedDateHelper::excelToDateTimeObject($excelDateValue);
+ }
+
+ /**
+ * Return result in one of three formats.
+ *
+ * @return mixed
+ */
+ public static function returnIn3FormatsObject(DateTime $PHPDateObject)
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
+ return $PHPDateObject;
+ }
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return (float) SharedDateHelper::PHPToExcel($PHPDateObject);
+ }
+ // RETURNDATE_UNIX_TIMESTAMP
+ $stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
+ $stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
+
+ return (int) SharedDateHelper::excelToTimestamp($stamp);
+ }
+
+ private static function baseDate(): int
+ {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ return 0;
+ }
+ if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @param mixed $number
+ */
+ public static function nullFalseTrueToNumber(&$number, bool $allowBool = true): void
+ {
+ $number = Functions::flattenSingleValue($number);
+ $nullVal = self::baseDate();
+ if ($number === null) {
+ $number = $nullVal;
+ } elseif ($allowBool && is_bool($number)) {
+ $number = $nullVal + (int) $number;
+ }
+ }
+
+ /**
+ * Many functions accept null argument treated as 0.
+ *
+ * @param mixed $number
+ *
+ * @return float|int
+ */
+ public static function validateNumericNull($number)
+ {
+ $number = Functions::flattenSingleValue($number);
+ if ($number === null) {
+ return 0;
+ }
+ if (is_int($number)) {
+ return $number;
+ }
+ if (is_numeric($number)) {
+ return (float) $number;
+ }
+
+ throw new Exception(Functions::VALUE());
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @param mixed $number
+ *
+ * @return float
+ */
+ public static function validateNotNegative($number)
+ {
+ if (!is_numeric($number)) {
+ throw new Exception(Functions::VALUE());
+ }
+ if ($number >= 0) {
+ return (float) $number;
+ }
+
+ throw new Exception(Functions::NAN());
+ }
+
+ public static function silly1900(DateTime $PHPDateObject, string $mod = '-1 day'): void
+ {
+ $isoDate = $PHPDateObject->format('c');
+ if ($isoDate < '1900-03-01') {
+ $PHPDateObject->modify($mod);
+ }
+ }
+
+ public static function dateParse(string $string): array
+ {
+ return self::forceArray(date_parse($string));
+ }
+
+ public static function dateParseSucceeded(array $dateArray): bool
+ {
+ return $dateArray['error_count'] === 0;
+ }
+
+ /**
+ * Despite documentation, date_parse probably never returns false.
+ * Just in case, this routine helps guarantee it.
+ *
+ * @param array|false $dateArray
+ */
+ private static function forceArray($dateArray): array
+ {
+ return is_array($dateArray) ? $dateArray : ['error_count' => 1];
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
new file mode 100644
index 00000000000..560b7a80714
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
@@ -0,0 +1,82 @@
+getMessage();
+ }
+ $adjustmentMonths = floor($adjustmentMonths);
+
+ // Execute function
+ $PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths);
+
+ return Helpers::returnIn3FormatsObject($PHPDateObject);
+ }
+
+ /**
+ * EOMONTH.
+ *
+ * Returns the date value for the last day of the month that is the indicated number of months
+ * before or after start_date.
+ * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
+ *
+ * Excel Function:
+ * EOMONTH(dateValue,adjustmentMonths)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $adjustmentMonths The number of months before or after start_date.
+ * A positive value for months yields a future date;
+ * a negative value yields a past date.
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function lastDay($dateValue, $adjustmentMonths)
+ {
+ try {
+ $dateValue = Helpers::getDateValue($dateValue, false);
+ $adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ $adjustmentMonths = floor($adjustmentMonths);
+
+ // Execute function
+ $PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-' . $adjustDays . ' days';
+ $PHPDateObject->modify($adjustDaysString);
+
+ return Helpers::returnIn3FormatsObject($PHPDateObject);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
new file mode 100644
index 00000000000..d0f53cf392c
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
@@ -0,0 +1,102 @@
+getMessage();
+ }
+
+ // Execute function
+ $startDow = self::calcStartDow($startDate);
+ $endDow = self::calcEndDow($endDate);
+ $wholeWeekDays = (int) floor(($endDate - $startDate) / 7) * 5;
+ $partWeekDays = self::calcPartWeekDays($startDow, $endDow);
+
+ // Test any extra holiday parameters
+ $holidayCountedArray = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if ((Week::day($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
+ --$partWeekDays;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+
+ return self::applySign($wholeWeekDays + $partWeekDays, $sDate, $eDate);
+ }
+
+ private static function calcStartDow(float $startDate): int
+ {
+ $startDow = 6 - (int) Week::day($startDate, 2);
+ if ($startDow < 0) {
+ $startDow = 5;
+ }
+
+ return $startDow;
+ }
+
+ private static function calcEndDow(float $endDate): int
+ {
+ $endDow = (int) Week::day($endDate, 2);
+ if ($endDow >= 6) {
+ $endDow = 0;
+ }
+
+ return $endDow;
+ }
+
+ private static function calcPartWeekDays(int $startDow, int $endDow): int
+ {
+ $partWeekDays = $endDow + $startDow;
+ if ($partWeekDays > 5) {
+ $partWeekDays -= 5;
+ }
+
+ return $partWeekDays;
+ }
+
+ private static function applySign(int $result, float $sDate, float $eDate): int
+ {
+ return ($sDate > $eDate) ? -$result : $result;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
new file mode 100644
index 00000000000..fb5e49652f8
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
@@ -0,0 +1,119 @@
+getMessage();
+ }
+
+ self::adjustSecond($second, $minute);
+ self::adjustMinute($minute, $hour);
+
+ if ($hour > 23) {
+ $hour = $hour % 24;
+ } elseif ($hour < 0) {
+ return Functions::NAN();
+ }
+
+ // Execute function
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ $calendar = SharedDateHelper::getExcelCalendar();
+ $date = (int) ($calendar !== SharedDateHelper::CALENDAR_WINDOWS_1900);
+
+ return (float) SharedDateHelper::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
+ }
+ if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ return (int) SharedDateHelper::excelToTimestamp(SharedDateHelper::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
+ }
+ // RETURNDATE_PHP_DATETIME_OBJECT
+ // Hour has already been normalized (0-23) above
+ $phpDateObject = new DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
+
+ return $phpDateObject;
+ }
+
+ private static function adjustSecond(int &$second, int &$minute): void
+ {
+ if ($second < 0) {
+ $minute += floor($second / 60);
+ $second = 60 - abs($second % 60);
+ if ($second == 60) {
+ $second = 0;
+ }
+ } elseif ($second >= 60) {
+ $minute += floor($second / 60);
+ $second = $second % 60;
+ }
+ }
+
+ private static function adjustMinute(int &$minute, int &$hour): void
+ {
+ if ($minute < 0) {
+ $hour += floor($minute / 60);
+ $minute = 60 - abs($minute % 60);
+ if ($minute == 60) {
+ $minute = 0;
+ }
+ } elseif ($minute >= 60) {
+ $hour += floor($minute / 60);
+ $minute = $minute % 60;
+ }
+ }
+
+ /**
+ * @param mixed $value expect int
+ */
+ private static function toIntWithNullBool($value): int
+ {
+ $value = Functions::flattenSingleValue($value);
+ $value = $value ?? 0;
+ if (is_bool($value)) {
+ $value = (int) $value;
+ }
+ if (!is_numeric($value)) {
+ throw new Exception(Functions::VALUE());
+ }
+
+ return (int) $value;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
new file mode 100644
index 00000000000..49cd983d874
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
@@ -0,0 +1,112 @@
+getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+
+ return (int) $timeValue->format('H');
+ }
+
+ /**
+ * MINUTE.
+ *
+ * Returns the minutes of a time value.
+ * The minute is given as an integer, ranging from 0 to 59.
+ *
+ * Excel Function:
+ * MINUTE(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ *
+ * @return int|string Minute
+ */
+ public static function minute($timeValue)
+ {
+ try {
+ $timeValue = Functions::flattenSingleValue($timeValue);
+ Helpers::nullFalseTrueToNumber($timeValue);
+ if (!is_numeric($timeValue)) {
+ $timeValue = Helpers::getTimeValue($timeValue);
+ }
+ Helpers::validateNotNegative($timeValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+
+ return (int) $timeValue->format('i');
+ }
+
+ /**
+ * SECOND.
+ *
+ * Returns the seconds of a time value.
+ * The minute is given as an integer, ranging from 0 to 59.
+ *
+ * Excel Function:
+ * SECOND(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ *
+ * @return int|string Second
+ */
+ public static function second($timeValue)
+ {
+ try {
+ $timeValue = Functions::flattenSingleValue($timeValue);
+ Helpers::nullFalseTrueToNumber($timeValue);
+ if (!is_numeric($timeValue)) {
+ $timeValue = Helpers::getTimeValue($timeValue);
+ }
+ Helpers::validateNotNegative($timeValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+
+ return (int) $timeValue->format('s');
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
new file mode 100644
index 00000000000..c9645e66573
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
@@ -0,0 +1,67 @@
+ 24) {
+ $arraySplit[0] = ($arraySplit[0] % 24);
+ $timeValue = implode(':', $arraySplit);
+ }
+
+ $PHPDateArray = Helpers::dateParse($timeValue);
+ $retValue = Functions::VALUE();
+ if (Helpers::dateParseSucceeded($PHPDateArray)) {
+ /** @var int */
+ $hour = $PHPDateArray['hour'];
+ /** @var int */
+ $minute = $PHPDateArray['minute'];
+ /** @var int */
+ $second = $PHPDateArray['second'];
+ // OpenOffice-specific code removed - it works just like Excel
+ $excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $hour, $minute, $second) - 1;
+
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ $retValue = (float) $excelDateValue;
+ } elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ $retValue = (int) $phpDateValue = SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600;
+ } else {
+ $retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
+ }
+ }
+
+ return $retValue;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
new file mode 100644
index 00000000000..66362221049
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
@@ -0,0 +1,254 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ if ($method == Constants::STARTWEEK_MONDAY_ISO) {
+ Helpers::silly1900($PHPDateObject);
+
+ return (int) $PHPDateObject->format('W');
+ }
+ if (self::buggyWeekNum1904($method, $origDateValueNull, $PHPDateObject)) {
+ return 0;
+ }
+ Helpers::silly1900($PHPDateObject, '+ 5 years'); // 1905 calendar matches
+ $dayOfYear = (int) $PHPDateObject->format('z');
+ $PHPDateObject->modify('-' . $dayOfYear . ' days');
+ $firstDayOfFirstWeek = (int) $PHPDateObject->format('w');
+ $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
+ $daysInFirstWeek += 7 * !$daysInFirstWeek;
+ $endFirstWeek = $daysInFirstWeek - 1;
+ $weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
+
+ return (int) $weekOfYear;
+ }
+
+ /**
+ * ISOWEEKNUM.
+ *
+ * Returns the ISO 8601 week number of the year for a specified date.
+ *
+ * Excel Function:
+ * ISOWEEKNUM(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ *
+ * @return int|string Week Number
+ */
+ public static function isoWeekNumber($dateValue)
+ {
+ if (self::apparentBug($dateValue)) {
+ return 52;
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ Helpers::silly1900($PHPDateObject);
+
+ return (int) $PHPDateObject->format('W');
+ }
+
+ /**
+ * WEEKDAY.
+ *
+ * Returns the day of the week for a specified date. The day is given as an integer
+ * ranging from 0 to 7 (dependent on the requested style).
+ *
+ * Excel Function:
+ * WEEKDAY(dateValue[,style])
+ *
+ * @param null|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param mixed $style A number that determines the type of return value
+ * 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
+ * 2 Numbers 1 (Monday) through 7 (Sunday).
+ * 3 Numbers 0 (Monday) through 6 (Sunday).
+ *
+ * @return int|string Day of the week value
+ */
+ public static function day($dateValue, $style = 1)
+ {
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ $style = self::validateStyle($style);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ Helpers::silly1900($PHPDateObject);
+ $DoW = (int) $PHPDateObject->format('w');
+
+ switch ($style) {
+ case 1:
+ ++$DoW;
+
+ break;
+ case 2:
+ $DoW = self::dow0Becomes7($DoW);
+
+ break;
+ case 3:
+ $DoW = self::dow0Becomes7($DoW) - 1;
+
+ break;
+ }
+
+ return $DoW;
+ }
+
+ /**
+ * @param mixed $style expect int
+ */
+ private static function validateStyle($style): int
+ {
+ $style = Functions::flattenSingleValue($style);
+
+ if (!is_numeric($style)) {
+ throw new Exception(Functions::VALUE());
+ }
+ $style = (int) $style;
+ if (($style < 1) || ($style > 3)) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $style;
+ }
+
+ private static function dow0Becomes7(int $DoW): int
+ {
+ return ($DoW === 0) ? 7 : $DoW;
+ }
+
+ /**
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ */
+ private static function apparentBug($dateValue): bool
+ {
+ if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
+ if (is_bool($dateValue)) {
+ return true;
+ }
+ if (is_numeric($dateValue) && !((int) $dateValue)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Validate dateValue parameter.
+ *
+ * @param mixed $dateValue
+ */
+ private static function validateDateValue($dateValue): float
+ {
+ if (is_bool($dateValue)) {
+ throw new Exception(Functions::VALUE());
+ }
+
+ return Helpers::getDateValue($dateValue);
+ }
+
+ /**
+ * Validate method parameter.
+ *
+ * @param mixed $method
+ */
+ private static function validateMethod($method): int
+ {
+ if ($method === null) {
+ $method = Constants::STARTWEEK_SUNDAY;
+ }
+ $method = Functions::flattenSingleValue($method);
+ if (!is_numeric($method)) {
+ throw new Exception(Functions::VALUE());
+ }
+
+ $method = (int) $method;
+ if (!array_key_exists($method, Constants::METHODARR)) {
+ throw new Exception(Functions::NAN());
+ }
+ $method = Constants::METHODARR[$method];
+
+ return $method;
+ }
+
+ private static function buggyWeekNum1900(int $method): bool
+ {
+ return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900;
+ }
+
+ private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool
+ {
+ // This appears to be another Excel bug.
+
+ return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 &&
+ !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
new file mode 100644
index 00000000000..89e47b9690b
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
@@ -0,0 +1,189 @@
+getMessage();
+ }
+
+ $startDate = (float) floor($startDate);
+ $endDays = (int) floor($endDays);
+ // If endDays is 0, we always return startDate
+ if ($endDays == 0) {
+ return $startDate;
+ }
+ if ($endDays < 0) {
+ return self::decrementing($startDate, $endDays, $holidayArray);
+ }
+
+ return self::incrementing($startDate, $endDays, $holidayArray);
+ }
+
+ /**
+ * Use incrementing logic to determine Workday.
+ *
+ * @return mixed
+ */
+ private static function incrementing(float $startDate, int $endDays, array $holidayArray)
+ {
+ // Adjust the start date if it falls over a weekend
+
+ $startDoW = self::getWeekDay($startDate, 3);
+ if (self::getWeekDay($startDate, 3) >= 5) {
+ $startDate += 7 - $startDoW;
+ --$endDays;
+ }
+
+ // Add endDays
+ $endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
+ $endDays = $endDays % 5;
+ while ($endDays > 0) {
+ ++$endDate;
+ // Adjust the calculated end date if it falls over a weekend
+ $endDow = self::getWeekDay($endDate, 3);
+ if ($endDow >= 5) {
+ $endDate += 7 - $endDow;
+ }
+ --$endDays;
+ }
+
+ // Test any extra holiday parameters
+ if (!empty($holidayArray)) {
+ $endDate = self::incrementingArray($startDate, $endDate, $holidayArray);
+ }
+
+ return Helpers::returnIn3FormatsFloat($endDate);
+ }
+
+ private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float
+ {
+ $holidayCountedArray = $holidayDates = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (self::getWeekDay($holidayDate, 3) < 5) {
+ $holidayDates[] = $holidayDate;
+ }
+ }
+ sort($holidayDates, SORT_NUMERIC);
+ foreach ($holidayDates as $holidayDate) {
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if (!in_array($holidayDate, $holidayCountedArray)) {
+ ++$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ // Adjust the calculated end date if it falls over a weekend
+ $endDoW = self::getWeekDay($endDate, 3);
+ if ($endDoW >= 5) {
+ $endDate += 7 - $endDoW;
+ }
+ }
+
+ return $endDate;
+ }
+
+ /**
+ * Use decrementing logic to determine Workday.
+ *
+ * @return mixed
+ */
+ private static function decrementing(float $startDate, int $endDays, array $holidayArray)
+ {
+ // Adjust the start date if it falls over a weekend
+
+ $startDoW = self::getWeekDay($startDate, 3);
+ if (self::getWeekDay($startDate, 3) >= 5) {
+ $startDate += -$startDoW + 4;
+ ++$endDays;
+ }
+
+ // Add endDays
+ $endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
+ $endDays = $endDays % 5;
+ while ($endDays < 0) {
+ --$endDate;
+ // Adjust the calculated end date if it falls over a weekend
+ $endDow = self::getWeekDay($endDate, 3);
+ if ($endDow >= 5) {
+ $endDate += 4 - $endDow;
+ }
+ ++$endDays;
+ }
+
+ // Test any extra holiday parameters
+ if (!empty($holidayArray)) {
+ $endDate = self::decrementingArray($startDate, $endDate, $holidayArray);
+ }
+
+ return Helpers::returnIn3FormatsFloat($endDate);
+ }
+
+ private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float
+ {
+ $holidayCountedArray = $holidayDates = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (self::getWeekDay($holidayDate, 3) < 5) {
+ $holidayDates[] = $holidayDate;
+ }
+ }
+ rsort($holidayDates, SORT_NUMERIC);
+ foreach ($holidayDates as $holidayDate) {
+ if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
+ if (!in_array($holidayDate, $holidayCountedArray)) {
+ --$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ // Adjust the calculated end date if it falls over a weekend
+ $endDoW = self::getWeekDay($endDate, 3);
+ if ($endDoW >= 5) {
+ $endDate += -$endDoW + 4;
+ }
+ }
+
+ return $endDate;
+ }
+
+ private static function getWeekDay(float $date, int $wd): int
+ {
+ $result = Week::day($date, $wd);
+
+ return is_string($result) ? -1 : $result;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
new file mode 100644
index 00000000000..da2ac12fc5a
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
@@ -0,0 +1,120 @@
+getMessage();
+ }
+
+ switch ($method) {
+ case 0:
+ return Days360::between($startDate, $endDate) / 360;
+ case 1:
+ return self::method1($startDate, $endDate);
+ case 2:
+ return Difference::interval($startDate, $endDate) / 360;
+ case 3:
+ return Difference::interval($startDate, $endDate) / 365;
+ case 4:
+ return Days360::between($startDate, $endDate, true) / 360;
+ }
+
+ return Functions::NAN();
+ }
+
+ /**
+ * Excel 1900 calendar treats date argument of null as 1900-01-00. Really.
+ *
+ * @param mixed $startDate
+ * @param mixed $endDate
+ */
+ private static function excelBug(float $sDate, $startDate, $endDate, int $method): float
+ {
+ if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
+ if ($endDate === null && $startDate !== null) {
+ if (DateParts::month($sDate) == 12 && DateParts::day($sDate) === 31 && $method === 0) {
+ $sDate += 2;
+ } else {
+ ++$sDate;
+ }
+ }
+ }
+
+ return $sDate;
+ }
+
+ private static function method1(float $startDate, float $endDate): float
+ {
+ $days = Difference::interval($startDate, $endDate);
+ $startYear = (int) DateParts::year($startDate);
+ $endYear = (int) DateParts::year($endDate);
+ $years = $endYear - $startYear + 1;
+ $startMonth = (int) DateParts::month($startDate);
+ $startDay = (int) DateParts::day($startDate);
+ $endMonth = (int) DateParts::month($endDate);
+ $endDay = (int) DateParts::day($endDate);
+ $startMonthDay = 100 * $startMonth + $startDay;
+ $endMonthDay = 100 * $endMonth + $endDay;
+ if ($years == 1) {
+ $tmpCalcAnnualBasis = 365 + (int) Helpers::isLeapYear($endYear);
+ } elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
+ if (Helpers::isLeapYear($startYear)) {
+ $tmpCalcAnnualBasis = 365 + (int) ($startMonthDay <= 229);
+ } elseif (Helpers::isLeapYear($endYear)) {
+ $tmpCalcAnnualBasis = 365 + (int) ($endMonthDay >= 229);
+ } else {
+ $tmpCalcAnnualBasis = 365;
+ }
+ } else {
+ $tmpCalcAnnualBasis = 0;
+ for ($year = $startYear; $year <= $endYear; ++$year) {
+ $tmpCalcAnnualBasis += 365 + (int) Helpers::isLeapYear($year);
+ }
+ $tmpCalcAnnualBasis /= $years;
+ }
+
+ return $days / $tmpCalcAnnualBasis;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
index 3c0f2377e4f..7f4657c43fc 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
@@ -48,11 +48,11 @@ class Logger
/**
* Enable/Disable Calculation engine logging.
*
- * @param bool $pValue
+ * @param bool $writeDebugLog
*/
- public function setWriteDebugLog($pValue): void
+ public function setWriteDebugLog($writeDebugLog): void
{
- $this->writeDebugLog = $pValue;
+ $this->writeDebugLog = $writeDebugLog;
}
/**
@@ -68,11 +68,11 @@ class Logger
/**
* Enable/Disable echoing of debug log information.
*
- * @param bool $pValue
+ * @param bool $echoDebugLog
*/
- public function setEchoDebugLog($pValue): void
+ public function setEchoDebugLog($echoDebugLog): void
{
- $this->echoDebugLog = $pValue;
+ $this->echoDebugLog = $echoDebugLog;
}
/**
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
index 57116f28f53..a70ddac5cfb 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
@@ -3,22 +3,28 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
use Complex\Complex;
-use Complex\Exception as ComplexException;
-use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ConvertUOM;
+use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ComplexFunctions;
+use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ComplexOperations;
+/**
+ * @deprecated 1.18.0
+ */
class Engineering
{
/**
* EULER.
+ *
+ * @deprecated 1.18.0
+ * @see Use Engineering\Constants\EULER instead
*/
- const EULER = 2.71828182845904523536;
+ public const EULER = 2.71828182845904523536;
/**
* parseComplex.
*
* Parses a complex number into its real and imaginary parts, and an I or J suffix
*
- * @deprecated 2.0.0 No longer used by internal code. Please use the Complex\Complex class instead
+ * @deprecated 1.12.0 No longer used by internal code. Please use the \Complex\Complex class instead
*
* @param string $complexNumber The complex number
*
@@ -35,35 +41,6 @@ class Engineering
];
}
- /**
- * Formats a number base string value with leading zeroes.
- *
- * @param string $xVal The "number" to pad
- * @param int $places The length that we want to pad this value
- *
- * @return string The padded "number"
- */
- private static function nbrConversionFormat($xVal, $places)
- {
- if ($places !== null) {
- if (is_numeric($places)) {
- $places = (int) $places;
- } else {
- return Functions::VALUE();
- }
- if ($places < 0) {
- return Functions::NAN();
- }
- if (strlen($xVal) <= $places) {
- return substr(str_pad($xVal, $places, '0', STR_PAD_LEFT), -10);
- }
-
- return Functions::NAN();
- }
-
- return substr($xVal, -10);
- }
-
/**
* BESSELI.
*
@@ -73,6 +50,10 @@ class Engineering
* Excel Function:
* BESSELI(x,ord)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BESSELI() method in the Engineering\BesselI class instead
+ *
* @param float $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELI returns the #VALUE! error value.
* @param int $ord The order of the Bessel function.
@@ -84,38 +65,7 @@ class Engineering
*/
public static function BESSELI($x, $ord)
{
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- $ord = floor($ord);
- if ($ord < 0) {
- return Functions::NAN();
- }
-
- if (abs($x) <= 30) {
- $fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord);
- $ordK = 1;
- $fSqrX = ($x * $x) / 4;
- do {
- $fTerm *= $fSqrX;
- $fTerm /= ($ordK * ($ordK + $ord));
- $fResult += $fTerm;
- } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
- } else {
- $f_2_PI = 2 * M_PI;
-
- $fXAbs = abs($x);
- $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
- if (($ord & 1) && ($x < 0)) {
- $fResult = -$fResult;
- }
- }
-
- return (is_nan($fResult)) ? Functions::NAN() : $fResult;
- }
-
- return Functions::VALUE();
+ return Engineering\BesselI::BESSELI($x, $ord);
}
/**
@@ -126,6 +76,10 @@ class Engineering
* Excel Function:
* BESSELJ(x,ord)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BESSELJ() method in the Engineering\BesselJ class instead
+ *
* @param float $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELJ returns the #VALUE! error value.
* @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
@@ -136,76 +90,7 @@ class Engineering
*/
public static function BESSELJ($x, $ord)
{
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- $ord = floor($ord);
- if ($ord < 0) {
- return Functions::NAN();
- }
-
- $fResult = 0;
- if (abs($x) <= 30) {
- $fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord);
- $ordK = 1;
- $fSqrX = ($x * $x) / -4;
- do {
- $fTerm *= $fSqrX;
- $fTerm /= ($ordK * ($ordK + $ord));
- $fResult += $fTerm;
- } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
- } else {
- $f_PI_DIV_2 = M_PI / 2;
- $f_PI_DIV_4 = M_PI / 4;
-
- $fXAbs = abs($x);
- $fResult = sqrt(Functions::M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4);
- if (($ord & 1) && ($x < 0)) {
- $fResult = -$fResult;
- }
- }
-
- return (is_nan($fResult)) ? Functions::NAN() : $fResult;
- }
-
- return Functions::VALUE();
- }
-
- private static function besselK0($fNum)
- {
- if ($fNum <= 2) {
- $fNum2 = $fNum * 0.5;
- $y = ($fNum2 * $fNum2);
- $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
- (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
- (0.10750e-3 + $y * 0.74e-5))))));
- } else {
- $y = 2 / $fNum;
- $fRet = exp(-$fNum) / sqrt($fNum) *
- (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
- (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
- }
-
- return $fRet;
- }
-
- private static function besselK1($fNum)
- {
- if ($fNum <= 2) {
- $fNum2 = $fNum * 0.5;
- $y = ($fNum2 * $fNum2);
- $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
- (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
- (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
- } else {
- $y = 2 / $fNum;
- $fRet = exp(-$fNum) / sqrt($fNum) *
- (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
- (0.325614e-2 + $y * (-0.68245e-3)))))));
- }
-
- return $fRet;
+ return Engineering\BesselJ::BESSELJ($x, $ord);
}
/**
@@ -217,6 +102,10 @@ class Engineering
* Excel Function:
* BESSELK(x,ord)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BESSELK() method in the Engineering\BesselK class instead
+ *
* @param float $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELK returns the #VALUE! error value.
* @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
@@ -227,73 +116,7 @@ class Engineering
*/
public static function BESSELK($x, $ord)
{
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- if (($ord < 0) || ($x == 0.0)) {
- return Functions::NAN();
- }
-
- switch (floor($ord)) {
- case 0:
- $fBk = self::besselK0($x);
-
- break;
- case 1:
- $fBk = self::besselK1($x);
-
- break;
- default:
- $fTox = 2 / $x;
- $fBkm = self::besselK0($x);
- $fBk = self::besselK1($x);
- for ($n = 1; $n < $ord; ++$n) {
- $fBkp = $fBkm + $n * $fTox * $fBk;
- $fBkm = $fBk;
- $fBk = $fBkp;
- }
- }
-
- return (is_nan($fBk)) ? Functions::NAN() : $fBk;
- }
-
- return Functions::VALUE();
- }
-
- private static function besselY0($fNum)
- {
- if ($fNum < 8.0) {
- $y = ($fNum * $fNum);
- $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
- $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
- $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
- } else {
- $z = 8.0 / $fNum;
- $y = ($z * $z);
- $xx = $fNum - 0.785398164;
- $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
- $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
- $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
- }
-
- return $fRet;
- }
-
- private static function besselY1($fNum)
- {
- if ($fNum < 8.0) {
- $y = ($fNum * $fNum);
- $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
- (-0.4237922726e7 + $y * 0.8511937935e4)))));
- $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
- (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
- $fRet = $f1 / $f2 + 0.636619772 * (self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
- } else {
- $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
- }
-
- return $fRet;
+ return Engineering\BesselK::BESSELK($x, $ord);
}
/**
@@ -304,48 +127,21 @@ class Engineering
* Excel Function:
* BESSELY(x,ord)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BESSELY() method in the Engineering\BesselY class instead
+ *
* @param float $x The value at which to evaluate the function.
- * If x is nonnumeric, BESSELK returns the #VALUE! error value.
+ * If x is nonnumeric, BESSELY returns the #VALUE! error value.
* @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
- * If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
- * If $ord < 0, BESSELK returns the #NUM! error value.
+ * If $ord is nonnumeric, BESSELY returns the #VALUE! error value.
+ * If $ord < 0, BESSELY returns the #NUM! error value.
*
* @return float|string Result, or a string containing an error
*/
public static function BESSELY($x, $ord)
{
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- if (($ord < 0) || ($x == 0.0)) {
- return Functions::NAN();
- }
-
- switch (floor($ord)) {
- case 0:
- $fBy = self::besselY0($x);
-
- break;
- case 1:
- $fBy = self::besselY1($x);
-
- break;
- default:
- $fTox = 2 / $x;
- $fBym = self::besselY0($x);
- $fBy = self::besselY1($x);
- for ($n = 1; $n < $ord; ++$n) {
- $fByp = $n * $fTox * $fBy - $fBym;
- $fBym = $fBy;
- $fBy = $fByp;
- }
- }
-
- return (is_nan($fBy)) ? Functions::NAN() : $fBy;
- }
-
- return Functions::VALUE();
+ return Engineering\BesselY::BESSELY($x, $ord);
}
/**
@@ -356,7 +152,11 @@ class Engineering
* Excel Function:
* BIN2DEC(x)
*
- * @param string $x The binary number (as a string) that you want to convert. The number
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toDecimal() method in the Engineering\ConvertBinary class instead
+ *
+ * @param mixed $x The binary number (as a string) that you want to convert. The number
* cannot contain more than 10 characters (10 bits). The most significant
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
@@ -367,32 +167,7 @@ class Engineering
*/
public static function BINTODEC($x)
{
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } elseif (strlen($x) == 10) {
- // Two's Complement
- $x = substr($x, -9);
-
- return '-' . (512 - bindec($x));
- }
-
- return bindec($x);
+ return Engineering\ConvertBinary::toDecimal($x);
}
/**
@@ -403,13 +178,17 @@ class Engineering
* Excel Function:
* BIN2HEX(x[,places])
*
- * @param string $x The binary number (as a string) that you want to convert. The number
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toHex() method in the Engineering\ConvertBinary class instead
+ *
+ * @param mixed $x The binary number (as a string) that you want to convert. The number
* cannot contain more than 10 characters (10 bits). The most significant
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
* If number is not a valid binary number, or if number contains more than
* 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, BIN2HEX uses the
+ * @param mixed $places The number of characters to use. If places is omitted, BIN2HEX uses the
* minimum number of characters necessary. Places is useful for padding the
* return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -420,33 +199,7 @@ class Engineering
*/
public static function BINTOHEX($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- // Argument X
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } elseif (strlen($x) == 10) {
- // Two's Complement
- return str_repeat('F', 8) . substr(strtoupper(dechex(bindec(substr($x, -9)))), -2);
- }
- $hexVal = (string) strtoupper(dechex(bindec($x)));
-
- return self::nbrConversionFormat($hexVal, $places);
+ return Engineering\ConvertBinary::toHex($x, $places);
}
/**
@@ -457,13 +210,17 @@ class Engineering
* Excel Function:
* BIN2OCT(x[,places])
*
- * @param string $x The binary number (as a string) that you want to convert. The number
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toOctal() method in the Engineering\ConvertBinary class instead
+ *
+ * @param mixed $x The binary number (as a string) that you want to convert. The number
* cannot contain more than 10 characters (10 bits). The most significant
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
* If number is not a valid binary number, or if number contains more than
* 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, BIN2OCT uses the
+ * @param mixed $places The number of characters to use. If places is omitted, BIN2OCT uses the
* minimum number of characters necessary. Places is useful for padding the
* return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -474,32 +231,7 @@ class Engineering
*/
public static function BINTOOCT($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } elseif (strlen($x) == 10) {
- // Two's Complement
- return str_repeat('7', 7) . substr(strtoupper(decoct(bindec(substr($x, -9)))), -3);
- }
- $octVal = (string) decoct(bindec($x));
-
- return self::nbrConversionFormat($octVal, $places);
+ return Engineering\ConvertBinary::toOctal($x, $places);
}
/**
@@ -510,7 +242,11 @@ class Engineering
* Excel Function:
* DEC2BIN(x[,places])
*
- * @param string $x The decimal integer you want to convert. If number is negative,
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toBinary() method in the Engineering\ConvertDecimal class instead
+ *
+ * @param mixed $x The decimal integer you want to convert. If number is negative,
* valid place values are ignored and DEC2BIN returns a 10-character
* (10-bit) binary number in which the most significant bit is the sign
* bit. The remaining 9 bits are magnitude bits. Negative numbers are
@@ -520,7 +256,7 @@ class Engineering
* If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
* If DEC2BIN requires more than places characters, it returns the #NUM!
* error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2BIN uses
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2BIN uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -531,34 +267,7 @@ class Engineering
*/
public static function DECTOBIN($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
-
- $x = (string) floor($x);
- if ($x < -512 || $x > 511) {
- return Functions::NAN();
- }
-
- $r = decbin($x);
- // Two's Complement
- $r = substr($r, -10);
- if (strlen($r) >= 11) {
- return Functions::NAN();
- }
-
- return self::nbrConversionFormat($r, $places);
+ return Engineering\ConvertDecimal::toBinary($x, $places);
}
/**
@@ -569,7 +278,11 @@ class Engineering
* Excel Function:
* DEC2HEX(x[,places])
*
- * @param string $x The decimal integer you want to convert. If number is negative,
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toHex() method in the Engineering\ConvertDecimal class instead
+ *
+ * @param mixed $x The decimal integer you want to convert. If number is negative,
* places is ignored and DEC2HEX returns a 10-character (40-bit)
* hexadecimal number in which the most significant bit is the sign
* bit. The remaining 39 bits are magnitude bits. Negative numbers
@@ -579,7 +292,7 @@ class Engineering
* If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
* If DEC2HEX requires more than places characters, it returns the
* #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2HEX uses
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2HEX uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -590,28 +303,7 @@ class Engineering
*/
public static function DECTOHEX($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
- $x = (string) floor($x);
- $r = strtoupper(dechex($x));
- if (strlen($r) == 8) {
- // Two's Complement
- $r = 'FF' . $r;
- }
-
- return self::nbrConversionFormat($r, $places);
+ return Engineering\ConvertDecimal::toHex($x, $places);
}
/**
@@ -622,7 +314,11 @@ class Engineering
* Excel Function:
* DEC2OCT(x[,places])
*
- * @param string $x The decimal integer you want to convert. If number is negative,
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toOctal() method in the Engineering\ConvertDecimal class instead
+ *
+ * @param mixed $x The decimal integer you want to convert. If number is negative,
* places is ignored and DEC2OCT returns a 10-character (30-bit)
* octal number in which the most significant bit is the sign bit.
* The remaining 29 bits are magnitude bits. Negative numbers are
@@ -632,7 +328,7 @@ class Engineering
* If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
* If DEC2OCT requires more than places characters, it returns the
* #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2OCT uses
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2OCT uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -643,29 +339,7 @@ class Engineering
*/
public static function DECTOOCT($x, $places = null)
{
- $xorig = $x;
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
- $x = (string) floor($x);
- $r = decoct($x);
- if (strlen($r) == 11) {
- // Two's Complement
- $r = substr($r, -10);
- }
-
- return self::nbrConversionFormat($r, $places);
+ return Engineering\ConvertDecimal::toOctal($x, $places);
}
/**
@@ -676,7 +350,11 @@ class Engineering
* Excel Function:
* HEX2BIN(x[,places])
*
- * @param string $x the hexadecimal number you want to convert.
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toBinary() method in the Engineering\ConvertHex class instead
+ *
+ * @param mixed $x the hexadecimal number (as a string) that you want to convert.
* Number cannot contain more than 10 characters.
* The most significant bit of number is the sign bit (40th bit from the right).
* The remaining 9 bits are magnitude bits.
@@ -686,7 +364,7 @@ class Engineering
* and if number is positive, it cannot be greater than 1FF.
* If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
* If HEX2BIN requires more than places characters, it returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted,
+ * @param mixed $places The number of characters to use. If places is omitted,
* HEX2BIN uses the minimum number of characters necessary. Places
* is useful for padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -697,18 +375,7 @@ class Engineering
*/
public static function HEXTOBIN($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- return self::DECTOBIN(self::HEXTODEC($x), $places);
+ return Engineering\ConvertHex::toBinary($x, $places);
}
/**
@@ -719,7 +386,11 @@ class Engineering
* Excel Function:
* HEX2DEC(x)
*
- * @param string $x The hexadecimal number you want to convert. This number cannot
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toDecimal() method in the Engineering\ConvertHex class instead
+ *
+ * @param mixed $x The hexadecimal number (as a string) that you want to convert. This number cannot
* contain more than 10 characters (40 bits). The most significant
* bit of number is the sign bit. The remaining 39 bits are magnitude
* bits. Negative numbers are represented using two's-complement
@@ -731,33 +402,7 @@ class Engineering
*/
public static function HEXTODEC($x)
{
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- if (strlen($x) > 10) {
- return Functions::NAN();
- }
-
- $binX = '';
- foreach (str_split($x) as $char) {
- $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
- }
- if (strlen($binX) == 40 && $binX[0] == '1') {
- for ($i = 0; $i < 40; ++$i) {
- $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
- }
-
- return (bindec($binX) + 1) * -1;
- }
-
- return bindec($binX);
+ return Engineering\ConvertHex::toDecimal($x);
}
/**
@@ -768,7 +413,11 @@ class Engineering
* Excel Function:
* HEX2OCT(x[,places])
*
- * @param string $x The hexadecimal number you want to convert. Number cannot
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toOctal() method in the Engineering\ConvertHex class instead
+ *
+ * @param mixed $x The hexadecimal number (as a string) that you want to convert. Number cannot
* contain more than 10 characters. The most significant bit of
* number is the sign bit. The remaining 39 bits are magnitude
* bits. Negative numbers are represented using two's-complement
@@ -781,7 +430,7 @@ class Engineering
* the #NUM! error value.
* If HEX2OCT requires more than places characters, it returns
* the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, HEX2OCT
+ * @param mixed $places The number of characters to use. If places is omitted, HEX2OCT
* uses the minimum number of characters necessary. Places is
* useful for padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -793,23 +442,7 @@ class Engineering
*/
public static function HEXTOOCT($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- $decimal = self::HEXTODEC($x);
- if ($decimal < -536870912 || $decimal > 536870911) {
- return Functions::NAN();
- }
-
- return self::DECTOOCT($decimal, $places);
+ return Engineering\ConvertHex::toOctal($x, $places);
}
/**
@@ -820,7 +453,11 @@ class Engineering
* Excel Function:
* OCT2BIN(x[,places])
*
- * @param string $x The octal number you want to convert. Number may not
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toBinary() method in the Engineering\ConvertOctal class instead
+ *
+ * @param mixed $x The octal number you want to convert. Number may not
* contain more than 10 characters. The most significant
* bit of number is the sign bit. The remaining 29 bits
* are magnitude bits. Negative numbers are represented
@@ -833,7 +470,7 @@ class Engineering
* the #NUM! error value.
* If OCT2BIN requires more than places characters, it
* returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted,
+ * @param mixed $places The number of characters to use. If places is omitted,
* OCT2BIN uses the minimum number of characters necessary.
* Places is useful for padding the return value with
* leading 0s (zeros).
@@ -847,18 +484,7 @@ class Engineering
*/
public static function OCTTOBIN($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
-
- return self::DECTOBIN(self::OCTTODEC($x), $places);
+ return Engineering\ConvertOctal::toBinary($x, $places);
}
/**
@@ -869,7 +495,11 @@ class Engineering
* Excel Function:
* OCT2DEC(x)
*
- * @param string $x The octal number you want to convert. Number may not contain
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toDecimal() method in the Engineering\ConvertOctal class instead
+ *
+ * @param mixed $x The octal number you want to convert. Number may not contain
* more than 10 octal characters (30 bits). The most significant
* bit of number is the sign bit. The remaining 29 bits are
* magnitude bits. Negative numbers are represented using
@@ -881,28 +511,7 @@ class Engineering
*/
public static function OCTTODEC($x)
{
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
- $binX = '';
- foreach (str_split($x) as $char) {
- $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
- }
- if (strlen($binX) == 30 && $binX[0] == '1') {
- for ($i = 0; $i < 30; ++$i) {
- $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
- }
-
- return (bindec($binX) + 1) * -1;
- }
-
- return bindec($binX);
+ return Engineering\ConvertOctal::toDecimal($x);
}
/**
@@ -913,7 +522,11 @@ class Engineering
* Excel Function:
* OCT2HEX(x[,places])
*
- * @param string $x The octal number you want to convert. Number may not contain
+ * @Deprecated 1.17.0
+ *
+ * @see Use the toHex() method in the Engineering\ConvertOctal class instead
+ *
+ * @param mixed $x The octal number you want to convert. Number may not contain
* more than 10 octal characters (30 bits). The most significant
* bit of number is the sign bit. The remaining 29 bits are
* magnitude bits. Negative numbers are represented using
@@ -924,7 +537,7 @@ class Engineering
* #NUM! error value.
* If OCT2HEX requires more than places characters, it returns
* the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, OCT2HEX
+ * @param mixed $places The number of characters to use. If places is omitted, OCT2HEX
* uses the minimum number of characters necessary. Places is useful
* for padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -935,19 +548,7 @@ class Engineering
*/
public static function OCTTOHEX($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
- $hexVal = strtoupper(dechex(self::OCTTODEC($x)));
-
- return self::nbrConversionFormat($hexVal, $places);
+ return Engineering\ConvertOctal::toHex($x, $places);
}
/**
@@ -958,6 +559,10 @@ class Engineering
* Excel Function:
* COMPLEX(realNumber,imaginary[,suffix])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the COMPLEX() method in the Engineering\Complex class instead
+ *
* @param float $realNumber the real coefficient of the complex number
* @param float $imaginary the imaginary coefficient of the complex number
* @param string $suffix The suffix for the imaginary component of the complex number.
@@ -967,20 +572,7 @@ class Engineering
*/
public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
{
- $realNumber = ($realNumber === null) ? 0.0 : Functions::flattenSingleValue($realNumber);
- $imaginary = ($imaginary === null) ? 0.0 : Functions::flattenSingleValue($imaginary);
- $suffix = ($suffix === null) ? 'i' : Functions::flattenSingleValue($suffix);
-
- if (
- ((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
- (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))
- ) {
- $complex = new Complex($realNumber, $imaginary, $suffix);
-
- return (string) $complex;
- }
-
- return Functions::VALUE();
+ return Engineering\Complex::COMPLEX($realNumber, $imaginary, $suffix);
}
/**
@@ -991,16 +583,18 @@ class Engineering
* Excel Function:
* IMAGINARY(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMAGINARY() method in the Engineering\Complex class instead
+ *
* @param string $complexNumber the complex number for which you want the imaginary
* coefficient
*
- * @return float
+ * @return float|string
*/
public static function IMAGINARY($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->getImaginary();
+ return Engineering\Complex::IMAGINARY($complexNumber);
}
/**
@@ -1011,15 +605,17 @@ class Engineering
* Excel Function:
* IMREAL(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMREAL() method in the Engineering\Complex class instead
+ *
* @param string $complexNumber the complex number for which you want the real coefficient
*
- * @return float
+ * @return float|string
*/
public static function IMREAL($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->getReal();
+ return Engineering\Complex::IMREAL($complexNumber);
}
/**
@@ -1030,15 +626,17 @@ class Engineering
* Excel Function:
* IMABS(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMABS() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the absolute value
*
- * @return float
+ * @return float|string
*/
public static function IMABS($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->abs();
+ return ComplexFunctions::IMABS($complexNumber);
}
/**
@@ -1050,20 +648,17 @@ class Engineering
* Excel Function:
* IMARGUMENT(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMARGUMENT() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the argument theta
*
* @return float|string
*/
public static function IMARGUMENT($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::DIV0();
- }
-
- return $complex->argument();
+ return ComplexFunctions::IMARGUMENT($complexNumber);
}
/**
@@ -1074,15 +669,17 @@ class Engineering
* Excel Function:
* IMCONJUGATE(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMARGUMENT() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the conjugate
*
* @return string
*/
public static function IMCONJUGATE($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->conjugate();
+ return ComplexFunctions::IMCONJUGATE($complexNumber);
}
/**
@@ -1093,15 +690,17 @@ class Engineering
* Excel Function:
* IMCOS(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMCOS() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the cosine
*
* @return float|string
*/
public static function IMCOS($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cos();
+ return ComplexFunctions::IMCOS($complexNumber);
}
/**
@@ -1112,15 +711,17 @@ class Engineering
* Excel Function:
* IMCOSH(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMCOSH() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the hyperbolic cosine
*
* @return float|string
*/
public static function IMCOSH($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cosh();
+ return ComplexFunctions::IMCOSH($complexNumber);
}
/**
@@ -1131,15 +732,17 @@ class Engineering
* Excel Function:
* IMCOT(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMCOT() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the cotangent
*
* @return float|string
*/
public static function IMCOT($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cot();
+ return ComplexFunctions::IMCOT($complexNumber);
}
/**
@@ -1150,15 +753,17 @@ class Engineering
* Excel Function:
* IMCSC(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMCSC() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the cosecant
*
* @return float|string
*/
public static function IMCSC($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->csc();
+ return ComplexFunctions::IMCSC($complexNumber);
}
/**
@@ -1169,15 +774,17 @@ class Engineering
* Excel Function:
* IMCSCH(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMCSCH() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the hyperbolic cosecant
*
* @return float|string
*/
public static function IMCSCH($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->csch();
+ return ComplexFunctions::IMCSCH($complexNumber);
}
/**
@@ -1188,15 +795,17 @@ class Engineering
* Excel Function:
* IMSIN(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMSIN() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the sine
*
* @return float|string
*/
public static function IMSIN($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sin();
+ return ComplexFunctions::IMSIN($complexNumber);
}
/**
@@ -1207,15 +816,17 @@ class Engineering
* Excel Function:
* IMSINH(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMSINH() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the hyperbolic sine
*
* @return float|string
*/
public static function IMSINH($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sinh();
+ return ComplexFunctions::IMSINH($complexNumber);
}
/**
@@ -1226,15 +837,17 @@ class Engineering
* Excel Function:
* IMSEC(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMSEC() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the secant
*
* @return float|string
*/
public static function IMSEC($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sec();
+ return ComplexFunctions::IMSEC($complexNumber);
}
/**
@@ -1245,15 +858,17 @@ class Engineering
* Excel Function:
* IMSECH(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMSECH() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the hyperbolic secant
*
* @return float|string
*/
public static function IMSECH($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sech();
+ return ComplexFunctions::IMSECH($complexNumber);
}
/**
@@ -1264,15 +879,17 @@ class Engineering
* Excel Function:
* IMTAN(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMTAN() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the tangent
*
* @return float|string
*/
public static function IMTAN($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->tan();
+ return ComplexFunctions::IMTAN($complexNumber);
}
/**
@@ -1283,20 +900,17 @@ class Engineering
* Excel Function:
* IMSQRT(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMSQRT() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the square root
*
* @return string
*/
public static function IMSQRT($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $theta = self::IMARGUMENT($complexNumber);
- if ($theta === Functions::DIV0()) {
- return '0';
- }
-
- return (string) (new Complex($complexNumber))->sqrt();
+ return ComplexFunctions::IMSQRT($complexNumber);
}
/**
@@ -1307,20 +921,17 @@ class Engineering
* Excel Function:
* IMLN(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMLN() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the natural logarithm
*
* @return string
*/
public static function IMLN($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->ln();
+ return ComplexFunctions::IMLN($complexNumber);
}
/**
@@ -1331,20 +942,17 @@ class Engineering
* Excel Function:
* IMLOG10(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMLOG10() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the common logarithm
*
* @return string
*/
public static function IMLOG10($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->log10();
+ return ComplexFunctions::IMLOG10($complexNumber);
}
/**
@@ -1355,20 +963,17 @@ class Engineering
* Excel Function:
* IMLOG2(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMLOG2() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the base-2 logarithm
*
* @return string
*/
public static function IMLOG2($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->log2();
+ return ComplexFunctions::IMLOG2($complexNumber);
}
/**
@@ -1379,15 +984,17 @@ class Engineering
* Excel Function:
* IMEXP(complexNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMEXP() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number for which you want the exponential
*
* @return string
*/
public static function IMEXP($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->exp();
+ return ComplexFunctions::IMEXP($complexNumber);
}
/**
@@ -1398,6 +1005,10 @@ class Engineering
* Excel Function:
* IMPOWER(complexNumber,realNumber)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMPOWER() method in the Engineering\ComplexFunctions class instead
+ *
* @param string $complexNumber the complex number you want to raise to a power
* @param float $realNumber the power to which you want to raise the complex number
*
@@ -1405,14 +1016,7 @@ class Engineering
*/
public static function IMPOWER($complexNumber, $realNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
- $realNumber = Functions::flattenSingleValue($realNumber);
-
- if (!is_numeric($realNumber)) {
- return Functions::VALUE();
- }
-
- return (string) (new Complex($complexNumber))->pow($realNumber);
+ return ComplexFunctions::IMPOWER($complexNumber, $realNumber);
}
/**
@@ -1423,6 +1027,10 @@ class Engineering
* Excel Function:
* IMDIV(complexDividend,complexDivisor)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMDIV() method in the Engineering\ComplexOperations class instead
+ *
* @param string $complexDividend the complex numerator or dividend
* @param string $complexDivisor the complex denominator or divisor
*
@@ -1430,14 +1038,7 @@ class Engineering
*/
public static function IMDIV($complexDividend, $complexDivisor)
{
- $complexDividend = Functions::flattenSingleValue($complexDividend);
- $complexDivisor = Functions::flattenSingleValue($complexDivisor);
-
- try {
- return (string) (new Complex($complexDividend))->divideby(new Complex($complexDivisor));
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
+ return ComplexOperations::IMDIV($complexDividend, $complexDivisor);
}
/**
@@ -1448,6 +1049,10 @@ class Engineering
* Excel Function:
* IMSUB(complexNumber1,complexNumber2)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMSUB() method in the Engineering\ComplexOperations class instead
+ *
* @param string $complexNumber1 the complex number from which to subtract complexNumber2
* @param string $complexNumber2 the complex number to subtract from complexNumber1
*
@@ -1455,14 +1060,7 @@ class Engineering
*/
public static function IMSUB($complexNumber1, $complexNumber2)
{
- $complexNumber1 = Functions::flattenSingleValue($complexNumber1);
- $complexNumber2 = Functions::flattenSingleValue($complexNumber2);
-
- try {
- return (string) (new Complex($complexNumber1))->subtract(new Complex($complexNumber2));
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
+ return ComplexOperations::IMSUB($complexNumber1, $complexNumber2);
}
/**
@@ -1473,26 +1071,17 @@ class Engineering
* Excel Function:
* IMSUM(complexNumber[,complexNumber[,...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMSUM() method in the Engineering\ComplexOperations class instead
+ *
* @param string ...$complexNumbers Series of complex numbers to add
*
* @return string
*/
public static function IMSUM(...$complexNumbers)
{
- // Return value
- $returnValue = new Complex(0.0);
- $aArgs = Functions::flattenArray($complexNumbers);
-
- try {
- // Loop through the arguments
- foreach ($aArgs as $complex) {
- $returnValue = $returnValue->add(new Complex($complex));
- }
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
-
- return (string) $returnValue;
+ return ComplexOperations::IMSUM(...$complexNumbers);
}
/**
@@ -1503,50 +1092,42 @@ class Engineering
* Excel Function:
* IMPRODUCT(complexNumber[,complexNumber[,...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the IMPRODUCT() method in the Engineering\ComplexOperations class instead
+ *
* @param string ...$complexNumbers Series of complex numbers to multiply
*
* @return string
*/
public static function IMPRODUCT(...$complexNumbers)
{
- // Return value
- $returnValue = new Complex(1.0);
- $aArgs = Functions::flattenArray($complexNumbers);
-
- try {
- // Loop through the arguments
- foreach ($aArgs as $complex) {
- $returnValue = $returnValue->multiply(new Complex($complex));
- }
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
-
- return (string) $returnValue;
+ return ComplexOperations::IMPRODUCT(...$complexNumbers);
}
/**
* DELTA.
*
* Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
- * Use this function to filter a set of values. For example, by summing several DELTA
- * functions you calculate the count of equal pairs. This function is also known as the
- * Kronecker Delta function.
+ * Use this function to filter a set of values. For example, by summing several DELTA
+ * functions you calculate the count of equal pairs. This function is also known as the
+ * Kronecker Delta function.
*
* Excel Function:
* DELTA(a[,b])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the DELTA() method in the Engineering\Compare class instead
+ *
* @param float $a the first number
* @param float $b The second number. If omitted, b is assumed to be zero.
*
- * @return int
+ * @return int|string (string in the event of an error)
*/
public static function DELTA($a, $b = 0)
{
- $a = Functions::flattenSingleValue($a);
- $b = Functions::flattenSingleValue($b);
-
- return (int) ($a == $b);
+ return Engineering\Compare::DELTA($a, $b);
}
/**
@@ -1557,77 +1138,20 @@ class Engineering
*
* Returns 1 if number >= step; returns 0 (zero) otherwise
* Use this function to filter a set of values. For example, by summing several GESTEP
- * functions you calculate the count of values that exceed a threshold.
+ * functions you calculate the count of values that exceed a threshold.
+ *
+ * @Deprecated 1.17.0
+ *
+ * @see Use the GESTEP() method in the Engineering\Compare class instead
*
* @param float $number the value to test against step
- * @param float $step The threshold value.
- * If you omit a value for step, GESTEP uses zero.
+ * @param float $step The threshold value. If you omit a value for step, GESTEP uses zero.
*
- * @return int
+ * @return int|string (string in the event of an error)
*/
public static function GESTEP($number, $step = 0)
{
- $number = Functions::flattenSingleValue($number);
- $step = Functions::flattenSingleValue($step);
-
- return (int) ($number >= $step);
- }
-
- //
- // Private method to calculate the erf value
- //
- private static $twoSqrtPi = 1.128379167095512574;
-
- public static function erfVal($x)
- {
- if (abs($x) > 2.2) {
- return 1 - self::erfcVal($x);
- }
- $sum = $term = $x;
- $xsqr = ($x * $x);
- $j = 1;
- do {
- $term *= $xsqr / $j;
- $sum -= $term / (2 * $j + 1);
- ++$j;
- $term *= $xsqr / $j;
- $sum += $term / (2 * $j + 1);
- ++$j;
- if ($sum == 0.0) {
- break;
- }
- } while (abs($term / $sum) > Functions::PRECISION);
-
- return self::$twoSqrtPi * $sum;
- }
-
- /**
- * Validate arguments passed to the bitwise functions.
- *
- * @param mixed $value
- *
- * @return int
- */
- private static function validateBitwiseArgument($value)
- {
- $value = Functions::flattenSingleValue($value);
-
- if (is_int($value)) {
- return $value;
- } elseif (is_numeric($value)) {
- if ($value == (int) ($value)) {
- $value = (int) ($value);
- if (($value > 2 ** 48 - 1) || ($value < 0)) {
- throw new Exception(Functions::NAN());
- }
-
- return $value;
- }
-
- throw new Exception(Functions::NAN());
- }
-
- throw new Exception(Functions::VALUE());
+ return Engineering\Compare::GESTEP($number, $step);
}
/**
@@ -1638,6 +1162,10 @@ class Engineering
* Excel Function:
* BITAND(number1, number2)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BITAND() method in the Engineering\BitWise class instead
+ *
* @param int $number1
* @param int $number2
*
@@ -1645,14 +1173,7 @@ class Engineering
*/
public static function BITAND($number1, $number2)
{
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 & $number2;
+ return Engineering\BitWise::BITAND($number1, $number2);
}
/**
@@ -1663,6 +1184,10 @@ class Engineering
* Excel Function:
* BITOR(number1, number2)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BITOR() method in the Engineering\BitWise class instead
+ *
* @param int $number1
* @param int $number2
*
@@ -1670,14 +1195,7 @@ class Engineering
*/
public static function BITOR($number1, $number2)
{
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 | $number2;
+ return Engineering\BitWise::BITOR($number1, $number2);
}
/**
@@ -1688,6 +1206,10 @@ class Engineering
* Excel Function:
* BITXOR(number1, number2)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BITXOR() method in the Engineering\BitWise class instead
+ *
* @param int $number1
* @param int $number2
*
@@ -1695,14 +1217,7 @@ class Engineering
*/
public static function BITXOR($number1, $number2)
{
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 ^ $number2;
+ return Engineering\BitWise::BITXOR($number1, $number2);
}
/**
@@ -1713,6 +1228,10 @@ class Engineering
* Excel Function:
* BITLSHIFT(number, shift_amount)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BITLSHIFT() method in the Engineering\BitWise class instead
+ *
* @param int $number
* @param int $shiftAmount
*
@@ -1720,20 +1239,7 @@ class Engineering
*/
public static function BITLSHIFT($number, $shiftAmount)
{
- try {
- $number = self::validateBitwiseArgument($number);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- $shiftAmount = Functions::flattenSingleValue($shiftAmount);
-
- $result = $number << $shiftAmount;
- if ($result > 2 ** 48 - 1) {
- return Functions::NAN();
- }
-
- return $result;
+ return Engineering\BitWise::BITLSHIFT($number, $shiftAmount);
}
/**
@@ -1744,6 +1250,10 @@ class Engineering
* Excel Function:
* BITRSHIFT(number, shift_amount)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the BITRSHIFT() method in the Engineering\BitWise class instead
+ *
* @param int $number
* @param int $shiftAmount
*
@@ -1751,15 +1261,7 @@ class Engineering
*/
public static function BITRSHIFT($number, $shiftAmount)
{
- try {
- $number = self::validateBitwiseArgument($number);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- $shiftAmount = Functions::flattenSingleValue($shiftAmount);
-
- return $number >> $shiftAmount;
+ return Engineering\BitWise::BITRSHIFT($number, $shiftAmount);
}
/**
@@ -1775,6 +1277,10 @@ class Engineering
* Excel Function:
* ERF(lower[,upper])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the ERF() method in the Engineering\Erf class instead
+ *
* @param float $lower lower bound for integrating ERF
* @param float $upper upper bound for integrating ERF.
* If omitted, ERF integrates between zero and lower_limit
@@ -1783,19 +1289,7 @@ class Engineering
*/
public static function ERF($lower, $upper = null)
{
- $lower = Functions::flattenSingleValue($lower);
- $upper = Functions::flattenSingleValue($upper);
-
- if (is_numeric($lower)) {
- if ($upper === null) {
- return self::erfVal($lower);
- }
- if (is_numeric($upper)) {
- return self::erfVal($upper) - self::erfVal($lower);
- }
- }
-
- return Functions::VALUE();
+ return Engineering\Erf::ERF($lower, $upper);
}
/**
@@ -1806,48 +1300,17 @@ class Engineering
* Excel Function:
* ERF.PRECISE(limit)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the ERFPRECISE() method in the Engineering\Erf class instead
+ *
* @param float $limit bound for integrating ERF
*
* @return float|string
*/
public static function ERFPRECISE($limit)
{
- $limit = Functions::flattenSingleValue($limit);
-
- return self::ERF($limit);
- }
-
- //
- // Private method to calculate the erfc value
- //
- private static $oneSqrtPi = 0.564189583547756287;
-
- private static function erfcVal($x)
- {
- if (abs($x) < 2.2) {
- return 1 - self::erfVal($x);
- }
- if ($x < 0) {
- return 2 - self::ERFC(-$x);
- }
- $a = $n = 1;
- $b = $c = $x;
- $d = ($x * $x) + 0.5;
- $q1 = $q2 = $b / $d;
- $t = 0;
- do {
- $t = $a * $n + $b * $x;
- $a = $b;
- $b = $t;
- $t = $c * $n + $d * $x;
- $c = $d;
- $d = $t;
- $n += 0.5;
- $q1 = $q2;
- $q2 = $b / $d;
- } while ((abs($q1 - $q2) / $q2) > Functions::PRECISION);
-
- return self::$oneSqrtPi * exp(-$x * $x) * $q2;
+ return Engineering\Erf::ERFPRECISE($limit);
}
/**
@@ -1863,26 +1326,26 @@ class Engineering
* Excel Function:
* ERFC(x)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Use the ERFC() method in the Engineering\ErfC class instead
+ *
* @param float $x The lower bound for integrating ERFC
*
* @return float|string
*/
public static function ERFC($x)
{
- $x = Functions::flattenSingleValue($x);
-
- if (is_numeric($x)) {
- return self::erfcVal($x);
- }
-
- return Functions::VALUE();
+ return Engineering\ErfC::ERFC($x);
}
/**
* getConversionGroups
* Returns a list of the different conversion groups for UOM conversions.
*
- * @Deprecated Use the getConversionCategories() method in the ConvertUOM class instead
+ * @Deprecated 1.16.0
+ *
+ * @see Use the getConversionCategories() method in the Engineering\ConvertUOM class instead
*
* @return array
*/
@@ -1895,7 +1358,9 @@ class Engineering
* getConversionGroupUnits
* Returns an array of units of measure, for a specified conversion group, or for all groups.
*
- * @Deprecated Use the getConversionCategoryUnits() method in the ConvertUOM class instead
+ * @Deprecated 1.16.0
+ *
+ * @see Use the getConversionCategoryUnits() method in the ConvertUOM class instead
*
* @param null|mixed $category
*
@@ -1909,7 +1374,9 @@ class Engineering
/**
* getConversionGroupUnitDetails.
*
- * @Deprecated Use the getConversionCategoryUnitDetails() method in the ConvertUOM class instead
+ * @Deprecated 1.16.0
+ *
+ * @see Use the getConversionCategoryUnitDetails() method in the ConvertUOM class instead
*
* @param null|mixed $category
*
@@ -1924,9 +1391,11 @@ class Engineering
* getConversionMultipliers
* Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
*
- * @Deprecated Use the getConversionMultipliers() method in the ConvertUOM class instead
+ * @Deprecated 1.16.0
*
- * @return array of mixed
+ * @see Use the getConversionMultipliers() method in the ConvertUOM class instead
+ *
+ * @return mixed[]
*/
public static function getConversionMultipliers()
{
@@ -1934,12 +1403,16 @@ class Engineering
}
/**
- * getBinaryConversionMultipliers
- * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM().
+ * getBinaryConversionMultipliers.
*
- * @Deprecated Use the getBinaryConversionMultipliers() method in the ConvertUOM class instead
+ * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure
+ * in CONVERTUOM().
*
- * @return array of mixed
+ * @Deprecated 1.16.0
+ *
+ * @see Use the getBinaryConversionMultipliers() method in the ConvertUOM class instead
+ *
+ * @return mixed[]
*/
public static function getBinaryConversionMultipliers()
{
@@ -1956,7 +1429,9 @@ class Engineering
* Excel Function:
* CONVERT(value,fromUOM,toUOM)
*
- * @Deprecated Use the CONVERT() method in the ConvertUOM class instead
+ * @Deprecated 1.16.0
+ *
+ * @see Use the CONVERT() method in the ConvertUOM class instead
*
* @param float|int $value the value in fromUOM to convert
* @param string $fromUOM the units for value
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
new file mode 100644
index 00000000000..ea2577cdf85
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
@@ -0,0 +1,137 @@
+getMessage();
+ }
+
+ if ($ord < 0) {
+ return Functions::NAN();
+ }
+
+ $fResult = self::calculate($x, $ord);
+
+ return (is_nan($fResult)) ? Functions::NAN() : $fResult;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ // special cases
+ switch ($ord) {
+ case 0:
+ return self::besselI0($x);
+ case 1:
+ return self::besselI1($x);
+ }
+
+ return self::besselI2($x, $ord);
+ }
+
+ private static function besselI0(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 3.75) {
+ $y = $x / 3.75;
+ $y = $y * $y;
+
+ return 1.0 + $y * (3.5156229 + $y * (3.0899424 + $y * (1.2067492
+ + $y * (0.2659732 + $y * (0.360768e-1 + $y * 0.45813e-2)))));
+ }
+
+ $y = 3.75 / $ax;
+
+ return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
+ + $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1 +
+ $y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
+ }
+
+ private static function besselI1(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 3.75) {
+ $y = $x / 3.75;
+ $y = $y * $y;
+ $ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1 +
+ $y * (0.301532e-2 + $y * 0.32411e-3))))));
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ $y = 3.75 / $ax;
+ $ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
+ $ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2 +
+ $y * (-0.1031555e-1 + $y * $ans))));
+ $ans *= exp($ax) / sqrt($ax);
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ private static function besselI2(float $x, int $ord): float
+ {
+ if ($x === 0.0) {
+ return 0.0;
+ }
+
+ $tox = 2.0 / abs($x);
+ $bip = 0;
+ $ans = 0.0;
+ $bi = 1.0;
+
+ for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
+ $bim = $bip + $j * $tox * $bi;
+ $bip = $bi;
+ $bi = $bim;
+
+ if (abs($bi) > 1.0e+12) {
+ $ans *= 1.0e-12;
+ $bi *= 1.0e-12;
+ $bip *= 1.0e-12;
+ }
+
+ if ($j === $ord) {
+ $ans = $bip;
+ }
+ }
+
+ $ans *= self::besselI0($x) / $bi;
+
+ return ($x < 0.0 && (($ord % 2) === 1)) ? -$ans : $ans;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
new file mode 100644
index 00000000000..7ea45a74fff
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
@@ -0,0 +1,172 @@
+ 8. This code provides a more accurate calculation
+ *
+ * @param mixed $x A float value at which to evaluate the function.
+ * If x is nonnumeric, BESSELJ returns the #VALUE! error value.
+ * @param mixed $ord The integer order of the Bessel function.
+ * If ord is not an integer, it is truncated.
+ * If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
+ * If $ord < 0, BESSELJ returns the #NUM! error value.
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function BESSELJ($x, $ord)
+ {
+ $x = Functions::flattenSingleValue($x);
+ $ord = Functions::flattenSingleValue($ord);
+
+ try {
+ $x = EngineeringValidations::validateFloat($x);
+ $ord = EngineeringValidations::validateInt($ord);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($ord < 0) {
+ return Functions::NAN();
+ }
+
+ $fResult = self::calculate($x, $ord);
+
+ return (is_nan($fResult)) ? Functions::NAN() : $fResult;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ // special cases
+ switch ($ord) {
+ case 0:
+ return self::besselJ0($x);
+ case 1:
+ return self::besselJ1($x);
+ }
+
+ return self::besselJ2($x, $ord);
+ }
+
+ private static function besselJ0(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 8.0) {
+ $y = $x * $x;
+ $ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y *
+ (77392.33017 + $y * (-184.9052456)))));
+ $ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y *
+ (267.8532712 + $y * 1.0))));
+
+ return $ans1 / $ans2;
+ }
+
+ $z = 8.0 / $ax;
+ $y = $z * $z;
+ $xx = $ax - 0.785398164;
+ $ans1 = 1.0 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y *
+ (0.7621095161e-6 - $y * 0.934935152e-7)));
+
+ return sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
+ }
+
+ private static function besselJ1(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 8.0) {
+ $y = $x * $x;
+ $ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y *
+ (-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
+ $ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y *
+ (376.9991397 + $y * 1.0))));
+
+ return $ans1 / $ans2;
+ }
+
+ $z = 8.0 / $ax;
+ $y = $z * $z;
+ $xx = $ax - 2.356194491;
+
+ $ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
+ $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
+ (-0.88228987e-6 + $y * 0.105787412e-6)));
+ $ans = sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ private static function besselJ2(float $x, int $ord): float
+ {
+ $ax = abs($x);
+ if ($ax === 0.0) {
+ return 0.0;
+ }
+
+ if ($ax > $ord) {
+ return self::besselj2a($ax, $ord, $x);
+ }
+
+ return self::besselj2b($ax, $ord, $x);
+ }
+
+ private static function besselj2a(float $ax, int $ord, float $x)
+ {
+ $tox = 2.0 / $ax;
+ $bjm = self::besselJ0($ax);
+ $bj = self::besselJ1($ax);
+ for ($j = 1; $j < $ord; ++$j) {
+ $bjp = $j * $tox * $bj - $bjm;
+ $bjm = $bj;
+ $bj = $bjp;
+ }
+ $ans = $bj;
+
+ return ($x < 0.0 && ($ord % 2) == 1) ? -$ans : $ans;
+ }
+
+ private static function besselj2b(float $ax, int $ord, float $x)
+ {
+ $tox = 2.0 / $ax;
+ $jsum = false;
+ $bjp = $ans = $sum = 0.0;
+ $bj = 1.0;
+ for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
+ $bjm = $j * $tox * $bj - $bjp;
+ $bjp = $bj;
+ $bj = $bjm;
+ if (abs($bj) > 1.0e+10) {
+ $bj *= 1.0e-10;
+ $bjp *= 1.0e-10;
+ $ans *= 1.0e-10;
+ $sum *= 1.0e-10;
+ }
+ if ($jsum === true) {
+ $sum += $bj;
+ }
+ $jsum = !$jsum;
+ if ($j === $ord) {
+ $ans = $bjp;
+ }
+ }
+ $sum = 2.0 * $sum - $bj;
+ $ans /= $sum;
+
+ return ($x < 0.0 && ($ord % 2) === 1) ? -$ans : $ans;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
new file mode 100644
index 00000000000..8facdffc79f
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
@@ -0,0 +1,111 @@
+getMessage();
+ }
+
+ if (($ord < 0) || ($x <= 0.0)) {
+ return Functions::NAN();
+ }
+
+ $fBk = self::calculate($x, $ord);
+
+ return (is_nan($fBk)) ? Functions::NAN() : $fBk;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ // special cases
+ switch ($ord) {
+ case 0:
+ return self::besselK0($x);
+ case 1:
+ return self::besselK1($x);
+ }
+
+ return self::besselK2($x, $ord);
+ }
+
+ private static function besselK0(float $x): float
+ {
+ if ($x <= 2) {
+ $fNum2 = $x * 0.5;
+ $y = ($fNum2 * $fNum2);
+
+ return -log($fNum2) * BesselI::BESSELI($x, 0) +
+ (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
+ (0.10750e-3 + $y * 0.74e-5))))));
+ }
+
+ $y = 2 / $x;
+
+ return exp(-$x) / sqrt($x) *
+ (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
+ (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
+ }
+
+ private static function besselK1(float $x): float
+ {
+ if ($x <= 2) {
+ $fNum2 = $x * 0.5;
+ $y = ($fNum2 * $fNum2);
+
+ return log($fNum2) * BesselI::BESSELI($x, 1) +
+ (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
+ (-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
+ }
+
+ $y = 2 / $x;
+
+ return exp(-$x) / sqrt($x) *
+ (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
+ (0.325614e-2 + $y * (-0.68245e-3)))))));
+ }
+
+ private static function besselK2(float $x, int $ord)
+ {
+ $fTox = 2 / $x;
+ $fBkm = self::besselK0($x);
+ $fBk = self::besselK1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fBkp = $fBkm + $n * $fTox * $fBk;
+ $fBkm = $fBk;
+ $fBk = $fBkp;
+ }
+
+ return $fBk;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
new file mode 100644
index 00000000000..7f387497169
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
@@ -0,0 +1,118 @@
+getMessage();
+ }
+
+ if (($ord < 0) || ($x <= 0.0)) {
+ return Functions::NAN();
+ }
+
+ $fBy = self::calculate($x, $ord);
+
+ return (is_nan($fBy)) ? Functions::NAN() : $fBy;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ // special cases
+ switch ($ord) {
+ case 0:
+ return self::besselY0($x);
+ case 1:
+ return self::besselY1($x);
+ }
+
+ return self::besselY2($x, $ord);
+ }
+
+ private static function besselY0(float $x): float
+ {
+ if ($x < 8.0) {
+ $y = ($x * $x);
+ $ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y *
+ (-86327.92757 + $y * 228.4622733))));
+ $ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y *
+ (47447.26470 + $y * (226.1030244 + $y))));
+
+ return $ans1 / $ans2 + 0.636619772 * BesselJ::BESSELJ($x, 0) * log($x);
+ }
+
+ $z = 8.0 / $x;
+ $y = ($z * $z);
+ $xx = $x - 0.785398164;
+ $ans1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y *
+ (-0.934945152e-7))));
+
+ return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
+ }
+
+ private static function besselY1(float $x): float
+ {
+ if ($x < 8.0) {
+ $y = ($x * $x);
+ $ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y *
+ (0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
+ $ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
+ (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
+
+ return ($ans1 / $ans2) + 0.636619772 * (BesselJ::BESSELJ($x, 1) * log($x) - 1 / $x);
+ }
+
+ $z = 8.0 / $x;
+ $y = $z * $z;
+ $xx = $x - 2.356194491;
+ $ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
+ $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
+ (-0.88228987e-6 + $y * 0.105787412e-6)));
+
+ return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
+ }
+
+ private static function besselY2(float $x, int $ord): float
+ {
+ $fTox = 2.0 / $x;
+ $fBym = self::besselY0($x);
+ $fBy = self::besselY1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fByp = $n * $fTox * $fBy - $fBym;
+ $fBym = $fBy;
+ $fBy = $fByp;
+ }
+
+ return $fBy;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
new file mode 100644
index 00000000000..9958f054f2d
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
@@ -0,0 +1,227 @@
+getMessage();
+ }
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
+ }
+
+ /**
+ * BITOR.
+ *
+ * Returns the bitwise OR of two integer values.
+ *
+ * Excel Function:
+ * BITOR(number1, number2)
+ *
+ * @param int $number1
+ * @param int $number2
+ *
+ * @return int|string
+ */
+ public static function BITOR($number1, $number2)
+ {
+ try {
+ $number1 = self::validateBitwiseArgument($number1);
+ $number2 = self::validateBitwiseArgument($number2);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
+ }
+
+ /**
+ * BITXOR.
+ *
+ * Returns the bitwise XOR of two integer values.
+ *
+ * Excel Function:
+ * BITXOR(number1, number2)
+ *
+ * @param int $number1
+ * @param int $number2
+ *
+ * @return int|string
+ */
+ public static function BITXOR($number1, $number2)
+ {
+ try {
+ $number1 = self::validateBitwiseArgument($number1);
+ $number2 = self::validateBitwiseArgument($number2);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
+ }
+
+ /**
+ * BITLSHIFT.
+ *
+ * Returns the number value shifted left by shift_amount bits.
+ *
+ * Excel Function:
+ * BITLSHIFT(number, shift_amount)
+ *
+ * @param int $number
+ * @param int $shiftAmount
+ *
+ * @return float|int|string
+ */
+ public static function BITLSHIFT($number, $shiftAmount)
+ {
+ try {
+ $number = self::validateBitwiseArgument($number);
+ $shiftAmount = self::validateShiftAmount($shiftAmount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = floor($number * (2 ** $shiftAmount));
+ if ($result > 2 ** 48 - 1) {
+ return Functions::NAN();
+ }
+
+ return $result;
+ }
+
+ /**
+ * BITRSHIFT.
+ *
+ * Returns the number value shifted right by shift_amount bits.
+ *
+ * Excel Function:
+ * BITRSHIFT(number, shift_amount)
+ *
+ * @param int $number
+ * @param int $shiftAmount
+ *
+ * @return float|int|string
+ */
+ public static function BITRSHIFT($number, $shiftAmount)
+ {
+ try {
+ $number = self::validateBitwiseArgument($number);
+ $shiftAmount = self::validateShiftAmount($shiftAmount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = floor($number / (2 ** $shiftAmount));
+ if ($result > 2 ** 48 - 1) { // possible because shiftAmount can be negative
+ return Functions::NAN();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validate arguments passed to the bitwise functions.
+ *
+ * @param mixed $value
+ *
+ * @return float|int
+ */
+ private static function validateBitwiseArgument($value)
+ {
+ self::nullFalseTrueToNumber($value);
+
+ if (is_numeric($value)) {
+ if ($value == floor($value)) {
+ if (($value > 2 ** 48 - 1) || ($value < 0)) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return floor($value);
+ }
+
+ throw new Exception(Functions::NAN());
+ }
+
+ throw new Exception(Functions::VALUE());
+ }
+
+ /**
+ * Validate arguments passed to the bitwise functions.
+ *
+ * @param mixed $value
+ *
+ * @return int
+ */
+ private static function validateShiftAmount($value)
+ {
+ self::nullFalseTrueToNumber($value);
+
+ if (is_numeric($value)) {
+ if (abs($value) > 53) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return (int) $value;
+ }
+
+ throw new Exception(Functions::VALUE());
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @param mixed $number
+ */
+ public static function nullFalseTrueToNumber(&$number): void
+ {
+ $number = Functions::flattenSingleValue($number);
+ if ($number === null) {
+ $number = 0;
+ } elseif (is_bool($number)) {
+ $number = (int) $number;
+ }
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
new file mode 100644
index 00000000000..0a6342069c9
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
@@ -0,0 +1,70 @@
+getMessage();
+ }
+
+ return (int) ($a == $b);
+ }
+
+ /**
+ * GESTEP.
+ *
+ * Excel Function:
+ * GESTEP(number[,step])
+ *
+ * Returns 1 if number >= step; returns 0 (zero) otherwise
+ * Use this function to filter a set of values. For example, by summing several GESTEP
+ * functions you calculate the count of values that exceed a threshold.
+ *
+ * @param float $number the value to test against step
+ * @param float $step The threshold value. If you omit a value for step, GESTEP uses zero.
+ *
+ * @return int|string (string in the event of an error)
+ */
+ public static function GESTEP($number, $step = 0)
+ {
+ $number = Functions::flattenSingleValue($number);
+ $step = Functions::flattenSingleValue($step);
+
+ try {
+ $number = EngineeringValidations::validateFloat($number);
+ $step = EngineeringValidations::validateFloat($step);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (int) ($number >= $step);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
new file mode 100644
index 00000000000..1c2f5f773ac
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
@@ -0,0 +1,99 @@
+getMessage();
+ }
+
+ if (($suffix == 'i') || ($suffix == 'j') || ($suffix == '')) {
+ $complex = new ComplexObject($realNumber, $imaginary, $suffix);
+
+ return (string) $complex;
+ }
+
+ return Functions::VALUE();
+ }
+
+ /**
+ * IMAGINARY.
+ *
+ * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMAGINARY(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the imaginary
+ * coefficient
+ *
+ * @return float|string
+ */
+ public static function IMAGINARY($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return $complex->getImaginary();
+ }
+
+ /**
+ * IMREAL.
+ *
+ * Returns the real coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMREAL(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the real coefficient
+ *
+ * @return float|string
+ */
+ public static function IMREAL($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return $complex->getReal();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
new file mode 100644
index 00000000000..3f37f373ff7
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
@@ -0,0 +1,513 @@
+abs();
+ }
+
+ /**
+ * IMARGUMENT.
+ *
+ * Returns the argument theta of a complex number, i.e. the angle in radians from the real
+ * axis to the representation of the number in polar coordinates.
+ *
+ * Excel Function:
+ * IMARGUMENT(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the argument theta
+ *
+ * @return float|string
+ */
+ public static function IMARGUMENT($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return Functions::DIV0();
+ }
+
+ return $complex->argument();
+ }
+
+ /**
+ * IMCONJUGATE.
+ *
+ * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCONJUGATE(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the conjugate
+ *
+ * @return string
+ */
+ public static function IMCONJUGATE($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->conjugate();
+ }
+
+ /**
+ * IMCOS.
+ *
+ * Returns the cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOS(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the cosine
+ *
+ * @return float|string
+ */
+ public static function IMCOS($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->cos();
+ }
+
+ /**
+ * IMCOSH.
+ *
+ * Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOSH(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the hyperbolic cosine
+ *
+ * @return float|string
+ */
+ public static function IMCOSH($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->cosh();
+ }
+
+ /**
+ * IMCOT.
+ *
+ * Returns the cotangent of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOT(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the cotangent
+ *
+ * @return float|string
+ */
+ public static function IMCOT($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->cot();
+ }
+
+ /**
+ * IMCSC.
+ *
+ * Returns the cosecant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCSC(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the cosecant
+ *
+ * @return float|string
+ */
+ public static function IMCSC($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->csc();
+ }
+
+ /**
+ * IMCSCH.
+ *
+ * Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCSCH(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the hyperbolic cosecant
+ *
+ * @return float|string
+ */
+ public static function IMCSCH($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->csch();
+ }
+
+ /**
+ * IMSIN.
+ *
+ * Returns the sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSIN(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the sine
+ *
+ * @return float|string
+ */
+ public static function IMSIN($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->sin();
+ }
+
+ /**
+ * IMSINH.
+ *
+ * Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSINH(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the hyperbolic sine
+ *
+ * @return float|string
+ */
+ public static function IMSINH($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->sinh();
+ }
+
+ /**
+ * IMSEC.
+ *
+ * Returns the secant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSEC(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the secant
+ *
+ * @return float|string
+ */
+ public static function IMSEC($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->sec();
+ }
+
+ /**
+ * IMSECH.
+ *
+ * Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSECH(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the hyperbolic secant
+ *
+ * @return float|string
+ */
+ public static function IMSECH($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->sech();
+ }
+
+ /**
+ * IMTAN.
+ *
+ * Returns the tangent of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMTAN(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the tangent
+ *
+ * @return float|string
+ */
+ public static function IMTAN($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->tan();
+ }
+
+ /**
+ * IMSQRT.
+ *
+ * Returns the square root of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSQRT(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the square root
+ *
+ * @return string
+ */
+ public static function IMSQRT($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ $theta = self::IMARGUMENT($complexNumber);
+ if ($theta === Functions::DIV0()) {
+ return '0';
+ }
+
+ return (string) $complex->sqrt();
+ }
+
+ /**
+ * IMLN.
+ *
+ * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLN(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the natural logarithm
+ *
+ * @return string
+ */
+ public static function IMLN($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->ln();
+ }
+
+ /**
+ * IMLOG10.
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG10(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the common logarithm
+ *
+ * @return string
+ */
+ public static function IMLOG10($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->log10();
+ }
+
+ /**
+ * IMLOG2.
+ *
+ * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG2(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the base-2 logarithm
+ *
+ * @return string
+ */
+ public static function IMLOG2($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->log2();
+ }
+
+ /**
+ * IMEXP.
+ *
+ * Returns the exponential of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMEXP(complexNumber)
+ *
+ * @param string $complexNumber the complex number for which you want the exponential
+ *
+ * @return string
+ */
+ public static function IMEXP($complexNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $complex->exp();
+ }
+
+ /**
+ * IMPOWER.
+ *
+ * Returns a complex number in x + yi or x + yj text format raised to a power.
+ *
+ * Excel Function:
+ * IMPOWER(complexNumber,realNumber)
+ *
+ * @param string $complexNumber the complex number you want to raise to a power
+ * @param float $realNumber the power to which you want to raise the complex number
+ *
+ * @return string
+ */
+ public static function IMPOWER($complexNumber, $realNumber)
+ {
+ $complexNumber = Functions::flattenSingleValue($complexNumber);
+ $realNumber = Functions::flattenSingleValue($realNumber);
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ if (!is_numeric($realNumber)) {
+ return Functions::VALUE();
+ }
+
+ return (string) $complex->pow($realNumber);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
new file mode 100644
index 00000000000..681aad8cafe
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
@@ -0,0 +1,120 @@
+divideby(new ComplexObject($complexDivisor));
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+ }
+
+ /**
+ * IMSUB.
+ *
+ * Returns the difference of two complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUB(complexNumber1,complexNumber2)
+ *
+ * @param string $complexNumber1 the complex number from which to subtract complexNumber2
+ * @param string $complexNumber2 the complex number to subtract from complexNumber1
+ *
+ * @return string
+ */
+ public static function IMSUB($complexNumber1, $complexNumber2)
+ {
+ $complexNumber1 = Functions::flattenSingleValue($complexNumber1);
+ $complexNumber2 = Functions::flattenSingleValue($complexNumber2);
+
+ try {
+ return (string) (new ComplexObject($complexNumber1))->subtract(new ComplexObject($complexNumber2));
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+ }
+
+ /**
+ * IMSUM.
+ *
+ * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUM(complexNumber[,complexNumber[,...]])
+ *
+ * @param string ...$complexNumbers Series of complex numbers to add
+ *
+ * @return string
+ */
+ public static function IMSUM(...$complexNumbers)
+ {
+ // Return value
+ $returnValue = new ComplexObject(0.0);
+ $aArgs = Functions::flattenArray($complexNumbers);
+
+ try {
+ // Loop through the arguments
+ foreach ($aArgs as $complex) {
+ $returnValue = $returnValue->add(new ComplexObject($complex));
+ }
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $returnValue;
+ }
+
+ /**
+ * IMPRODUCT.
+ *
+ * Returns the product of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMPRODUCT(complexNumber[,complexNumber[,...]])
+ *
+ * @param string ...$complexNumbers Series of complex numbers to multiply
+ *
+ * @return string
+ */
+ public static function IMPRODUCT(...$complexNumbers)
+ {
+ // Return value
+ $returnValue = new ComplexObject(1.0);
+ $aArgs = Functions::flattenArray($complexNumbers);
+
+ try {
+ // Loop through the arguments
+ foreach ($aArgs as $complex) {
+ $returnValue = $returnValue->multiply(new ComplexObject($complex));
+ }
+ } catch (ComplexException $e) {
+ return Functions::NAN();
+ }
+
+ return (string) $returnValue;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php
new file mode 100644
index 00000000000..a926db6e1cd
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php
@@ -0,0 +1,11 @@
+ 10) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return (int) $places;
+ }
+
+ throw new Exception(Functions::VALUE());
+ }
+
+ /**
+ * Formats a number base string value with leading zeroes.
+ *
+ * @param string $value The "number" to pad
+ * @param ?int $places The length that we want to pad this value
+ *
+ * @return string The padded "number"
+ */
+ protected static function nbrConversionFormat(string $value, ?int $places): string
+ {
+ if ($places !== null) {
+ if (strlen($value) <= $places) {
+ return substr(str_pad($value, $places, '0', STR_PAD_LEFT), -10);
+ }
+
+ return Functions::NAN();
+ }
+
+ return substr($value, -10);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
new file mode 100644
index 00000000000..a662b78de30
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
@@ -0,0 +1,134 @@
+getMessage();
+ }
+
+ if (strlen($value) == 10) {
+ // Two's Complement
+ $value = substr($value, -9);
+
+ return '-' . (512 - bindec($value));
+ }
+
+ return (string) bindec($value);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return a binary value as hex.
+ *
+ * Excel Function:
+ * BIN2HEX(x[,places])
+ *
+ * @param string $value The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
+ * @param int $places The number of characters to use. If places is omitted, BIN2HEX uses the
+ * minimum number of characters necessary. Places is useful for padding the
+ * return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
+ * If places is negative, BIN2HEX returns the #NUM! error value.
+ */
+ public static function toHex($value, $places = null): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateBinary($value);
+ $places = self::validatePlaces(Functions::flattenSingleValue($places));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) == 10) {
+ $high2 = substr($value, 0, 2);
+ $low8 = substr($value, 2);
+ $xarr = ['00' => '00000000', '01' => '00000001', '10' => 'FFFFFFFE', '11' => 'FFFFFFFF'];
+
+ return $xarr[$high2] . strtoupper(substr('0' . dechex((int) bindec($low8)), -2));
+ }
+ $hexVal = (string) strtoupper(dechex((int) bindec($value)));
+
+ return self::nbrConversionFormat($hexVal, $places);
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return a binary value as octal.
+ *
+ * Excel Function:
+ * BIN2OCT(x[,places])
+ *
+ * @param string $value The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
+ * @param int $places The number of characters to use. If places is omitted, BIN2OCT uses the
+ * minimum number of characters necessary. Places is useful for padding the
+ * return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
+ * If places is negative, BIN2OCT returns the #NUM! error value.
+ */
+ public static function toOctal($value, $places = null): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateBinary($value);
+ $places = self::validatePlaces(Functions::flattenSingleValue($places));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) == 10 && substr($value, 0, 1) === '1') { // Two's Complement
+ return str_repeat('7', 6) . strtoupper(decoct((int) bindec("11$value")));
+ }
+ $octVal = (string) decoct((int) bindec($value));
+
+ return self::nbrConversionFormat($octVal, $places);
+ }
+
+ protected static function validateBinary(string $value): string
+ {
+ if ((strlen($value) > preg_match_all('/[01]/', $value)) || (strlen($value) > 10)) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
new file mode 100644
index 00000000000..a34332fb353
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
@@ -0,0 +1,183 @@
+ 511, DEC2BIN returns the #NUM! error
+ * value.
+ * If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If DEC2BIN requires more than places characters, it returns the #NUM!
+ * error value.
+ * @param int $places The number of characters to use. If places is omitted, DEC2BIN uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If places is zero or negative, DEC2BIN returns the #NUM! error value.
+ */
+ public static function toBinary($value, $places = null): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces(Functions::flattenSingleValue($places));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = (int) floor((float) $value);
+ if ($value > self::LARGEST_BINARY_IN_DECIMAL || $value < self::SMALLEST_BINARY_IN_DECIMAL) {
+ return Functions::NAN();
+ }
+
+ $r = decbin($value);
+ // Two's Complement
+ $r = substr($r, -10);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return a decimal value as hex.
+ *
+ * Excel Function:
+ * DEC2HEX(x[,places])
+ *
+ * @param string $value The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2HEX returns a 10-character (40-bit)
+ * hexadecimal number in which the most significant bit is the sign
+ * bit. The remaining 39 bits are magnitude bits. Negative numbers
+ * are represented using two's-complement notation.
+ * If number < -549,755,813,888 or if number > 549,755,813,887,
+ * DEC2HEX returns the #NUM! error value.
+ * If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If DEC2HEX requires more than places characters, it returns the
+ * #NUM! error value.
+ * @param int $places The number of characters to use. If places is omitted, DEC2HEX uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If places is zero or negative, DEC2HEX returns the #NUM! error value.
+ */
+ public static function toHex($value, $places = null): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces(Functions::flattenSingleValue($places));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = floor((float) $value);
+ if ($value > self::LARGEST_HEX_IN_DECIMAL || $value < self::SMALLEST_HEX_IN_DECIMAL) {
+ return Functions::NAN();
+ }
+ $r = strtoupper(dechex((int) $value));
+ $r = self::hex32bit($value, $r);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ public static function hex32bit(float $value, string $hexstr, bool $force = false): string
+ {
+ if (PHP_INT_SIZE === 4 || $force) {
+ if ($value >= 2 ** 32) {
+ $quotient = (int) ($value / (2 ** 32));
+
+ return strtoupper(substr('0' . dechex($quotient), -2) . $hexstr);
+ }
+ if ($value < -(2 ** 32)) {
+ $quotient = 256 - (int) ceil((-$value) / (2 ** 32));
+
+ return strtoupper(substr('0' . dechex($quotient), -2) . substr("00000000$hexstr", -8));
+ }
+ if ($value < 0) {
+ return "FF$hexstr";
+ }
+ }
+
+ return $hexstr;
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return an decimal value as octal.
+ *
+ * Excel Function:
+ * DEC2OCT(x[,places])
+ *
+ * @param string $value The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2OCT returns a 10-character (30-bit)
+ * octal number in which the most significant bit is the sign bit.
+ * The remaining 29 bits are magnitude bits. Negative numbers are
+ * represented using two's-complement notation.
+ * If number < -536,870,912 or if number > 536,870,911, DEC2OCT
+ * returns the #NUM! error value.
+ * If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If DEC2OCT requires more than places characters, it returns the
+ * #NUM! error value.
+ * @param int $places The number of characters to use. If places is omitted, DEC2OCT uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If places is zero or negative, DEC2OCT returns the #NUM! error value.
+ */
+ public static function toOctal($value, $places = null): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces(Functions::flattenSingleValue($places));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = (int) floor((float) $value);
+ if ($value > self::LARGEST_OCTAL_IN_DECIMAL || $value < self::SMALLEST_OCTAL_IN_DECIMAL) {
+ return Functions::NAN();
+ }
+ $r = decoct($value);
+ $r = substr($r, -10);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ protected static function validateDecimal(string $value): string
+ {
+ if (strlen($value) > preg_match_all('/[-0123456789.]/', $value)) {
+ throw new Exception(Functions::VALUE());
+ }
+
+ return $value;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
new file mode 100644
index 00000000000..de1b0704f78
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
@@ -0,0 +1,146 @@
+getMessage();
+ }
+
+ $dec = self::toDecimal($value);
+
+ return ConvertDecimal::toBinary($dec, $places);
+ }
+
+ /**
+ * toDecimal.
+ *
+ * Return a hex value as decimal.
+ *
+ * Excel Function:
+ * HEX2DEC(x)
+ *
+ * @param string $value The hexadecimal number you want to convert. This number cannot
+ * contain more than 10 characters (40 bits). The most significant
+ * bit of number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is not a valid hexadecimal number, HEX2DEC returns the
+ * #NUM! error value.
+ */
+ public static function toDecimal($value): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateHex($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) > 10) {
+ return Functions::NAN();
+ }
+
+ $binX = '';
+ foreach (str_split($value) as $char) {
+ $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
+ }
+ if (strlen($binX) == 40 && $binX[0] == '1') {
+ for ($i = 0; $i < 40; ++$i) {
+ $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
+ }
+
+ return (string) ((bindec($binX) + 1) * -1);
+ }
+
+ return (string) bindec($binX);
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return a hex value as octal.
+ *
+ * Excel Function:
+ * HEX2OCT(x[,places])
+ *
+ * @param string $value The hexadecimal number you want to convert. Number cannot
+ * contain more than 10 characters. The most significant bit of
+ * number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is negative, HEX2OCT ignores places and returns a
+ * 10-character octal number.
+ * If number is negative, it cannot be less than FFE0000000, and
+ * if number is positive, it cannot be greater than 1FFFFFFF.
+ * If number is not a valid hexadecimal number, HEX2OCT returns
+ * the #NUM! error value.
+ * If HEX2OCT requires more than places characters, it returns
+ * the #NUM! error value.
+ * @param int $places The number of characters to use. If places is omitted, HEX2OCT
+ * uses the minimum number of characters necessary. Places is
+ * useful for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, HEX2OCT returns the #VALUE! error
+ * value.
+ * If places is negative, HEX2OCT returns the #NUM! error value.
+ */
+ public static function toOctal($value, $places = null): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateHex($value);
+ $places = self::validatePlaces(Functions::flattenSingleValue($places));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $decimal = self::toDecimal($value);
+
+ return ConvertDecimal::toOctal($decimal, $places);
+ }
+
+ protected static function validateHex(string $value): string
+ {
+ if (strlen($value) > preg_match_all('/[0123456789ABCDEF]/', $value)) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
new file mode 100644
index 00000000000..1181e2ee191
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
@@ -0,0 +1,145 @@
+getMessage();
+ }
+
+ return ConvertDecimal::toBinary(self::toDecimal($value), $places);
+ }
+
+ /**
+ * toDecimal.
+ *
+ * Return an octal value as decimal.
+ *
+ * Excel Function:
+ * OCT2DEC(x)
+ *
+ * @param string $value The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is not a valid octal number, OCT2DEC returns the
+ * #NUM! error value.
+ */
+ public static function toDecimal($value): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateOctal($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $binX = '';
+ foreach (str_split($value) as $char) {
+ $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
+ }
+ if (strlen($binX) == 30 && $binX[0] == '1') {
+ for ($i = 0; $i < 30; ++$i) {
+ $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
+ }
+
+ return (string) ((bindec($binX) + 1) * -1);
+ }
+
+ return (string) bindec($binX);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return an octal value as hex.
+ *
+ * Excel Function:
+ * OCT2HEX(x[,places])
+ *
+ * @param string $value The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is negative, OCT2HEX ignores places and returns a
+ * 10-character hexadecimal number.
+ * If number is not a valid octal number, OCT2HEX returns the
+ * #NUM! error value.
+ * If OCT2HEX requires more than places characters, it returns
+ * the #NUM! error value.
+ * @param int $places The number of characters to use. If places is omitted, OCT2HEX
+ * uses the minimum number of characters necessary. Places is useful
+ * for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
+ * If places is negative, OCT2HEX returns the #NUM! error value.
+ */
+ public static function toHex($value, $places = null): string
+ {
+ try {
+ $value = self::validateValue(Functions::flattenSingleValue($value));
+ $value = self::validateOctal($value);
+ $places = self::validatePlaces(Functions::flattenSingleValue($places));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $hexVal = strtoupper(dechex((int) self::toDecimal($value)));
+ $hexVal = (PHP_INT_SIZE === 4 && strlen($value) === 10 && $value[0] >= '4') ? "FF$hexVal" : $hexVal;
+
+ return self::nbrConversionFormat($hexVal, $places);
+ }
+
+ protected static function validateOctal(string $value): string
+ {
+ $numDigits = (int) preg_match_all('/[01234567]/', $value);
+ if (strlen($value) > $numDigits || $numDigits > 10) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
index 0aafe05ec3a..d169ae54bb0 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
@@ -490,7 +490,7 @@ class ConvertUOM
* getConversionMultipliers
* Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
*
- * @return array of mixed
+ * @return mixed[]
*/
public static function getConversionMultipliers()
{
@@ -501,7 +501,7 @@ class ConvertUOM
* getBinaryConversionMultipliers
* Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM().
*
- * @return array of mixed
+ * @return mixed[]
*/
public static function getBinaryConversionMultipliers()
{
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
new file mode 100644
index 00000000000..01630af3412
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
@@ -0,0 +1,33 @@
+ 2.2) {
+ return 1 - ErfC::ERFC($value);
+ }
+ $sum = $term = $value;
+ $xsqr = ($value * $value);
+ $j = 1;
+ do {
+ $term *= $xsqr / $j;
+ $sum -= $term / (2 * $j + 1);
+ ++$j;
+ $term *= $xsqr / $j;
+ $sum += $term / (2 * $j + 1);
+ ++$j;
+ if ($sum == 0.0) {
+ break;
+ }
+ } while (abs($term / $sum) > Functions::PRECISION);
+
+ return self::$twoSqrtPi * $sum;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
new file mode 100644
index 00000000000..c57a28f490c
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
@@ -0,0 +1,68 @@
+ Functions::PRECISION);
+
+ return self::$oneSqrtPi * exp(-$value * $value) * $q2;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
index 5a908aa5136..9d933b4a921 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
@@ -2,167 +2,81 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Amortization;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Depreciation;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Dollar;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\InterestRate;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\TreasuryBill;
+/**
+ * @deprecated 1.18.0
+ */
class Financial
{
const FINANCIAL_MAX_ITERATIONS = 128;
const FINANCIAL_PRECISION = 1.0e-08;
- /**
- * isLastDayOfMonth.
- *
- * Returns a boolean TRUE/FALSE indicating if this date is the last date of the month
- *
- * @param \DateTime $testDate The date for testing
- *
- * @return bool
- */
- private static function isLastDayOfMonth(\DateTime $testDate)
- {
- return $testDate->format('d') == $testDate->format('t');
- }
-
- private static function couponFirstPeriodDate($settlement, $maturity, $frequency, $next)
- {
- $months = 12 / $frequency;
-
- $result = Date::excelToDateTimeObject($maturity);
- $eom = self::isLastDayOfMonth($result);
-
- while ($settlement < Date::PHPToExcel($result)) {
- $result->modify('-' . $months . ' months');
- }
- if ($next) {
- $result->modify('+' . $months . ' months');
- }
-
- if ($eom) {
- $result->modify('-1 day');
- }
-
- return Date::PHPToExcel($result);
- }
-
- private static function isValidFrequency($frequency)
- {
- if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * daysPerYear.
- *
- * Returns the number of days in a specified year, as defined by the "basis" value
- *
- * @param int|string $year The year against which we're testing
- * @param int|string $basis The type of day count:
- * 0 or omitted US (NASD) 360
- * 1 Actual (365 or 366 in a leap year)
- * 2 360
- * 3 365
- * 4 European 360
- *
- * @return int|string Result, or a string containing an error
- */
- private static function daysPerYear($year, $basis = 0)
- {
- switch ($basis) {
- case 0:
- case 2:
- case 4:
- $daysPerYear = 360;
-
- break;
- case 3:
- $daysPerYear = 365;
-
- break;
- case 1:
- $daysPerYear = (DateTime::isLeapYear($year)) ? 366 : 365;
-
- break;
- default:
- return Functions::NAN();
- }
-
- return $daysPerYear;
- }
-
- private static function interestAndPrincipal($rate = 0, $per = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
- {
- $pmt = self::PMT($rate, $nper, $pv, $fv, $type);
- $capital = $pv;
- for ($i = 1; $i <= $per; ++$i) {
- $interest = ($type && $i == 1) ? 0 : -$capital * $rate;
- $principal = $pmt - $interest;
- $capital += $principal;
- }
-
- return [$interest, $principal];
- }
-
/**
* ACCRINT.
*
* Returns the accrued interest for a security that pays periodic interest.
*
* Excel Function:
- * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis])
+ * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis][,calc_method])
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\AccruedInterest::periodic()
+ * Use the periodic() method in the Financial\Securities\AccruedInterest class instead
*
* @param mixed $issue the security's issue date
- * @param mixed $firstinterest the security's first interest date
+ * @param mixed $firstInterest the security's first interest date
* @param mixed $settlement The security's settlement date.
- * The security settlement date is the date after the issue date
- * when the security is traded to the buyer.
- * @param float $rate the security's annual coupon rate
- * @param float $par The security's par value.
- * If you omit par, ACCRINT uses $1,000.
- * @param int $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * @param int $basis The type of day count to use.
- * 0 or omitted US (NASD) 30/360
- * 1 Actual/actual
- * 2 Actual/360
- * 3 Actual/365
- * 4 European 30/360
+ * The security settlement date is the date after the issue date
+ * when the security is traded to the buyer.
+ * @param mixed $rate the security's annual coupon rate
+ * @param mixed $parValue The security's par value.
+ * If you omit par, ACCRINT uses $1,000.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @param mixed $calcMethod
+ * If true, use Issue to Settlement
+ * If false, use FirstInterest to Settlement
*
* @return float|string Result, or a string containing an error
*/
- public static function ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0)
- {
- $issue = Functions::flattenSingleValue($issue);
- $firstinterest = Functions::flattenSingleValue($firstinterest);
- $settlement = Functions::flattenSingleValue($settlement);
- $rate = Functions::flattenSingleValue($rate);
- $par = ($par === null) ? 1000 : Functions::flattenSingleValue($par);
- $frequency = ($frequency === null) ? 1 : Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($rate)) && (is_numeric($par))) {
- $rate = (float) $rate;
- $par = (float) $par;
- if (($rate <= 0) || ($par <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
-
- return $par * $rate * $daysBetweenIssueAndSettlement;
- }
-
- return Functions::VALUE();
+ public static function ACCRINT(
+ $issue,
+ $firstInterest,
+ $settlement,
+ $rate,
+ $parValue = 1000,
+ $frequency = 1,
+ $basis = 0,
+ $calcMethod = true
+ ) {
+ return Securities\AccruedInterest::periodic(
+ $issue,
+ $firstInterest,
+ $settlement,
+ $rate,
+ $parValue,
+ $frequency,
+ $basis,
+ $calcMethod
+ );
}
/**
@@ -173,45 +87,28 @@ class Financial
* Excel Function:
* ACCRINTM(issue,settlement,rate[,par[,basis]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\AccruedInterest::atMaturity()
+ * Use the atMaturity() method in the Financial\Securities\AccruedInterest class instead
+ *
* @param mixed $issue The security's issue date
* @param mixed $settlement The security's settlement (or maturity) date
- * @param float $rate The security's annual coupon rate
- * @param float $par The security's par value.
- * If you omit par, ACCRINT uses $1,000.
- * @param int $basis The type of day count to use.
- * 0 or omitted US (NASD) 30/360
- * 1 Actual/actual
- * 2 Actual/360
- * 3 Actual/365
- * 4 European 30/360
+ * @param mixed $rate The security's annual coupon rate
+ * @param mixed $parValue The security's par value.
+ * If you omit par, ACCRINT uses $1,000.
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
- public static function ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0)
+ public static function ACCRINTM($issue, $settlement, $rate, $parValue = 1000, $basis = 0)
{
- $issue = Functions::flattenSingleValue($issue);
- $settlement = Functions::flattenSingleValue($settlement);
- $rate = Functions::flattenSingleValue($rate);
- $par = ($par === null) ? 1000 : Functions::flattenSingleValue($par);
- $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($rate)) && (is_numeric($par))) {
- $rate = (float) $rate;
- $par = (float) $par;
- if (($rate <= 0) || ($par <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
-
- return $par * $rate * $daysBetweenIssueAndSettlement;
- }
-
- return Functions::VALUE();
+ return Securities\AccruedInterest::atMaturity($issue, $settlement, $rate, $parValue, $basis);
}
/**
@@ -229,6 +126,11 @@ class Financial
* Excel Function:
* AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Amortization::AMORDEGRC()
+ * Use the AMORDEGRC() method in the Financial\Amortization class instead
+ *
* @param float $cost The cost of the asset
* @param mixed $purchased Date of the purchase of the asset
* @param mixed $firstPeriod Date of the end of the first period
@@ -236,63 +138,17 @@ class Financial
* @param float $period The period
* @param float $rate Rate of depreciation
* @param int $basis The type of day count to use.
- * 0 or omitted US (NASD) 30/360
- * 1 Actual/actual
- * 2 Actual/360
- * 3 Actual/365
- * 4 European 30/360
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
- * @return float
+ * @return float|string (string containing the error type if there is an error)
*/
public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
{
- $cost = Functions::flattenSingleValue($cost);
- $purchased = Functions::flattenSingleValue($purchased);
- $firstPeriod = Functions::flattenSingleValue($firstPeriod);
- $salvage = Functions::flattenSingleValue($salvage);
- $period = floor(Functions::flattenSingleValue($period));
- $rate = Functions::flattenSingleValue($rate);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- // The depreciation coefficients are:
- // Life of assets (1/rate) Depreciation coefficient
- // Less than 3 years 1
- // Between 3 and 4 years 1.5
- // Between 5 and 6 years 2
- // More than 6 years 2.5
- $fUsePer = 1.0 / $rate;
- if ($fUsePer < 3.0) {
- $amortiseCoeff = 1.0;
- } elseif ($fUsePer < 5.0) {
- $amortiseCoeff = 1.5;
- } elseif ($fUsePer <= 6.0) {
- $amortiseCoeff = 2.0;
- } else {
- $amortiseCoeff = 2.5;
- }
-
- $rate *= $amortiseCoeff;
- $fNRate = round(DateTime::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost, 0);
- $cost -= $fNRate;
- $fRest = $cost - $salvage;
-
- for ($n = 0; $n < $period; ++$n) {
- $fNRate = round($rate * $cost, 0);
- $fRest -= $fNRate;
-
- if ($fRest < 0.0) {
- switch ($period - $n) {
- case 0:
- case 1:
- return round($cost * 0.5, 0);
- default:
- return 0.0;
- }
- }
- $cost -= $fNRate;
- }
-
- return $fNRate;
+ return Amortization::AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis);
}
/**
@@ -305,6 +161,11 @@ class Financial
* Excel Function:
* AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Amortization::AMORLINC()
+ * Use the AMORLINC() method in the Financial\Amortization class instead
+ *
* @param float $cost The cost of the asset
* @param mixed $purchased Date of the purchase of the asset
* @param mixed $firstPeriod Date of the end of the first period
@@ -312,46 +173,17 @@ class Financial
* @param float $period The period
* @param float $rate Rate of depreciation
* @param int $basis The type of day count to use.
- * 0 or omitted US (NASD) 30/360
- * 1 Actual/actual
- * 2 Actual/360
- * 3 Actual/365
- * 4 European 30/360
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
- * @return float
+ * @return float|string (string containing the error type if there is an error)
*/
public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
{
- $cost = Functions::flattenSingleValue($cost);
- $purchased = Functions::flattenSingleValue($purchased);
- $firstPeriod = Functions::flattenSingleValue($firstPeriod);
- $salvage = Functions::flattenSingleValue($salvage);
- $period = Functions::flattenSingleValue($period);
- $rate = Functions::flattenSingleValue($rate);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- $fOneRate = $cost * $rate;
- $fCostDelta = $cost - $salvage;
- // Note, quirky variation for leap years on the YEARFRAC for this function
- $purchasedYear = DateTime::YEAR($purchased);
- $yearFrac = DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
-
- if (($basis == 1) && ($yearFrac < 1) && (DateTime::isLeapYear($purchasedYear))) {
- $yearFrac *= 365 / 366;
- }
-
- $f0Rate = $yearFrac * $rate * $cost;
- $nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
-
- if ($period == 0) {
- return $f0Rate;
- } elseif ($period <= $nNumOfFullPeriods) {
- return $fOneRate;
- } elseif ($period == ($nNumOfFullPeriods + 1)) {
- return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
- }
-
- return 0.0;
+ return Amortization::AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis);
}
/**
@@ -362,6 +194,11 @@ class Financial
* Excel Function:
* COUPDAYBS(settlement,maturity,frequency[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Coupons::COUPDAYBS()
+ * Use the COUPDAYBS() method in the Financial\Coupons class instead
+ *
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
* date when the security is traded to the buyer.
@@ -383,34 +220,7 @@ class Financial
*/
public static function COUPDAYBS($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
-
- if ($basis == 1) {
- return abs(DateTime::DAYS($prev, $settlement));
- }
-
- return DateTime::YEARFRAC($prev, $settlement, $basis) * $daysPerYear;
+ return Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
}
/**
@@ -421,6 +231,11 @@ class Financial
* Excel Function:
* COUPDAYS(settlement,maturity,frequency[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Coupons::COUPDAYS()
+ * Use the COUPDAYS() method in the Financial\Coupons class instead
+ *
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
* date when the security is traded to the buyer.
@@ -442,45 +257,7 @@ class Financial
*/
public static function COUPDAYS($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- switch ($basis) {
- case 3:
- // Actual/365
- return 365 / $frequency;
- case 1:
- // Actual/actual
- if ($frequency == 1) {
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
-
- return $daysPerYear / $frequency;
- }
- $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
- $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
-
- return $next - $prev;
- default:
- // US (NASD) 30/360, Actual/360 or European 30/360
- return 360 / $frequency;
- }
+ return Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
}
/**
@@ -491,6 +268,11 @@ class Financial
* Excel Function:
* COUPDAYSNC(settlement,maturity,frequency[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Coupons::COUPDAYSNC()
+ * Use the COUPDAYSNC() method in the Financial\Coupons class instead
+ *
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
* date when the security is traded to the buyer.
@@ -512,30 +294,7 @@ class Financial
*/
public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
-
- return DateTime::YEARFRAC($settlement, $next, $basis) * $daysPerYear;
+ return Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
}
/**
@@ -546,6 +305,11 @@ class Financial
* Excel Function:
* COUPNCD(settlement,maturity,frequency[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Coupons::COUPNCD()
+ * Use the COUPNCD() method in the Financial\Coupons class instead
+ *
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
* date when the security is traded to the buyer.
@@ -568,27 +332,7 @@ class Financial
*/
public static function COUPNCD($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- return self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
+ return Coupons::COUPNCD($settlement, $maturity, $frequency, $basis);
}
/**
@@ -600,6 +344,11 @@ class Financial
* Excel Function:
* COUPNUM(settlement,maturity,frequency[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Coupons::COUPNUM()
+ * Use the COUPNUM() method in the Financial\Coupons class instead
+ *
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
* date when the security is traded to the buyer.
@@ -621,29 +370,7 @@ class Financial
*/
public static function COUPNUM($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- $yearsBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, 0);
-
- return ceil($yearsBetweenSettlementAndMaturity * $frequency);
+ return Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
}
/**
@@ -654,6 +381,11 @@ class Financial
* Excel Function:
* COUPPCD(settlement,maturity,frequency[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Coupons::COUPPCD()
+ * Use the COUPPCD() method in the Financial\Coupons class instead
+ *
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
* date when the security is traded to the buyer.
@@ -676,27 +408,7 @@ class Financial
*/
public static function COUPPCD($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- return self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
+ return Coupons::COUPPCD($settlement, $maturity, $frequency, $basis);
}
/**
@@ -707,42 +419,26 @@ class Financial
* Excel Function:
* CUMIPMT(rate,nper,pv,start,end[,type])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic\Cumulative::interest()
+ * Use the interest() method in the Financial\CashFlow\Constant\Periodic\Cumulative class instead
+ *
* @param float $rate The Interest rate
* @param int $nper The total number of payment periods
* @param float $pv Present Value
* @param int $start The first period in the calculation.
- * Payment periods are numbered beginning with 1.
+ * Payment periods are numbered beginning with 1.
* @param int $end the last period in the calculation
* @param int $type A number 0 or 1 and indicates when payments are due:
- * 0 or omitted At the end of the period.
- * 1 At the beginning of the period.
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
*
* @return float|string
*/
public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $start = (int) Functions::flattenSingleValue($start);
- $end = (int) Functions::flattenSingleValue($end);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($start < 1 || $start > $end) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interest = 0;
- for ($per = $start; $per <= $end; ++$per) {
- $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
- }
-
- return $interest;
+ return Financial\CashFlow\Constant\Periodic\Cumulative::interest($rate, $nper, $pv, $start, $end, $type);
}
/**
@@ -753,42 +449,26 @@ class Financial
* Excel Function:
* CUMPRINC(rate,nper,pv,start,end[,type])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic\Cumulative::principal()
+ * Use the principal() method in the Financial\CashFlow\Constant\Periodic\Cumulative class instead
+ *
* @param float $rate The Interest rate
* @param int $nper The total number of payment periods
* @param float $pv Present Value
* @param int $start The first period in the calculation.
- * Payment periods are numbered beginning with 1.
+ * Payment periods are numbered beginning with 1.
* @param int $end the last period in the calculation
* @param int $type A number 0 or 1 and indicates when payments are due:
- * 0 or omitted At the end of the period.
- * 1 At the beginning of the period.
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
*
* @return float|string
*/
public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $start = (int) Functions::flattenSingleValue($start);
- $end = (int) Functions::flattenSingleValue($end);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($start < 1 || $start > $end) {
- return Functions::VALUE();
- }
-
- // Calculate
- $principal = 0;
- for ($per = $start; $per <= $end; ++$per) {
- $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
- }
-
- return $principal;
+ return Financial\CashFlow\Constant\Periodic\Cumulative::principal($rate, $nper, $pv, $start, $end, $type);
}
/**
@@ -804,6 +484,11 @@ class Financial
* Excel Function:
* DB(cost,salvage,life,period[,month])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Depreciation::DB()
+ * Use the DB() method in the Financial\Depreciation class instead
+ *
* @param float $cost Initial cost of the asset
* @param float $salvage Value at the end of the depreciation.
* (Sometimes called the salvage value of the asset)
@@ -818,46 +503,7 @@ class Financial
*/
public static function DB($cost, $salvage, $life, $period, $month = 12)
{
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
- $month = Functions::flattenSingleValue($month);
-
- // Validate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
- $cost = (float) $cost;
- $salvage = (float) $salvage;
- $life = (int) $life;
- $period = (int) $period;
- $month = (int) $month;
- if ($cost == 0) {
- return 0.0;
- } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
- return Functions::NAN();
- }
- // Set Fixed Depreciation Rate
- $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
- $fixedDepreciationRate = round($fixedDepreciationRate, 3);
-
- // Loop through each period calculating the depreciation
- $previousDepreciation = 0;
- $depreciation = 0;
- for ($per = 1; $per <= $period; ++$per) {
- if ($per == 1) {
- $depreciation = $cost * $fixedDepreciationRate * $month / 12;
- } elseif ($per == ($life + 1)) {
- $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
- } else {
- $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
- }
- $previousDepreciation += $depreciation;
- }
-
- return $depreciation;
- }
-
- return Functions::VALUE();
+ return Depreciation::DB($cost, $salvage, $life, $period, $month);
}
/**
@@ -869,6 +515,11 @@ class Financial
* Excel Function:
* DDB(cost,salvage,life,period[,factor])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Depreciation::DDB()
+ * Use the DDB() method in the Financial\Depreciation class instead
+ *
* @param float $cost Initial cost of the asset
* @param float $salvage Value at the end of the depreciation.
* (Sometimes called the salvage value of the asset)
@@ -884,38 +535,7 @@ class Financial
*/
public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
{
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
- $factor = Functions::flattenSingleValue($factor);
-
- // Validate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
- $cost = (float) $cost;
- $salvage = (float) $salvage;
- $life = (int) $life;
- $period = (int) $period;
- $factor = (float) $factor;
- if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
- return Functions::NAN();
- }
- // Set Fixed Depreciation Rate
- $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
- $fixedDepreciationRate = round($fixedDepreciationRate, 3);
-
- // Loop through each period calculating the depreciation
- $previousDepreciation = 0;
- $depreciation = 0;
- for ($per = 1; $per <= $period; ++$per) {
- $depreciation = min(($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation));
- $previousDepreciation += $depreciation;
- }
-
- return $depreciation;
- }
-
- return Functions::VALUE();
+ return Depreciation::DDB($cost, $salvage, $life, $period, $factor);
}
/**
@@ -926,6 +546,11 @@ class Financial
* Excel Function:
* DISC(settlement,maturity,price,redemption[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\Rates::discount()
+ * Use the discount() method in the Financial\Securities\Rates class instead
+ *
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
* date when the security is traded to the buyer.
@@ -944,30 +569,7 @@ class Financial
*/
public static function DISC($settlement, $maturity, $price, $redemption, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- $price = (float) $price;
- $redemption = (float) $redemption;
- $basis = (int) $basis;
- if (($price <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
- }
-
- return Functions::VALUE();
+ return Financial\Securities\Rates::discount($settlement, $maturity, $price, $redemption, $basis);
}
/**
@@ -980,6 +582,11 @@ class Financial
* Excel Function:
* DOLLARDE(fractional_dollar,fraction)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Dollar::decimal()
+ * Use the decimal() method in the Financial\Dollar class instead
+ *
* @param float $fractional_dollar Fractional Dollar
* @param int $fraction Fraction
*
@@ -987,23 +594,7 @@ class Financial
*/
public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
{
- $fractional_dollar = Functions::flattenSingleValue($fractional_dollar);
- $fraction = (int) Functions::flattenSingleValue($fraction);
-
- // Validate parameters
- if ($fractional_dollar === null || $fraction < 0) {
- return Functions::NAN();
- }
- if ($fraction == 0) {
- return Functions::DIV0();
- }
-
- $dollars = floor($fractional_dollar);
- $cents = fmod($fractional_dollar, 1);
- $cents /= $fraction;
- $cents *= 10 ** ceil(log10($fraction));
-
- return $dollars + $cents;
+ return Dollar::decimal($fractional_dollar, $fraction);
}
/**
@@ -1016,6 +607,11 @@ class Financial
* Excel Function:
* DOLLARFR(decimal_dollar,fraction)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Dollar::fractional()
+ * Use the fractional() method in the Financial\Dollar class instead
+ *
* @param float $decimal_dollar Decimal Dollar
* @param int $fraction Fraction
*
@@ -1023,23 +619,7 @@ class Financial
*/
public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
{
- $decimal_dollar = Functions::flattenSingleValue($decimal_dollar);
- $fraction = (int) Functions::flattenSingleValue($fraction);
-
- // Validate parameters
- if ($decimal_dollar === null || $fraction < 0) {
- return Functions::NAN();
- }
- if ($fraction == 0) {
- return Functions::DIV0();
- }
-
- $dollars = floor($decimal_dollar);
- $cents = fmod($decimal_dollar, 1);
- $cents *= $fraction;
- $cents *= 10 ** (-ceil(log10($fraction)));
-
- return $dollars + $cents;
+ return Dollar::fractional($decimal_dollar, $fraction);
}
/**
@@ -1051,22 +631,19 @@ class Financial
* Excel Function:
* EFFECT(nominal_rate,npery)
*
- * @param float $nominal_rate Nominal interest rate
- * @param int $npery Number of compounding payments per year
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\InterestRate::effective()
+ * Use the effective() method in the Financial\InterestRate class instead
+ *
+ * @param float $nominalRate Nominal interest rate
+ * @param int $periodsPerYear Number of compounding payments per year
*
* @return float|string
*/
- public static function EFFECT($nominal_rate = 0, $npery = 0)
+ public static function EFFECT($nominalRate = 0, $periodsPerYear = 0)
{
- $nominal_rate = Functions::flattenSingleValue($nominal_rate);
- $npery = (int) Functions::flattenSingleValue($npery);
-
- // Validate parameters
- if ($nominal_rate <= 0 || $npery < 1) {
- return Functions::NAN();
- }
-
- return (1 + $nominal_rate / $npery) ** $npery - 1;
+ return Financial\InterestRate::effective($nominalRate, $periodsPerYear);
}
/**
@@ -1077,6 +654,11 @@ class Financial
* Excel Function:
* FV(rate,nper,pmt[,pv[,type]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic::futureValue()
+ * Use the futureValue() method in the Financial\CashFlow\Constant\Periodic class instead
+ *
* @param float $rate The interest rate per period
* @param int $nper Total number of payment periods in an annuity
* @param float $pmt The payment made each period: it cannot change over the
@@ -1092,23 +674,7 @@ class Financial
*/
public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return -$pv * (1 + $rate) ** $nper - $pmt * (1 + $rate * $type) * ((1 + $rate) ** $nper - 1) / $rate;
- }
-
- return -$pv - $pmt * $nper;
+ return Financial\CashFlow\Constant\Periodic::futureValue($rate, $nper, $pmt, $pv, $type);
}
/**
@@ -1120,21 +686,19 @@ class Financial
* Excel Function:
* FVSCHEDULE(principal,schedule)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Single::futureValue()
+ * Use the futureValue() method in the Financial\CashFlow\Single class instead
+ *
* @param float $principal the present value
* @param float[] $schedule an array of interest rates to apply
*
- * @return float
+ * @return float|string
*/
public static function FVSCHEDULE($principal, $schedule)
{
- $principal = Functions::flattenSingleValue($principal);
- $schedule = Functions::flattenArray($schedule);
-
- foreach ($schedule as $rate) {
- $principal *= 1 + $rate;
- }
-
- return $principal;
+ return Financial\CashFlow\Single::futureValue($principal, $schedule);
}
/**
@@ -1145,57 +709,46 @@ class Financial
* Excel Function:
* INTRATE(settlement,maturity,investment,redemption[,basis])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\Rates::interest()
+ * Use the interest() method in the Financial\Securities\Rates class instead
+ *
* @param mixed $settlement The security's settlement date.
- * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
* @param mixed $maturity The security's maturity date.
- * The maturity date is the date when the security expires.
+ * The maturity date is the date when the security expires.
* @param int $investment the amount invested in the security
* @param int $redemption the amount to be received at maturity
* @param int $basis The type of day count to use.
- * 0 or omitted US (NASD) 30/360
- * 1 Actual/actual
- * 2 Actual/360
- * 3 Actual/365
- * 4 European 30/360
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
* @return float|string
*/
public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $investment = Functions::flattenSingleValue($investment);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- $investment = (float) $investment;
- $redemption = (float) $redemption;
- $basis = (int) $basis;
- if (($investment <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return Financial\Securities\Rates::interest($settlement, $maturity, $investment, $redemption, $basis);
}
/**
* IPMT.
*
- * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments
+ * and a constant interest rate.
*
* Excel Function:
* IPMT(rate,per,nper,pv[,fv][,type])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic\Interest::payment()
+ * Use the payment() method in the Financial\CashFlow\Constant\Periodic class instead
+ *
* @param float $rate Interest rate per period
* @param int $per Period for which we want to find the interest
* @param int $nper Number of periods
@@ -1207,25 +760,7 @@ class Financial
*/
public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $per = (int) Functions::flattenSingleValue($per);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($per <= 0 || $per > $nper) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
-
- return $interestAndPrincipal[0];
+ return Financial\CashFlow\Constant\Periodic\Interest::payment($rate, $per, $nper, $pv, $fv, $type);
}
/**
@@ -1240,63 +775,22 @@ class Financial
* Excel Function:
* IRR(values[,guess])
*
- * @param float[] $values An array or a reference to cells that contain numbers for which you want
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Variable\Periodic::rate()
+ * Use the rate() method in the Financial\CashFlow\Variable\Periodic class instead
+ *
+ * @param mixed $values An array or a reference to cells that contain numbers for which you want
* to calculate the internal rate of return.
* Values must contain at least one positive value and one negative value to
* calculate the internal rate of return.
- * @param float $guess A number that you guess is close to the result of IRR
+ * @param mixed $guess A number that you guess is close to the result of IRR
*
* @return float|string
*/
public static function IRR($values, $guess = 0.1)
{
- if (!is_array($values)) {
- return Functions::VALUE();
- }
- $values = Functions::flattenArray($values);
- $guess = Functions::flattenSingleValue($guess);
-
- // create an initial range, with a root somewhere between 0 and guess
- $x1 = 0.0;
- $x2 = $guess;
- $f1 = self::NPV($x1, $values);
- $f2 = self::NPV($x2, $values);
- for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
- if (($f1 * $f2) < 0.0) {
- break;
- }
- if (abs($f1) < abs($f2)) {
- $f1 = self::NPV($x1 += 1.6 * ($x1 - $x2), $values);
- } else {
- $f2 = self::NPV($x2 += 1.6 * ($x2 - $x1), $values);
- }
- }
- if (($f1 * $f2) > 0.0) {
- return Functions::VALUE();
- }
-
- $f = self::NPV($x1, $values);
- if ($f < 0.0) {
- $rtb = $x1;
- $dx = $x2 - $x1;
- } else {
- $rtb = $x2;
- $dx = $x1 - $x2;
- }
-
- for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
- $dx *= 0.5;
- $x_mid = $rtb + $dx;
- $f_mid = self::NPV($x_mid, $values);
- if ($f_mid <= 0.0) {
- $rtb = $x_mid;
- }
- if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
- return $x_mid;
- }
- }
-
- return Functions::VALUE();
+ return Financial\CashFlow\Variable\Periodic::rate($values, $guess);
}
/**
@@ -1305,7 +799,12 @@ class Financial
* Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
*
* Excel Function:
- * =ISPMT(interest_rate, period, number_payments, PV)
+ * =ISPMT(interest_rate, period, number_payments, pv)
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic\Interest::schedulePayment()
+ * Use the schedulePayment() method in the Financial\CashFlow\Constant\Periodic class instead
*
* interest_rate is the interest rate for the investment
*
@@ -1313,32 +812,11 @@ class Financial
*
* number_payments is the number of payments for the annuity
*
- * PV is the loan amount or present value of the payments
+ * pv is the loan amount or present value of the payments
*/
public static function ISPMT(...$args)
{
- // Return value
- $returnValue = 0;
-
- // Get the parameters
- $aArgs = Functions::flattenArray($args);
- $interestRate = array_shift($aArgs);
- $period = array_shift($aArgs);
- $numberPeriods = array_shift($aArgs);
- $principleRemaining = array_shift($aArgs);
-
- // Calculate
- $principlePayment = ($principleRemaining * 1.0) / ($numberPeriods * 1.0);
- for ($i = 0; $i <= $period; ++$i) {
- $returnValue = $interestRate * $principleRemaining * -1;
- $principleRemaining -= $principlePayment;
- // principle needs to be 0 after the last payment, don't let floating point screw it up
- if ($i == $numberPeriods) {
- $returnValue = 0;
- }
- }
-
- return $returnValue;
+ return Financial\CashFlow\Constant\Periodic\Interest::schedulePayment(...$args);
}
/**
@@ -1350,44 +828,22 @@ class Financial
* Excel Function:
* MIRR(values,finance_rate, reinvestment_rate)
*
- * @param float[] $values An array or a reference to cells that contain a series of payments and
- * income occurring at regular intervals.
- * Payments are negative value, income is positive values.
- * @param float $finance_rate The interest rate you pay on the money used in the cash flows
- * @param float $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Variable\Periodic::modifiedRate()
+ * Use the modifiedRate() method in the Financial\CashFlow\Variable\Periodic class instead
+ *
+ * @param mixed $values An array or a reference to cells that contain a series of payments and
+ * income occurring at regular intervals.
+ * Payments are negative value, income is positive values.
+ * @param mixed $finance_rate The interest rate you pay on the money used in the cash flows
+ * @param mixed $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them
*
* @return float|string Result, or a string containing an error
*/
public static function MIRR($values, $finance_rate, $reinvestment_rate)
{
- if (!is_array($values)) {
- return Functions::VALUE();
- }
- $values = Functions::flattenArray($values);
- $finance_rate = Functions::flattenSingleValue($finance_rate);
- $reinvestment_rate = Functions::flattenSingleValue($reinvestment_rate);
- $n = count($values);
-
- $rr = 1.0 + $reinvestment_rate;
- $fr = 1.0 + $finance_rate;
-
- $npv_pos = $npv_neg = 0.0;
- foreach ($values as $i => $v) {
- if ($v >= 0) {
- $npv_pos += $v / $rr ** $i;
- } else {
- $npv_neg += $v / $fr ** $i;
- }
- }
-
- if (($npv_neg == 0) || ($npv_pos == 0) || ($reinvestment_rate <= -1)) {
- return Functions::VALUE();
- }
-
- $mirr = ((-$npv_pos * $rr ** $n)
- / ($npv_neg * ($rr))) ** (1.0 / ($n - 1)) - 1.0;
-
- return is_finite($mirr) ? $mirr : Functions::VALUE();
+ return Financial\CashFlow\Variable\Periodic::modifiedRate($values, $finance_rate, $reinvestment_rate);
}
/**
@@ -1395,23 +851,22 @@ class Financial
*
* Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
*
- * @param float $effect_rate Effective interest rate
- * @param int $npery Number of compounding payments per year
+ * Excel Function:
+ * NOMINAL(effect_rate, npery)
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\InterestRate::nominal()
+ * Use the nominal() method in the Financial\InterestRate class instead
+ *
+ * @param float $effectiveRate Effective interest rate
+ * @param int $periodsPerYear Number of compounding payments per year
*
* @return float|string Result, or a string containing an error
*/
- public static function NOMINAL($effect_rate = 0, $npery = 0)
+ public static function NOMINAL($effectiveRate = 0, $periodsPerYear = 0)
{
- $effect_rate = Functions::flattenSingleValue($effect_rate);
- $npery = (int) Functions::flattenSingleValue($npery);
-
- // Validate parameters
- if ($effect_rate <= 0 || $npery < 1) {
- return Functions::NAN();
- }
-
- // Calculate
- return $npery * (($effect_rate + 1) ** (1 / $npery) - 1);
+ return InterestRate::nominal($effectiveRate, $periodsPerYear);
}
/**
@@ -1419,6 +874,8 @@ class Financial
*
* Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
*
+ * @Deprecated 1.18.0
+ *
* @param float $rate Interest rate per period
* @param int $pmt Periodic payment (annuity)
* @param float $pv Present Value
@@ -1426,33 +883,13 @@ class Financial
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
*
* @return float|string Result, or a string containing an error
+ *
+ *@see Financial\CashFlow\Constant\Periodic::periods()
+ * Use the periods() method in the Financial\CashFlow\Constant\Periodic class instead
*/
public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- if ($pmt == 0 && $pv == 0) {
- return Functions::NAN();
- }
-
- return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
- }
- if ($pmt == 0) {
- return Functions::NAN();
- }
-
- return (-$pv - $fv) / $pmt;
+ return Financial\CashFlow\Constant\Periodic::periods($rate, $pmt, $pv, $fv, $type);
}
/**
@@ -1460,28 +897,16 @@ class Financial
*
* Returns the Net Present Value of a cash flow series given a discount rate.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Variable\Periodic::presentValue()
+ * Use the presentValue() method in the Financial\CashFlow\Variable\Periodic class instead
+ *
* @return float
*/
public static function NPV(...$args)
{
- // Return value
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $rate = array_shift($aArgs);
- $countArgs = count($aArgs);
- for ($i = 1; $i <= $countArgs; ++$i) {
- // Is it a numeric value?
- if (is_numeric($aArgs[$i - 1])) {
- $returnValue += $aArgs[$i - 1] / (1 + $rate) ** $i;
- }
- }
-
- // Return
- return $returnValue;
+ return Financial\CashFlow\Variable\Periodic::presentValue(...$args);
}
/**
@@ -1489,6 +914,11 @@ class Financial
*
* Calculates the number of periods required for an investment to reach a specified value.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Single::periods()
+ * Use the periods() method in the Financial\CashFlow\Single class instead
+ *
* @param float $rate Interest rate per period
* @param float $pv Present Value
* @param float $fv Future Value
@@ -1497,18 +927,7 @@ class Financial
*/
public static function PDURATION($rate = 0, $pv = 0, $fv = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
-
- // Validate parameters
- if (!is_numeric($rate) || !is_numeric($pv) || !is_numeric($fv)) {
- return Functions::VALUE();
- } elseif ($rate <= 0.0 || $pv <= 0.0 || $fv <= 0.0) {
- return Functions::NAN();
- }
-
- return (log($fv) - log($pv)) / log(1 + $rate);
+ return Financial\CashFlow\Single::periods($rate, $pv, $fv);
}
/**
@@ -1516,6 +935,11 @@ class Financial
*
* Returns the constant payment (annuity) for a cash flow with a constant interest rate.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic\Payments::annuity()
+ * Use the annuity() method in the Financial\CashFlow\Constant\Periodic\Payments class instead
+ *
* @param float $rate Interest rate per period
* @param int $nper Number of periods
* @param float $pv Present Value
@@ -1526,29 +950,19 @@ class Financial
*/
public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return (-$fv - $pv * (1 + $rate) ** $nper) / (1 + $rate * $type) / (((1 + $rate) ** $nper - 1) / $rate);
- }
-
- return (-$pv - $fv) / $nper;
+ return Financial\CashFlow\Constant\Periodic\Payments::annuity($rate, $nper, $pv, $fv, $type);
}
/**
* PPMT.
*
- * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments
+ * and a constant interest rate.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic\Payments::interestPayment()
+ * Use the interestPayment() method in the Financial\CashFlow\Constant\Periodic\Payments class instead
*
* @param float $rate Interest rate per period
* @param int $per Period for which we want to find the interest
@@ -1561,100 +975,43 @@ class Financial
*/
public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $per = (int) Functions::flattenSingleValue($per);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($per <= 0 || $per > $nper) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
-
- return $interestAndPrincipal[1];
- }
-
- private static function validatePrice($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis)
- {
- if (is_string($settlement)) {
- return Functions::VALUE();
- }
- if (is_string($maturity)) {
- return Functions::VALUE();
- }
- if (!is_numeric($rate)) {
- return Functions::VALUE();
- }
- if (!is_numeric($yield)) {
- return Functions::VALUE();
- }
- if (!is_numeric($redemption)) {
- return Functions::VALUE();
- }
- if (!is_numeric($frequency)) {
- return Functions::VALUE();
- }
- if (!is_numeric($basis)) {
- return Functions::VALUE();
- }
-
- return '';
+ return Financial\CashFlow\Constant\Periodic\Payments::interestPayment($rate, $per, $nper, $pv, $fv, $type);
}
+ /**
+ * PRICE.
+ *
+ * Returns the price per $100 face value of a security that pays periodic interest.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\Price::price()
+ * Use the price() method in the Financial\Securities\Price class instead
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param float $rate the security's annual coupon rate
+ * @param float $yield the security's annual yield
+ * @param float $redemption The number of coupon payments per year.
+ * For annual payments, frequency = 1;
+ * for semiannual, frequency = 2;
+ * for quarterly, frequency = 4.
+ * @param int $frequency
+ * @param int $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $rate = Functions::flattenSingleValue($rate);
- $yield = Functions::flattenSingleValue($yield);
- $redemption = Functions::flattenSingleValue($redemption);
- $frequency = Functions::flattenSingleValue($frequency);
- $basis = Functions::flattenSingleValue($basis);
-
- $settlement = DateTime::getDateValue($settlement);
- $maturity = DateTime::getDateValue($maturity);
- $rslt = self::validatePrice($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis);
- if ($rslt) {
- return $rslt;
- }
- $rate = (float) $rate;
- $yield = (float) $yield;
- $redemption = (float) $redemption;
- $frequency = (int) $frequency;
- $basis = (int) $basis;
-
- if (
- ($settlement > $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- $dsc = self::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
- $e = self::COUPDAYS($settlement, $maturity, $frequency, $basis);
- $n = self::COUPNUM($settlement, $maturity, $frequency, $basis);
- $a = self::COUPDAYBS($settlement, $maturity, $frequency, $basis);
-
- $baseYF = 1.0 + ($yield / $frequency);
- $rfp = 100 * ($rate / $frequency);
- $de = $dsc / $e;
-
- $result = $redemption / $baseYF ** (--$n + $de);
- for ($k = 0; $k <= $n; ++$k) {
- $result += $rfp / ($baseYF ** ($k + $de));
- }
- $result -= $rfp * ($a / $e);
-
- return $result;
+ return Securities\Price::price($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis);
}
/**
@@ -1662,10 +1019,16 @@ class Financial
*
* Returns the price per $100 face value of a discounted security.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\Price::priceDiscounted()
+ * Use the priceDiscounted() method in the Financial\Securities\Price class instead
+ *
* @param mixed $settlement The security's settlement date.
- * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
* @param mixed $maturity The security's maturity date.
- * The maturity date is the date when the security expires.
+ * The maturity date is the date when the security expires.
* @param int $discount The security's discount rate
* @param int $redemption The security's redemption value per $100 face value
* @param int $basis The type of day count to use.
@@ -1679,27 +1042,7 @@ class Financial
*/
public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = (float) Functions::flattenSingleValue($discount);
- $redemption = (float) Functions::flattenSingleValue($redemption);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- if (($discount <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return Securities\Price::priceDiscounted($settlement, $maturity, $discount, $redemption, $basis);
}
/**
@@ -1707,10 +1050,16 @@ class Financial
*
* Returns the price per $100 face value of a security that pays interest at maturity.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\Price::priceAtMaturity()
+ * Use the priceAtMaturity() method in the Financial\Securities\Price class instead
+ *
* @param mixed $settlement The security's settlement date.
- * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * The security's settlement date is the date after the issue date when the security
+ * is traded to the buyer.
* @param mixed $maturity The security's maturity date.
- * The maturity date is the date when the security expires.
+ * The maturity date is the date when the security expires.
* @param mixed $issue The security's issue date
* @param int $rate The security's interest rate at date of issue
* @param int $yield The security's annual yield
@@ -1725,47 +1074,7 @@ class Financial
*/
public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $issue = Functions::flattenSingleValue($issue);
- $rate = Functions::flattenSingleValue($rate);
- $yield = Functions::flattenSingleValue($yield);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($rate) && is_numeric($yield)) {
- if (($rate <= 0) || ($yield <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
- $daysBetweenIssueAndSettlement *= $daysPerYear;
- $daysBetweenIssueAndMaturity = DateTime::YEARFRAC($issue, $maturity, $basis);
- if (!is_numeric($daysBetweenIssueAndMaturity)) {
- // return date error
- return $daysBetweenIssueAndMaturity;
- }
- $daysBetweenIssueAndMaturity *= $daysPerYear;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- $daysBetweenSettlementAndMaturity *= $daysPerYear;
-
- return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
- (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
- (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
- }
-
- return Functions::VALUE();
+ return Securities\Price::priceAtMaturity($settlement, $maturity, $issue, $rate, $yield, $basis);
}
/**
@@ -1773,6 +1082,11 @@ class Financial
*
* Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic::presentValue()
+ * Use the presentValue() method in the Financial\CashFlow\Constant\Periodic class instead
+ *
* @param float $rate Interest rate per period
* @param int $nper Number of periods
* @param float $pmt Periodic payment (annuity)
@@ -1783,23 +1097,7 @@ class Financial
*/
public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return (-$pmt * (1 + $rate * $type) * (((1 + $rate) ** $nper - 1) / $rate) - $fv) / (1 + $rate) ** $nper;
- }
-
- return -$fv - $pmt * $nper;
+ return Financial\CashFlow\Constant\Periodic::presentValue($rate, $nper, $pmt, $fv, $type);
}
/**
@@ -1813,112 +1111,63 @@ class Financial
* Excel Function:
* RATE(nper,pmt,pv[,fv[,type[,guess]]])
*
- * @param float $nper The total number of payment periods in an annuity
- * @param float $pmt The payment made each period and cannot change over the life
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Constant\Periodic\Interest::rate()
+ * Use the rate() method in the Financial\CashFlow\Constant\Periodic class instead
+ *
+ * @param mixed $nper The total number of payment periods in an annuity
+ * @param mixed $pmt The payment made each period and cannot change over the life
* of the annuity.
* Typically, pmt includes principal and interest but no other
* fees or taxes.
- * @param float $pv The present value - the total amount that a series of future
+ * @param mixed $pv The present value - the total amount that a series of future
* payments is worth now
- * @param float $fv The future value, or a cash balance you want to attain after
+ * @param mixed $fv The future value, or a cash balance you want to attain after
* the last payment is made. If fv is omitted, it is assumed
* to be 0 (the future value of a loan, for example, is 0).
- * @param int $type A number 0 or 1 and indicates when payments are due:
+ * @param mixed $type A number 0 or 1 and indicates when payments are due:
* 0 or omitted At the end of the period.
* 1 At the beginning of the period.
- * @param float $guess Your guess for what the rate will be.
+ * @param mixed $guess Your guess for what the rate will be.
* If you omit guess, it is assumed to be 10 percent.
*
* @return float|string
*/
public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
{
- $nper = (int) Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $fv = ($fv === null) ? 0.0 : Functions::flattenSingleValue($fv);
- $type = ($type === null) ? 0 : (int) Functions::flattenSingleValue($type);
- $guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
-
- $rate = $guess;
- // rest of code adapted from python/numpy
- $close = false;
- $iter = 0;
- while (!$close && $iter < self::FINANCIAL_MAX_ITERATIONS) {
- $nextdiff = self::rateNextGuess($rate, $nper, $pmt, $pv, $fv, $type);
- if (!is_numeric($nextdiff)) {
- break;
- }
- $rate1 = $rate - $nextdiff;
- $close = abs($rate1 - $rate) < self::FINANCIAL_PRECISION;
- ++$iter;
- $rate = $rate1;
- }
-
- return $close ? $rate : Functions::NAN();
- }
-
- private static function rateNextGuess($rate, $nper, $pmt, $pv, $fv, $type)
- {
- if ($rate == 0) {
- return Functions::NAN();
- }
- $tt1 = ($rate + 1) ** $nper;
- $tt2 = ($rate + 1) ** ($nper - 1);
- $numerator = $fv + $tt1 * $pv + $pmt * ($tt1 - 1) * ($rate * $type + 1) / $rate;
- $denominator = $nper * $tt2 * $pv - $pmt * ($tt1 - 1) * ($rate * $type + 1) / ($rate * $rate)
- + $nper * $pmt * $tt2 * ($rate * $type + 1) / $rate
- + $pmt * ($tt1 - 1) * $type / $rate;
- if ($denominator == 0) {
- return Functions::NAN();
- }
-
- return $numerator / $denominator;
+ return Financial\CashFlow\Constant\Periodic\Interest::rate($nper, $pmt, $pv, $fv, $type, $guess);
}
/**
* RECEIVED.
*
- * Returns the price per $100 face value of a discounted security.
+ * Returns the amount received at maturity for a fully invested Security.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\Price::received()
+ * Use the received() method in the Financial\Securities\Price class instead
*
* @param mixed $settlement The security's settlement date.
- * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
* @param mixed $maturity The security's maturity date.
- * The maturity date is the date when the security expires.
- * @param int $investment The amount invested in the security
- * @param int $discount The security's discount rate
- * @param int $basis The type of day count to use.
- * 0 or omitted US (NASD) 30/360
- * 1 Actual/actual
- * 2 Actual/360
- * 3 Actual/365
- * 4 European 30/360
+ * The maturity date is the date when the security expires.
+ * @param mixed $investment The amount invested in the security
+ * @param mixed $discount The security's discount rate
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $investment = (float) Functions::flattenSingleValue($investment);
- $discount = (float) Functions::flattenSingleValue($discount);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
- if (($investment <= 0) || ($discount <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
- }
-
- return Functions::VALUE();
+ return Financial\Securities\Price::received($settlement, $maturity, $investment, $discount, $basis);
}
/**
@@ -1926,6 +1175,11 @@ class Financial
*
* Calculates the interest rate required for an investment to grow to a specified future value .
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Single::interestRate()
+ * Use the interestRate() method in the Financial\CashFlow\Single class instead
+ *
* @param float $nper The number of periods over which the investment is made
* @param float $pv Present Value
* @param float $fv Future Value
@@ -1934,18 +1188,7 @@ class Financial
*/
public static function RRI($nper = 0, $pv = 0, $fv = 0)
{
- $nper = Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
-
- // Validate parameters
- if (!is_numeric($nper) || !is_numeric($pv) || !is_numeric($fv)) {
- return Functions::VALUE();
- } elseif ($nper <= 0.0 || $pv <= 0.0 || $fv < 0.0) {
- return Functions::NAN();
- }
-
- return ($fv / $pv) ** (1 / $nper) - 1;
+ return Financial\CashFlow\Single::interestRate($nper, $pv, $fv);
}
/**
@@ -1953,6 +1196,11 @@ class Financial
*
* Returns the straight-line depreciation of an asset for one period
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Depreciation::SLN()
+ * Use the SLN() method in the Financial\Depreciation class instead
+ *
* @param mixed $cost Initial cost of the asset
* @param mixed $salvage Value at the end of the depreciation
* @param mixed $life Number of periods over which the asset is depreciated
@@ -1961,20 +1209,7 @@ class Financial
*/
public static function SLN($cost, $salvage, $life)
{
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
-
- // Calculate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
- if ($life < 0) {
- return Functions::NAN();
- }
-
- return ($cost - $salvage) / $life;
- }
-
- return Functions::VALUE();
+ return Depreciation::SLN($cost, $salvage, $life);
}
/**
@@ -1982,6 +1217,11 @@ class Financial
*
* Returns the sum-of-years' digits depreciation of an asset for a specified period.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Depreciation::SYD()
+ * Use the SYD() method in the Financial\Depreciation class instead
+ *
* @param mixed $cost Initial cost of the asset
* @param mixed $salvage Value at the end of the depreciation
* @param mixed $life Number of periods over which the asset is depreciated
@@ -1991,21 +1231,7 @@ class Financial
*/
public static function SYD($cost, $salvage, $life, $period)
{
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
-
- // Calculate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
- if (($life < 1) || ($period > $life)) {
- return Functions::NAN();
- }
-
- return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
- }
-
- return Functions::VALUE();
+ return Depreciation::SYD($cost, $salvage, $life, $period);
}
/**
@@ -2013,8 +1239,14 @@ class Financial
*
* Returns the bond-equivalent yield for a Treasury bill.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\TreasuryBill::bondEquivalentYield()
+ * Use the bondEquivalentYield() method in the Financial\TreasuryBill class instead
+ *
* @param mixed $settlement The Treasury bill's settlement date.
- * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * The Treasury bill's settlement date is the date after the issue date when the
+ * Treasury bill is traded to the buyer.
* @param mixed $maturity The Treasury bill's maturity date.
* The maturity date is the date when the Treasury bill expires.
* @param int $discount The Treasury bill's discount rate
@@ -2023,37 +1255,22 @@ class Financial
*/
public static function TBILLEQ($settlement, $maturity, $discount)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = Functions::flattenSingleValue($discount);
-
- // Use TBILLPRICE for validation
- $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
- if (is_string($testValue)) {
- return $testValue;
- }
-
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
+ return TreasuryBill::bondEquivalentYield($settlement, $maturity, $discount);
}
/**
* TBILLPRICE.
*
- * Returns the yield for a Treasury bill.
+ * Returns the price per $100 face value for a Treasury bill.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\TreasuryBill::price()
+ * Use the price() method in the Financial\TreasuryBill class instead
*
* @param mixed $settlement The Treasury bill's settlement date.
- * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * The Treasury bill's settlement date is the date after the issue date
+ * when the Treasury bill is traded to the buyer.
* @param mixed $maturity The Treasury bill's maturity date.
* The maturity date is the date when the Treasury bill expires.
* @param int $discount The Treasury bill's discount rate
@@ -2062,44 +1279,7 @@ class Financial
*/
public static function TBILLPRICE($settlement, $maturity, $discount)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = Functions::flattenSingleValue($discount);
-
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- // Validate
- if (is_numeric($discount)) {
- if ($discount <= 0) {
- return Functions::NAN();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- if ($daysBetweenSettlementAndMaturity > 360) {
- return Functions::NAN();
- }
-
- $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
- if ($price <= 0) {
- return Functions::NAN();
- }
-
- return $price;
- }
-
- return Functions::VALUE();
+ return TreasuryBill::price($settlement, $maturity, $discount);
}
/**
@@ -2107,8 +1287,14 @@ class Financial
*
* Returns the yield for a Treasury bill.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\TreasuryBill::yield()
+ * Use the yield() method in the Financial\TreasuryBill class instead
+ *
* @param mixed $settlement The Treasury bill's settlement date.
- * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * The Treasury bill's settlement date is the date after the issue date
+ * when the Treasury bill is traded to the buyer.
* @param mixed $maturity The Treasury bill's maturity date.
* The maturity date is the date when the Treasury bill expires.
* @param int $price The Treasury bill's price per $100 face value
@@ -2117,113 +1303,7 @@ class Financial
*/
public static function TBILLYIELD($settlement, $maturity, $price)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
-
- // Validate
- if (is_numeric($price)) {
- if ($price <= 0) {
- return Functions::NAN();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- if ($daysBetweenSettlementAndMaturity > 360) {
- return Functions::NAN();
- }
-
- return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
- }
-
- private static function bothNegAndPos($neg, $pos)
- {
- return $neg && $pos;
- }
-
- private static function xirrPart2(&$values)
- {
- $valCount = count($values);
- $foundpos = false;
- $foundneg = false;
- for ($i = 0; $i < $valCount; ++$i) {
- $fld = $values[$i];
- if (!is_numeric($fld)) {
- return Functions::VALUE();
- } elseif ($fld > 0) {
- $foundpos = true;
- } elseif ($fld < 0) {
- $foundneg = true;
- }
- }
- if (!self::bothNegAndPos($foundneg, $foundpos)) {
- return Functions::NAN();
- }
-
- return '';
- }
-
- private static function xirrPart1(&$values, &$dates)
- {
- if ((!is_array($values)) && (!is_array($dates))) {
- return Functions::NA();
- }
- $values = Functions::flattenArray($values);
- $dates = Functions::flattenArray($dates);
- if (count($values) != count($dates)) {
- return Functions::NAN();
- }
-
- $datesCount = count($dates);
- for ($i = 0; $i < $datesCount; ++$i) {
- $dates[$i] = DateTime::getDateValue($dates[$i]);
- if (!is_numeric($dates[$i])) {
- return Functions::VALUE();
- }
- }
-
- return self::xirrPart2($values);
- }
-
- private static function xirrPart3($values, $dates, $x1, $x2)
- {
- $f = self::xnpvOrdered($x1, $values, $dates, false);
- if ($f < 0.0) {
- $rtb = $x1;
- $dx = $x2 - $x1;
- } else {
- $rtb = $x2;
- $dx = $x1 - $x2;
- }
-
- $rslt = Functions::VALUE();
- for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
- $dx *= 0.5;
- $x_mid = $rtb + $dx;
- $f_mid = self::xnpvOrdered($x_mid, $values, $dates, false);
- if ($f_mid <= 0.0) {
- $rtb = $x_mid;
- }
- if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
- $rslt = $x_mid;
-
- break;
- }
- }
-
- return $rslt;
+ return TreasuryBill::yield($settlement, $maturity, $price);
}
/**
@@ -2234,6 +1314,11 @@ class Financial
* Excel Function:
* =XIRR(values,dates,guess)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Variable\NonPeriodic::rate()
+ * Use the rate() method in the Financial\CashFlow\Variable\NonPeriodic class instead
+ *
* @param float[] $values A series of cash flow payments
* The series of values must contain at least one positive value & one negative value
* @param mixed[] $dates A series of payment dates
@@ -2245,37 +1330,7 @@ class Financial
*/
public static function XIRR($values, $dates, $guess = 0.1)
{
- $rslt = self::xirrPart1($values, $dates);
- if ($rslt) {
- return $rslt;
- }
-
- // create an initial range, with a root somewhere between 0 and guess
- $guess = Functions::flattenSingleValue($guess);
- $x1 = 0.0;
- $x2 = $guess ? $guess : 0.1;
- $f1 = self::xnpvOrdered($x1, $values, $dates, false);
- $f2 = self::xnpvOrdered($x2, $values, $dates, false);
- $found = false;
- for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
- if (!is_numeric($f1) || !is_numeric($f2)) {
- break;
- }
- if (($f1 * $f2) < 0.0) {
- $found = true;
-
- break;
- } elseif (abs($f1) < abs($f2)) {
- $f1 = self::xnpvOrdered($x1 += 1.6 * ($x1 - $x2), $values, $dates, false);
- } else {
- $f2 = self::xnpvOrdered($x2 += 1.6 * ($x2 - $x1), $values, $dates, false);
- }
- }
- if (!$found) {
- return Functions::NAN();
- }
-
- return self::xirrPart3($values, $dates, $x1, $x2);
+ return Financial\CashFlow\Variable\NonPeriodic::rate($values, $dates, $guess);
}
/**
@@ -2287,74 +1342,27 @@ class Financial
* Excel Function:
* =XNPV(rate,values,dates)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\CashFlow\Variable\NonPeriodic::presentValue()
+ * Use the presentValue() method in the Financial\CashFlow\Variable\NonPeriodic class instead
+ *
* @param float $rate the discount rate to apply to the cash flows
- * @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
- * The first payment is optional and corresponds to a cost or payment that occurs at the beginning of the investment.
- * If the first value is a cost or payment, it must be a negative value. All succeeding payments are discounted based on a 365-day year.
- * The series of values must contain at least one positive value and one negative value.
- * @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
- * The first payment date indicates the beginning of the schedule of payments.
- * All other dates must be later than this date, but they may occur in any order.
+ * @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
+ * The first payment is optional and corresponds to a cost or payment that occurs
+ * at the beginning of the investment.
+ * If the first value is a cost or payment, it must be a negative value.
+ * All succeeding payments are discounted based on a 365-day year.
+ * The series of values must contain at least one positive value and one negative value.
+ * @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
+ * The first payment date indicates the beginning of the schedule of payments.
+ * All other dates must be later than this date, but they may occur in any order.
*
* @return float|mixed|string
*/
public static function XNPV($rate, $values, $dates)
{
- return self::xnpvOrdered($rate, $values, $dates, true);
- }
-
- private static function validateXnpv($rate, $values, $dates)
- {
- if (!is_numeric($rate)) {
- return Functions::VALUE();
- }
- $valCount = count($values);
- if ($valCount != count($dates)) {
- return Functions::NAN();
- }
- if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) {
- return Functions::NAN();
- }
- $date0 = DateTime::getDateValue($dates[0]);
- if (is_string($date0)) {
- return Functions::VALUE();
- }
-
- return '';
- }
-
- private static function xnpvOrdered($rate, $values, $dates, $ordered = true)
- {
- $rate = Functions::flattenSingleValue($rate);
- $values = Functions::flattenArray($values);
- $dates = Functions::flattenArray($dates);
- $valCount = count($values);
- $date0 = DateTime::getDateValue($dates[0]);
- $rslt = self::validateXnpv($rate, $values, $dates);
- if ($rslt) {
- return $rslt;
- }
- $xnpv = 0.0;
- for ($i = 0; $i < $valCount; ++$i) {
- if (!is_numeric($values[$i])) {
- return Functions::VALUE();
- }
- $datei = DateTime::getDateValue($dates[$i]);
- if (is_string($datei)) {
- return Functions::VALUE();
- }
- if ($date0 > $datei) {
- $dif = $ordered ? Functions::NAN() : -DateTime::DATEDIF($datei, $date0, 'd');
- } else {
- $dif = DateTime::DATEDIF($date0, $datei, 'd');
- }
- if (!is_numeric($dif)) {
- return $dif;
- }
- $xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
- }
-
- return is_finite($xnpv) ? $xnpv : Functions::VALUE();
+ return Financial\CashFlow\Variable\NonPeriodic::presentValue($rate, $values, $dates);
}
/**
@@ -2362,10 +1370,16 @@ class Financial
*
* Returns the annual yield of a security that pays interest at maturity.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\Yields::yieldDiscounted()
+ * Use the yieldDiscounted() method in the Financial\Securities\Yields class instead
+ *
* @param mixed $settlement The security's settlement date.
- * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * The security's settlement date is the date after the issue date when the security
+ * is traded to the buyer.
* @param mixed $maturity The security's maturity date.
- * The maturity date is the date when the security expires.
+ * The maturity date is the date when the security expires.
* @param int $price The security's price per $100 face value
* @param int $redemption The security's redemption value per $100 face value
* @param int $basis The type of day count to use.
@@ -2379,32 +1393,7 @@ class Financial
*/
public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($price) && is_numeric($redemption)) {
- if (($price <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- $daysBetweenSettlementAndMaturity *= $daysPerYear;
-
- return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return Securities\Yields::yieldDiscounted($settlement, $maturity, $price, $redemption, $basis);
}
/**
@@ -2412,64 +1401,30 @@ class Financial
*
* Returns the annual yield of a security that pays interest at maturity.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Financial\Securities\Yields::yieldAtMaturity()
+ * Use the yieldAtMaturity() method in the Financial\Securities\Yields class instead
+ *
* @param mixed $settlement The security's settlement date.
- * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * The security's settlement date is the date after the issue date when the security
+ * is traded to the buyer.
* @param mixed $maturity The security's maturity date.
- * The maturity date is the date when the security expires.
+ * The maturity date is the date when the security expires.
* @param mixed $issue The security's issue date
* @param int $rate The security's interest rate at date of issue
* @param int $price The security's price per $100 face value
* @param int $basis The type of day count to use.
- * 0 or omitted US (NASD) 30/360
- * 1 Actual/actual
- * 2 Actual/360
- * 3 Actual/365
- * 4 European 30/360
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $issue = Functions::flattenSingleValue($issue);
- $rate = Functions::flattenSingleValue($rate);
- $price = Functions::flattenSingleValue($price);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($rate) && is_numeric($price)) {
- if (($rate <= 0) || ($price <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
- $daysBetweenIssueAndSettlement *= $daysPerYear;
- $daysBetweenIssueAndMaturity = DateTime::YEARFRAC($issue, $maturity, $basis);
- if (!is_numeric($daysBetweenIssueAndMaturity)) {
- // return date error
- return $daysBetweenIssueAndMaturity;
- }
- $daysBetweenIssueAndMaturity *= $daysPerYear;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- $daysBetweenSettlementAndMaturity *= $daysPerYear;
-
- return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
- (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
- ($daysPerYear / $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return Securities\Yields::yieldAtMaturity($settlement, $maturity, $issue, $rate, $price, $basis);
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
new file mode 100644
index 00000000000..ba7fb5210d7
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
@@ -0,0 +1,210 @@
+getMessage();
+ }
+
+ $yearFrac = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
+ if (is_string($yearFrac)) {
+ return $yearFrac;
+ }
+
+ $amortiseCoeff = self::getAmortizationCoefficient($rate);
+
+ $rate *= $amortiseCoeff;
+ $fNRate = round($yearFrac * $rate * $cost, 0);
+ $cost -= $fNRate;
+ $fRest = $cost - $salvage;
+
+ for ($n = 0; $n < $period; ++$n) {
+ $fNRate = round($rate * $cost, 0);
+ $fRest -= $fNRate;
+
+ if ($fRest < 0.0) {
+ switch ($period - $n) {
+ case 0:
+ case 1:
+ return round($cost * 0.5, 0);
+ default:
+ return 0.0;
+ }
+ }
+ $cost -= $fNRate;
+ }
+
+ return $fNRate;
+ }
+
+ /**
+ * AMORLINC.
+ *
+ * Returns the depreciation for each accounting period.
+ * This function is provided for the French accounting system. If an asset is purchased in
+ * the middle of the accounting period, the prorated depreciation is taken into account.
+ *
+ * Excel Function:
+ * AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
+ *
+ * @param mixed $cost The cost of the asset as a float
+ * @param mixed $purchased Date of the purchase of the asset
+ * @param mixed $firstPeriod Date of the end of the first period
+ * @param mixed $salvage The salvage value at the end of the life of the asset
+ * @param mixed $period The period as a float
+ * @param mixed $rate Rate of depreciation as float
+ * @param mixed $basis Integer indicating the type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string (string containing the error type if there is an error)
+ */
+ public static function AMORLINC(
+ $cost,
+ $purchased,
+ $firstPeriod,
+ $salvage,
+ $period,
+ $rate,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $cost = Functions::flattenSingleValue($cost);
+ $purchased = Functions::flattenSingleValue($purchased);
+ $firstPeriod = Functions::flattenSingleValue($firstPeriod);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $period = Functions::flattenSingleValue($period);
+ $rate = Functions::flattenSingleValue($rate);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $cost = FinancialValidations::validateFloat($cost);
+ $purchased = FinancialValidations::validateDate($purchased);
+ $firstPeriod = FinancialValidations::validateDate($firstPeriod);
+ $salvage = FinancialValidations::validateFloat($salvage);
+ $period = FinancialValidations::validateFloat($period);
+ $rate = FinancialValidations::validateFloat($rate);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $fOneRate = $cost * $rate;
+ $fCostDelta = $cost - $salvage;
+ // Note, quirky variation for leap years on the YEARFRAC for this function
+ $purchasedYear = DateTimeExcel\DateParts::year($purchased);
+ $yearFrac = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
+ if (is_string($yearFrac)) {
+ return $yearFrac;
+ }
+
+ if (
+ ($basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) &&
+ ($yearFrac < 1) && (DateTimeExcel\Helpers::isLeapYear($purchasedYear))
+ ) {
+ $yearFrac *= 365 / 366;
+ }
+
+ $f0Rate = $yearFrac * $rate * $cost;
+ $nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
+
+ if ($period == 0) {
+ return $f0Rate;
+ } elseif ($period <= $nNumOfFullPeriods) {
+ return $fOneRate;
+ } elseif ($period == ($nNumOfFullPeriods + 1)) {
+ return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
+ }
+
+ return 0.0;
+ }
+
+ private static function getAmortizationCoefficient(float $rate): float
+ {
+ // The depreciation coefficients are:
+ // Life of assets (1/rate) Depreciation coefficient
+ // Less than 3 years 1
+ // Between 3 and 4 years 1.5
+ // Between 5 and 6 years 2
+ // More than 6 years 2.5
+ $fUsePer = 1.0 / $rate;
+
+ if ($fUsePer < 3.0) {
+ return 1.0;
+ } elseif ($fUsePer < 4.0) {
+ return 1.5;
+ } elseif ($fUsePer <= 6.0) {
+ return 2.0;
+ }
+
+ return 2.5;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
new file mode 100644
index 00000000000..e4c8a3a4ceb
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
@@ -0,0 +1,53 @@
+getMessage();
+ }
+
+ return self::calculateFutureValue($rate, $numberOfPeriods, $payment, $presentValue, $type);
+ }
+
+ /**
+ * PV.
+ *
+ * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $numberOfPeriods Number of periods as an integer
+ * @param mixed $payment Periodic payment (annuity)
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function presentValue(
+ $rate,
+ $numberOfPeriods,
+ $payment = 0.0,
+ $futureValue = 0.0,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $rate = Functions::flattenSingleValue($rate);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($numberOfPeriods < 0) {
+ return Functions::NAN();
+ }
+
+ return self::calculatePresentValue($rate, $numberOfPeriods, $payment, $futureValue, $type);
+ }
+
+ /**
+ * NPER.
+ *
+ * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $payment Periodic payment (annuity)
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function periods(
+ $rate,
+ $payment,
+ $presentValue,
+ $futureValue = 0.0,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $rate = Functions::flattenSingleValue($rate);
+ $payment = Functions::flattenSingleValue($payment);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($payment == 0.0) {
+ return Functions::NAN();
+ }
+
+ return self::calculatePeriods($rate, $payment, $presentValue, $futureValue, $type);
+ }
+
+ private static function calculateFutureValue(
+ float $rate,
+ int $numberOfPeriods,
+ float $payment,
+ float $presentValue,
+ int $type
+ ): float {
+ if ($rate !== null && $rate != 0) {
+ return -$presentValue *
+ (1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
+ / $rate;
+ }
+
+ return -$presentValue - $payment * $numberOfPeriods;
+ }
+
+ private static function calculatePresentValue(
+ float $rate,
+ int $numberOfPeriods,
+ float $payment,
+ float $futureValue,
+ int $type
+ ): float {
+ if ($rate != 0.0) {
+ return (-$payment * (1 + $rate * $type)
+ * (((1 + $rate) ** $numberOfPeriods - 1) / $rate) - $futureValue) / (1 + $rate) ** $numberOfPeriods;
+ }
+
+ return -$futureValue - $payment * $numberOfPeriods;
+ }
+
+ /**
+ * @return float|string
+ */
+ private static function calculatePeriods(
+ float $rate,
+ float $payment,
+ float $presentValue,
+ float $futureValue,
+ int $type
+ ) {
+ if ($rate != 0.0) {
+ if ($presentValue == 0.0) {
+ return Functions::NAN();
+ }
+
+ return log(($payment * (1 + $rate * $type) / $rate - $futureValue) /
+ ($presentValue + $payment * (1 + $rate * $type) / $rate)) / log(1 + $rate);
+ }
+
+ return (-$presentValue - $futureValue) / $payment;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
new file mode 100644
index 00000000000..b7f6011c315
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
@@ -0,0 +1,141 @@
+getMessage();
+ }
+
+ // Validate parameters
+ if ($start < 1 || $start > $end) {
+ return Functions::NAN();
+ }
+
+ // Calculate
+ $interest = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $ipmt = Interest::payment($rate, $per, $periods, $presentValue, 0, $type);
+ if (is_string($ipmt)) {
+ return $ipmt;
+ }
+
+ $interest += $ipmt;
+ }
+
+ return $interest;
+ }
+
+ /**
+ * CUMPRINC.
+ *
+ * Returns the cumulative principal paid on a loan between the start and end periods.
+ *
+ * Excel Function:
+ * CUMPRINC(rate,nper,pv,start,end[,type])
+ *
+ * @param mixed $rate The Interest rate
+ * @param mixed $periods The total number of payment periods as an integer
+ * @param mixed $presentValue Present Value
+ * @param mixed $start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param mixed $end the last period in the calculation
+ * @param mixed $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ *
+ * @return float|string
+ */
+ public static function principal(
+ $rate,
+ $periods,
+ $presentValue,
+ $start,
+ $end,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $rate = Functions::flattenSingleValue($rate);
+ $periods = Functions::flattenSingleValue($periods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $start = Functions::flattenSingleValue($start);
+ $end = Functions::flattenSingleValue($end);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $periods = CashFlowValidations::validateInt($periods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $start = CashFlowValidations::validateInt($start);
+ $end = CashFlowValidations::validateInt($end);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($start < 1 || $start > $end) {
+ return Functions::VALUE();
+ }
+
+ // Calculate
+ $principal = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $ppmt = Payments::interestPayment($rate, $per, $periods, $presentValue, 0, $type);
+ if (is_string($ppmt)) {
+ return $ppmt;
+ }
+
+ $principal += $ppmt;
+ }
+
+ return $principal;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
new file mode 100644
index 00000000000..56d2e379298
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
@@ -0,0 +1,216 @@
+getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return Functions::NAN();
+ }
+
+ // Calculate
+ $interestAndPrincipal = new InterestAndPrincipal(
+ $interestRate,
+ $period,
+ $numberOfPeriods,
+ $presentValue,
+ $futureValue,
+ $type
+ );
+
+ return $interestAndPrincipal->interest();
+ }
+
+ /**
+ * ISPMT.
+ *
+ * Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
+ *
+ * Excel Function:
+ * =ISPMT(interest_rate, period, number_payments, pv)
+ *
+ * @param mixed $interestRate is the interest rate for the investment
+ * @param mixed $period is the period to calculate the interest rate. It must be betweeen 1 and number_payments.
+ * @param mixed $numberOfPeriods is the number of payments for the annuity
+ * @param mixed $principleRemaining is the loan amount or present value of the payments
+ */
+ public static function schedulePayment($interestRate, $period, $numberOfPeriods, $principleRemaining)
+ {
+ $interestRate = Functions::flattenSingleValue($interestRate);
+ $period = Functions::flattenSingleValue($period);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $principleRemaining = Functions::flattenSingleValue($principleRemaining);
+
+ try {
+ $interestRate = CashFlowValidations::validateRate($interestRate);
+ $period = CashFlowValidations::validateInt($period);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $principleRemaining = CashFlowValidations::validateFloat($principleRemaining);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return Functions::NAN();
+ }
+
+ // Return value
+ $returnValue = 0;
+
+ // Calculate
+ $principlePayment = ($principleRemaining * 1.0) / ($numberOfPeriods * 1.0);
+ for ($i = 0; $i <= $period; ++$i) {
+ $returnValue = $interestRate * $principleRemaining * -1;
+ $principleRemaining -= $principlePayment;
+ // principle needs to be 0 after the last payment, don't let floating point screw it up
+ if ($i == $numberOfPeriods) {
+ $returnValue = 0.0;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * RATE.
+ *
+ * Returns the interest rate per period of an annuity.
+ * RATE is calculated by iteration and can have zero or more solutions.
+ * If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
+ * RATE returns the #NUM! error value.
+ *
+ * Excel Function:
+ * RATE(nper,pmt,pv[,fv[,type[,guess]]])
+ *
+ * @param mixed $numberOfPeriods The total number of payment periods in an annuity
+ * @param mixed $payment The payment made each period and cannot change over the life of the annuity.
+ * Typically, pmt includes principal and interest but no other fees or taxes.
+ * @param mixed $presentValue The present value - the total amount that a series of future payments is worth now
+ * @param mixed $futureValue The future value, or a cash balance you want to attain after the last payment is made.
+ * If fv is omitted, it is assumed to be 0 (the future value of a loan,
+ * for example, is 0).
+ * @param mixed $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ * @param mixed $guess Your guess for what the rate will be.
+ * If you omit guess, it is assumed to be 10 percent.
+ *
+ * @return float|string
+ */
+ public static function rate(
+ $numberOfPeriods,
+ $payment,
+ $presentValue,
+ $futureValue = 0.0,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD,
+ $guess = 0.1
+ ) {
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $payment = Functions::flattenSingleValue($payment);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+ $guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
+
+ try {
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ $guess = CashFlowValidations::validateFloat($guess);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $rate = $guess;
+ // rest of code adapted from python/numpy
+ $close = false;
+ $iter = 0;
+ while (!$close && $iter < self::FINANCIAL_MAX_ITERATIONS) {
+ $nextdiff = self::rateNextGuess($rate, $numberOfPeriods, $payment, $presentValue, $futureValue, $type);
+ if (!is_numeric($nextdiff)) {
+ break;
+ }
+ $rate1 = $rate - $nextdiff;
+ $close = abs($rate1 - $rate) < self::FINANCIAL_PRECISION;
+ ++$iter;
+ $rate = $rate1;
+ }
+
+ return $close ? $rate : Functions::NAN();
+ }
+
+ private static function rateNextGuess($rate, $numberOfPeriods, $payment, $presentValue, $futureValue, $type)
+ {
+ if ($rate == 0.0) {
+ return Functions::NAN();
+ }
+ $tt1 = ($rate + 1) ** $numberOfPeriods;
+ $tt2 = ($rate + 1) ** ($numberOfPeriods - 1);
+ $numerator = $futureValue + $tt1 * $presentValue + $payment * ($tt1 - 1) * ($rate * $type + 1) / $rate;
+ $denominator = $numberOfPeriods * $tt2 * $presentValue - $payment * ($tt1 - 1)
+ * ($rate * $type + 1) / ($rate * $rate) + $numberOfPeriods
+ * $payment * $tt2 * ($rate * $type + 1) / $rate + $payment * ($tt1 - 1) * $type / $rate;
+ if ($denominator == 0) {
+ return Functions::NAN();
+ }
+
+ return $numerator / $denominator;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
new file mode 100644
index 00000000000..ca989e0058a
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
@@ -0,0 +1,44 @@
+interest = $interest;
+ $this->principal = $principal;
+ }
+
+ public function interest(): float
+ {
+ return $this->interest;
+ }
+
+ public function principal(): float
+ {
+ return $this->principal;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
new file mode 100644
index 00000000000..e103f923666
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
@@ -0,0 +1,115 @@
+getMessage();
+ }
+
+ // Calculate
+ if ($interestRate != 0.0) {
+ return (-$futureValue - $presentValue * (1 + $interestRate) ** $numberOfPeriods) /
+ (1 + $interestRate * $type) / (((1 + $interestRate) ** $numberOfPeriods - 1) / $interestRate);
+ }
+
+ return (-$presentValue - $futureValue) / $numberOfPeriods;
+ }
+
+ /**
+ * PPMT.
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments
+ * and a constant interest rate.
+ *
+ * @param mixed $interestRate Interest rate per period
+ * @param mixed $period Period for which we want to find the interest
+ * @param mixed $numberOfPeriods Number of periods
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function interestPayment(
+ $interestRate,
+ $period,
+ $numberOfPeriods,
+ $presentValue,
+ $futureValue = 0,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $interestRate = Functions::flattenSingleValue($interestRate);
+ $period = Functions::flattenSingleValue($period);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $interestRate = CashFlowValidations::validateRate($interestRate);
+ $period = CashFlowValidations::validateInt($period);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return Functions::NAN();
+ }
+
+ // Calculate
+ $interestAndPrincipal = new InterestAndPrincipal(
+ $interestRate,
+ $period,
+ $numberOfPeriods,
+ $presentValue,
+ $futureValue,
+ $type
+ );
+
+ return $interestAndPrincipal->principal();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
new file mode 100644
index 00000000000..a30634da3be
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
@@ -0,0 +1,108 @@
+getMessage();
+ }
+
+ return $principal;
+ }
+
+ /**
+ * PDURATION.
+ *
+ * Calculates the number of periods required for an investment to reach a specified value.
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function periods($rate, $presentValue, $futureValue)
+ {
+ $rate = Functions::flattenSingleValue($rate);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = Functions::flattenSingleValue($futureValue);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($rate <= 0.0 || $presentValue <= 0.0 || $futureValue <= 0.0) {
+ return Functions::NAN();
+ }
+
+ return (log($futureValue) - log($presentValue)) / log(1 + $rate);
+ }
+
+ /**
+ * RRI.
+ *
+ * Calculates the interest rate required for an investment to grow to a specified future value .
+ *
+ * @param float $periods The number of periods over which the investment is made
+ * @param float $presentValue Present Value
+ * @param float $futureValue Future Value
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function interestRate($periods = 0.0, $presentValue = 0.0, $futureValue = 0.0)
+ {
+ $periods = Functions::flattenSingleValue($periods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = Functions::flattenSingleValue($futureValue);
+
+ try {
+ $periods = CashFlowValidations::validateFloat($periods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($periods <= 0.0 || $presentValue <= 0.0 || $futureValue < 0.0) {
+ return Functions::NAN();
+ }
+
+ return ($futureValue / $presentValue) ** (1 / $periods) - 1;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
new file mode 100644
index 00000000000..8986146c11c
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
@@ -0,0 +1,244 @@
+getMessage();
+ }
+ }
+
+ return self::xirrPart2($values);
+ }
+
+ private static function xirrPart2(array &$values): string
+ {
+ $valCount = count($values);
+ $foundpos = false;
+ $foundneg = false;
+ for ($i = 0; $i < $valCount; ++$i) {
+ $fld = $values[$i];
+ if (!is_numeric($fld)) {
+ return Functions::VALUE();
+ } elseif ($fld > 0) {
+ $foundpos = true;
+ } elseif ($fld < 0) {
+ $foundneg = true;
+ }
+ }
+ if (!self::bothNegAndPos($foundneg, $foundpos)) {
+ return Functions::NAN();
+ }
+
+ return '';
+ }
+
+ /**
+ * @return float|string
+ */
+ private static function xirrPart3(array $values, array $dates, float $x1, float $x2)
+ {
+ $f = self::xnpvOrdered($x1, $values, $dates, false);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ $rslt = Functions::VALUE();
+ for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = (float) self::xnpvOrdered($x_mid, $values, $dates, false);
+ if ($f_mid <= 0.0) {
+ $rtb = $x_mid;
+ }
+ if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
+ $rslt = $x_mid;
+
+ break;
+ }
+ }
+
+ return $rslt;
+ }
+
+ /**
+ * @param mixed $rate
+ * @param mixed $values
+ * @param mixed $dates
+ *
+ * @return float|string
+ */
+ private static function xnpvOrdered($rate, $values, $dates, bool $ordered = true)
+ {
+ $rate = Functions::flattenSingleValue($rate);
+ $values = Functions::flattenArray($values);
+ $dates = Functions::flattenArray($dates);
+ $valCount = count($values);
+
+ try {
+ self::validateXnpv($rate, $values, $dates);
+ $date0 = DateTimeExcel\Helpers::getDateValue($dates[0]);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $xnpv = 0.0;
+ for ($i = 0; $i < $valCount; ++$i) {
+ if (!is_numeric($values[$i])) {
+ return Functions::VALUE();
+ }
+
+ try {
+ $datei = DateTimeExcel\Helpers::getDateValue($dates[$i]);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ if ($date0 > $datei) {
+ $dif = $ordered ? Functions::NAN() : -((int) DateTimeExcel\Difference::interval($datei, $date0, 'd'));
+ } else {
+ $dif = DateTimeExcel\Difference::interval($date0, $datei, 'd');
+ }
+ if (!is_numeric($dif)) {
+ return $dif;
+ }
+ $xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
+ }
+
+ return is_finite($xnpv) ? $xnpv : Functions::VALUE();
+ }
+
+ /**
+ * @param mixed $rate
+ */
+ private static function validateXnpv($rate, array $values, array $dates): void
+ {
+ if (!is_numeric($rate)) {
+ throw new Exception(Functions::VALUE());
+ }
+ $valCount = count($values);
+ if ($valCount != count($dates)) {
+ throw new Exception(Functions::NAN());
+ }
+ if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) {
+ throw new Exception(Functions::NAN());
+ }
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
new file mode 100644
index 00000000000..c42df0c39eb
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
@@ -0,0 +1,160 @@
+ 0.0) {
+ return Functions::VALUE();
+ }
+
+ $f = self::presentValue($x1, $values);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::presentValue($x_mid, $values);
+ if ($f_mid <= 0.0) {
+ $rtb = $x_mid;
+ }
+ if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
+ return $x_mid;
+ }
+ }
+
+ return Functions::VALUE();
+ }
+
+ /**
+ * MIRR.
+ *
+ * Returns the modified internal rate of return for a series of periodic cash flows. MIRR considers both
+ * the cost of the investment and the interest received on reinvestment of cash.
+ *
+ * Excel Function:
+ * MIRR(values,finance_rate, reinvestment_rate)
+ *
+ * @param mixed $values An array or a reference to cells that contain a series of payments and
+ * income occurring at regular intervals.
+ * Payments are negative value, income is positive values.
+ * @param mixed $financeRate The interest rate you pay on the money used in the cash flows
+ * @param mixed $reinvestmentRate The interest rate you receive on the cash flows as you reinvest them
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function modifiedRate($values, $financeRate, $reinvestmentRate)
+ {
+ if (!is_array($values)) {
+ return Functions::VALUE();
+ }
+ $values = Functions::flattenArray($values);
+ $financeRate = Functions::flattenSingleValue($financeRate);
+ $reinvestmentRate = Functions::flattenSingleValue($reinvestmentRate);
+ $n = count($values);
+
+ $rr = 1.0 + $reinvestmentRate;
+ $fr = 1.0 + $financeRate;
+
+ $npvPos = $npvNeg = 0.0;
+ foreach ($values as $i => $v) {
+ if ($v >= 0) {
+ $npvPos += $v / $rr ** $i;
+ } else {
+ $npvNeg += $v / $fr ** $i;
+ }
+ }
+
+ if (($npvNeg === 0.0) || ($npvPos === 0.0) || ($reinvestmentRate <= -1.0)) {
+ return Functions::VALUE();
+ }
+
+ $mirr = ((-$npvPos * $rr ** $n)
+ / ($npvNeg * ($rr))) ** (1.0 / ($n - 1)) - 1.0;
+
+ return is_finite($mirr) ? $mirr : Functions::VALUE();
+ }
+
+ /**
+ * NPV.
+ *
+ * Returns the Net Present Value of a cash flow series given a discount rate.
+ *
+ * @param mixed $rate
+ *
+ * @return float
+ */
+ public static function presentValue($rate, ...$args)
+ {
+ $returnValue = 0;
+
+ $rate = Functions::flattenSingleValue($rate);
+ $aArgs = Functions::flattenArray($args);
+
+ // Calculate
+ $countArgs = count($aArgs);
+ for ($i = 1; $i <= $countArgs; ++$i) {
+ // Is it a numeric value?
+ if (is_numeric($aArgs[$i - 1])) {
+ $returnValue += $aArgs[$i - 1] / (1 + $rate) ** $i;
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php
new file mode 100644
index 00000000000..17740b0acd7
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php
@@ -0,0 +1,19 @@
+getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+ if (is_string($daysPerYear)) {
+ return Functions::VALUE();
+ }
+ $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+
+ if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) {
+ return abs((float) DateTimeExcel\Days::between($prev, $settlement));
+ }
+
+ return (float) DateTimeExcel\YearFrac::fraction($prev, $settlement, $basis) * $daysPerYear;
+ }
+
+ /**
+ * COUPDAYS.
+ *
+ * Returns the number of days in the coupon period that contains the settlement date.
+ *
+ * Excel Function:
+ * COUPDAYS(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string
+ */
+ public static function COUPDAYS(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ switch ($basis) {
+ case FinancialConstants::BASIS_DAYS_PER_YEAR_365:
+ // Actual/365
+ return 365 / $frequency;
+ case FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL:
+ // Actual/actual
+ if ($frequency == FinancialConstants::FREQUENCY_ANNUAL) {
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+
+ return $daysPerYear / $frequency;
+ }
+ $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+ $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+
+ return $next - $prev;
+ default:
+ // US (NASD) 30/360, Actual/360 or European 30/360
+ return 360 / $frequency;
+ }
+ }
+
+ /**
+ * COUPDAYSNC.
+ *
+ * Returns the number of days from the settlement date to the next coupon date.
+ *
+ * Excel Function:
+ * COUPDAYSNC(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int) .
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string
+ */
+ public static function COUPDAYSNC(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+ $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+
+ if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_NASD) {
+ $settlementDate = Date::excelToDateTimeObject($settlement);
+ $settlementEoM = Helpers::isLastDayOfMonth($settlementDate);
+ if ($settlementEoM) {
+ ++$settlement;
+ }
+ }
+
+ return (float) DateTimeExcel\YearFrac::fraction($settlement, $next, $basis) * $daysPerYear;
+ }
+
+ /**
+ * COUPNCD.
+ *
+ * Returns the next coupon date after the settlement date.
+ *
+ * Excel Function:
+ * COUPNCD(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function COUPNCD(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+ }
+
+ /**
+ * COUPNUM.
+ *
+ * Returns the number of coupons payable between the settlement date and maturity date,
+ * rounded up to the nearest whole coupon.
+ *
+ * Excel Function:
+ * COUPNUM(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return int|string
+ */
+ public static function COUPNUM(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $yearsBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction(
+ $settlement,
+ $maturity,
+ FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ );
+
+ return (int) ceil((float) $yearsBetweenSettlementAndMaturity * $frequency);
+ }
+
+ /**
+ * COUPPCD.
+ *
+ * Returns the previous coupon date before the settlement date.
+ *
+ * Excel Function:
+ * COUPPCD(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function COUPPCD(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+ }
+
+ private static function monthsDiff(DateTime $result, int $months, string $plusOrMinus, int $day, bool $lastDayFlag): void
+ {
+ $result->setDate((int) $result->format('Y'), (int) $result->format('m'), 1);
+ $result->modify("$plusOrMinus $months months");
+ $daysInMonth = (int) $result->format('t');
+ $result->setDate((int) $result->format('Y'), (int) $result->format('m'), $lastDayFlag ? $daysInMonth : min($day, $daysInMonth));
+ }
+
+ private static function couponFirstPeriodDate(float $settlement, float $maturity, int $frequency, bool $next): float
+ {
+ $months = 12 / $frequency;
+
+ $result = Date::excelToDateTimeObject($maturity);
+ $day = (int) $result->format('d');
+ $lastDayFlag = Helpers::isLastDayOfMonth($result);
+
+ while ($settlement < Date::PHPToExcel($result)) {
+ self::monthsDiff($result, $months, '-', $day, $lastDayFlag);
+ }
+ if ($next === true) {
+ self::monthsDiff($result, $months, '+', $day, $lastDayFlag);
+ }
+
+ return (float) Date::PHPToExcel($result);
+ }
+
+ private static function validateCouponPeriod(float $settlement, float $maturity): void
+ {
+ if ($settlement >= $maturity) {
+ throw new Exception(Functions::NAN());
+ }
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
new file mode 100644
index 00000000000..650a4861b29
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
@@ -0,0 +1,266 @@
+getMessage();
+ }
+
+ if ($cost === 0.0) {
+ return 0.0;
+ }
+
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ // TODO Handle period value between 0 and 1 (e.g. 0.5)
+ $previousDepreciation = 0;
+ $depreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ if ($per == 1) {
+ $depreciation = $cost * $fixedDepreciationRate * $month / 12;
+ } elseif ($per == ($life + 1)) {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
+ } else {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
+ }
+ $previousDepreciation += $depreciation;
+ }
+
+ return $depreciation;
+ }
+
+ /**
+ * DDB.
+ *
+ * Returns the depreciation of an asset for a specified period using the
+ * double-declining balance method or some other method you specify.
+ *
+ * Excel Function:
+ * DDB(cost,salvage,life,period[,factor])
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation.
+ * (Sometimes called the salvage value of the asset)
+ * @param mixed $life Number of periods over which the asset is depreciated.
+ * (Sometimes called the useful life of the asset)
+ * @param mixed $period The period for which you want to calculate the
+ * depreciation. Period must use the same units as life.
+ * @param mixed $factor The rate at which the balance declines.
+ * If factor is omitted, it is assumed to be 2 (the
+ * double-declining balance method).
+ *
+ * @return float|string
+ */
+ public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+ $period = Functions::flattenSingleValue($period);
+ $factor = Functions::flattenSingleValue($factor);
+
+ try {
+ $cost = self::validateCost($cost);
+ $salvage = self::validateSalvage($salvage);
+ $life = self::validateLife($life);
+ $period = self::validatePeriod($period);
+ $factor = self::validateFactor($factor);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($period > $life) {
+ return Functions::NAN();
+ }
+
+ // Loop through each period calculating the depreciation
+ // TODO Handling for fractional $period values
+ $previousDepreciation = 0;
+ $depreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ $depreciation = min(
+ ($cost - $previousDepreciation) * ($factor / $life),
+ ($cost - $salvage - $previousDepreciation)
+ );
+ $previousDepreciation += $depreciation;
+ }
+
+ return $depreciation;
+ }
+
+ /**
+ * SLN.
+ *
+ * Returns the straight-line depreciation of an asset for one period
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation
+ * @param mixed $life Number of periods over which the asset is depreciated
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function SLN($cost, $salvage, $life)
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+
+ try {
+ $cost = self::validateCost($cost, true);
+ $salvage = self::validateSalvage($salvage, true);
+ $life = self::validateLife($life, true);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($life === 0.0) {
+ return Functions::DIV0();
+ }
+
+ return ($cost - $salvage) / $life;
+ }
+
+ /**
+ * SYD.
+ *
+ * Returns the sum-of-years' digits depreciation of an asset for a specified period.
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation
+ * @param mixed $life Number of periods over which the asset is depreciated
+ * @param mixed $period Period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function SYD($cost, $salvage, $life, $period)
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+ $period = Functions::flattenSingleValue($period);
+
+ try {
+ $cost = self::validateCost($cost, true);
+ $salvage = self::validateSalvage($salvage);
+ $life = self::validateLife($life);
+ $period = self::validatePeriod($period);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($period > $life) {
+ return Functions::NAN();
+ }
+
+ $syd = (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
+
+ return $syd;
+ }
+
+ private static function validateCost($cost, bool $negativeValueAllowed = false): float
+ {
+ $cost = FinancialValidations::validateFloat($cost);
+ if ($cost < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $cost;
+ }
+
+ private static function validateSalvage($salvage, bool $negativeValueAllowed = false): float
+ {
+ $salvage = FinancialValidations::validateFloat($salvage);
+ if ($salvage < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $salvage;
+ }
+
+ private static function validateLife($life, bool $negativeValueAllowed = false): float
+ {
+ $life = FinancialValidations::validateFloat($life);
+ if ($life < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $life;
+ }
+
+ private static function validatePeriod($period, bool $negativeValueAllowed = false): float
+ {
+ $period = FinancialValidations::validateFloat($period);
+ if ($period <= 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $period;
+ }
+
+ private static function validateMonth($month): int
+ {
+ $month = FinancialValidations::validateInt($month);
+ if ($month < 1) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $month;
+ }
+
+ private static function validateFactor($factor): float
+ {
+ $factor = FinancialValidations::validateFloat($factor);
+ if ($factor <= 0.0) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $factor;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
new file mode 100644
index 00000000000..7bebb39178f
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
@@ -0,0 +1,97 @@
+ 4)) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $basis;
+ }
+
+ /**
+ * @param mixed $price
+ */
+ public static function validatePrice($price): float
+ {
+ $price = self::validateFloat($price);
+ if ($price < 0.0) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $price;
+ }
+
+ /**
+ * @param mixed $parValue
+ */
+ public static function validateParValue($parValue): float
+ {
+ $parValue = self::validateFloat($parValue);
+ if ($parValue < 0.0) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $parValue;
+ }
+
+ /**
+ * @param mixed $yield
+ */
+ public static function validateYield($yield): float
+ {
+ $yield = self::validateFloat($yield);
+ if ($yield < 0.0) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $yield;
+ }
+
+ /**
+ * @param mixed $discount
+ */
+ public static function validateDiscount($discount): float
+ {
+ $discount = self::validateFloat($discount);
+ if ($discount <= 0.0) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $discount;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
new file mode 100644
index 00000000000..d339b13416c
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
@@ -0,0 +1,58 @@
+format('d') === $date->format('t');
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
new file mode 100644
index 00000000000..72df31e1641
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
@@ -0,0 +1,72 @@
+getMessage();
+ }
+
+ if ($nominalRate <= 0 || $periodsPerYear < 1) {
+ return Functions::NAN();
+ }
+
+ return ((1 + $nominalRate / $periodsPerYear) ** $periodsPerYear) - 1;
+ }
+
+ /**
+ * NOMINAL.
+ *
+ * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
+ *
+ * @param mixed $effectiveRate Effective interest rate as a float
+ * @param mixed $periodsPerYear Integer number of compounding payments per year
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function nominal($effectiveRate = 0, $periodsPerYear = 0)
+ {
+ $effectiveRate = Functions::flattenSingleValue($effectiveRate);
+ $periodsPerYear = Functions::flattenSingleValue($periodsPerYear);
+
+ try {
+ $effectiveRate = FinancialValidations::validateFloat($effectiveRate);
+ $periodsPerYear = FinancialValidations::validateInt($periodsPerYear);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($effectiveRate <= 0 || $periodsPerYear < 1) {
+ return Functions::NAN();
+ }
+
+ // Calculate
+ return $periodsPerYear * (($effectiveRate + 1) ** (1 / $periodsPerYear) - 1);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
new file mode 100644
index 00000000000..e167429b72b
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
@@ -0,0 +1,151 @@
+getMessage();
+ }
+
+ $daysBetweenIssueAndSettlement = YearFrac::fraction($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenFirstInterestAndSettlement = YearFrac::fraction($firstInterest, $settlement, $basis);
+ if (!is_numeric($daysBetweenFirstInterestAndSettlement)) {
+ // return date error
+ return $daysBetweenFirstInterestAndSettlement;
+ }
+
+ return $parValue * $rate * $daysBetweenIssueAndSettlement;
+ }
+
+ /**
+ * ACCRINTM.
+ *
+ * Returns the accrued interest for a security that pays interest at maturity.
+ *
+ * Excel Function:
+ * ACCRINTM(issue,settlement,rate[,par[,basis]])
+ *
+ * @param mixed $issue The security's issue date
+ * @param mixed $settlement The security's settlement (or maturity) date
+ * @param mixed $rate The security's annual coupon rate
+ * @param mixed $parValue The security's par value.
+ * If you omit parValue, ACCRINT uses $1,000.
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function atMaturity(
+ $issue,
+ $settlement,
+ $rate,
+ $parValue = 1000,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $issue = Functions::flattenSingleValue($issue);
+ $settlement = Functions::flattenSingleValue($settlement);
+ $rate = Functions::flattenSingleValue($rate);
+ $parValue = ($parValue === null) ? 1000 : Functions::flattenSingleValue($parValue);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ SecurityValidations::validateSecurityPeriod($issue, $settlement);
+ $rate = SecurityValidations::validateRate($rate);
+ $parValue = SecurityValidations::validateParValue($parValue);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenIssueAndSettlement = YearFrac::fraction($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+
+ return $parValue * $rate * $daysBetweenIssueAndSettlement;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
new file mode 100644
index 00000000000..7d8d5a3210e
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
@@ -0,0 +1,283 @@
+getMessage();
+ }
+
+ $dsc = Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
+ $e = Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
+ $n = Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
+ $a = Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
+
+ $baseYF = 1.0 + ($yield / $frequency);
+ $rfp = 100 * ($rate / $frequency);
+ $de = $dsc / $e;
+
+ $result = $redemption / $baseYF ** (--$n + $de);
+ for ($k = 0; $k <= $n; ++$k) {
+ $result += $rfp / ($baseYF ** ($k + $de));
+ }
+ $result -= $rfp * ($a / $e);
+
+ return $result;
+ }
+
+ /**
+ * PRICEDISC.
+ *
+ * Returns the price per $100 face value of a discounted security.
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $discount The security's discount rate
+ * @param mixed $redemption The security's redemption value per $100 face value
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function priceDiscounted(
+ $settlement,
+ $maturity,
+ $discount,
+ $redemption,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $discount = Functions::flattenSingleValue($discount);
+ $redemption = Functions::flattenSingleValue($redemption);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $discount = SecurityValidations::validateDiscount($discount);
+ $redemption = SecurityValidations::validateRedemption($redemption);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * PRICEMAT.
+ *
+ * Returns the price per $100 face value of a security that pays interest at maturity.
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the
+ * security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $issue The security's issue date
+ * @param mixed $rate The security's interest rate at date of issue
+ * @param mixed $yield The security's annual yield
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function priceAtMaturity(
+ $settlement,
+ $maturity,
+ $issue,
+ $rate,
+ $yield,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $issue = Functions::flattenSingleValue($issue);
+ $rate = Functions::flattenSingleValue($rate);
+ $yield = Functions::flattenSingleValue($yield);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $rate = SecurityValidations::validateRate($rate);
+ $yield = SecurityValidations::validateYield($yield);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis);
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ // return date error
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
+ (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
+ (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
+ }
+
+ /**
+ * RECEIVED.
+ *
+ * Returns the amount received at maturity for a fully invested Security.
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $investment The amount invested in the security
+ * @param mixed $discount The security's discount rate
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function received(
+ $settlement,
+ $maturity,
+ $investment,
+ $discount,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $investment = Functions::flattenSingleValue($investment);
+ $discount = Functions::flattenSingleValue($discount);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $investment = SecurityValidations::validateFloat($investment);
+ $discount = SecurityValidations::validateDiscount($discount);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($investment <= 0) {
+ return Functions::NAN();
+ }
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
new file mode 100644
index 00000000000..c5c5211b70e
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
@@ -0,0 +1,137 @@
+getMessage();
+ }
+
+ if ($price <= 0.0) {
+ return Functions::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
+ }
+
+ /**
+ * INTRATE.
+ *
+ * Returns the interest rate for a fully invested security.
+ *
+ * Excel Function:
+ * INTRATE(settlement,maturity,investment,redemption[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $investment the amount invested in the security
+ * @param mixed $redemption the amount to be received at maturity
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string
+ */
+ public static function interest(
+ $settlement,
+ $maturity,
+ $investment,
+ $redemption,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $investment = Functions::flattenSingleValue($investment);
+ $redemption = Functions::flattenSingleValue($redemption);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $investment = SecurityValidations::validateFloat($investment);
+ $redemption = SecurityValidations::validateRedemption($redemption);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($investment <= 0) {
+ return Functions::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
new file mode 100644
index 00000000000..497197b843d
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
@@ -0,0 +1,42 @@
+= $maturity) {
+ throw new Exception(Functions::NAN());
+ }
+ }
+
+ /**
+ * @param mixed $redemption
+ */
+ public static function validateRedemption($redemption): float
+ {
+ $redemption = self::validateFloat($redemption);
+ if ($redemption <= 0.0) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $redemption;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
new file mode 100644
index 00000000000..aa6269354bb
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
@@ -0,0 +1,153 @@
+getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * YIELDMAT.
+ *
+ * Returns the annual yield of a security that pays interest at maturity.
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $issue The security's issue date
+ * @param mixed $rate The security's interest rate at date of issue
+ * @param mixed $price The security's price per $100 face value
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function yieldAtMaturity(
+ $settlement,
+ $maturity,
+ $issue,
+ $rate,
+ $price,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $issue = Functions::flattenSingleValue($issue);
+ $rate = Functions::flattenSingleValue($rate);
+ $price = Functions::flattenSingleValue($price);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $rate = SecurityValidations::validateRate($rate);
+ $price = SecurityValidations::validatePrice($price);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis);
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ // return date error
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) -
+ (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
+ (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
+ ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
new file mode 100644
index 00000000000..c60af0b0d0c
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
@@ -0,0 +1,147 @@
+getMessage();
+ }
+
+ if ($discount <= 0) {
+ return Functions::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ DateTimeExcel\DateParts::year($maturity),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return Functions::NAN();
+ }
+
+ return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * TBILLPRICE.
+ *
+ * Returns the price per $100 face value for a Treasury bill.
+ *
+ * @param mixed $settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date
+ * when the Treasury bill is traded to the buyer.
+ * @param mixed $maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param mixed $discount The Treasury bill's discount rate
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function price($settlement, $maturity, $discount)
+ {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $discount = Functions::flattenSingleValue($discount);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ $discount = FinancialValidations::validateFloat($discount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($discount <= 0) {
+ return Functions::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ DateTimeExcel\DateParts::year($maturity),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return Functions::NAN();
+ }
+
+ $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
+ if ($price < 0.0) {
+ return Functions::NAN();
+ }
+
+ return $price;
+ }
+
+ /**
+ * TBILLYIELD.
+ *
+ * Returns the yield for a Treasury bill.
+ *
+ * @param mixed $settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when
+ * the Treasury bill is traded to the buyer.
+ * @param mixed $maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param mixed $price The Treasury bill's price per $100 face value
+ *
+ * @return float|string
+ */
+ public static function yield($settlement, $maturity, $price)
+ {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $price = Functions::flattenSingleValue($price);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ $price = FinancialValidations::validatePrice($price);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ DateTimeExcel\DateParts::year($maturity),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return Functions::NAN();
+ }
+
+ return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
index c11af834ff8..ddf45b23be8 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
@@ -61,17 +61,17 @@ class FormulaParser
/**
* Create a new FormulaParser.
*
- * @param string $pFormula Formula to parse
+ * @param string $formula Formula to parse
*/
- public function __construct($pFormula = '')
+ public function __construct($formula = '')
{
// Check parameters
- if ($pFormula === null) {
+ if ($formula === null) {
throw new Exception('Invalid parameter passed: formula');
}
// Initialise values
- $this->formula = trim($pFormula);
+ $this->formula = trim($formula);
// Parse!
$this->parseToTokens();
}
@@ -89,17 +89,15 @@ class FormulaParser
/**
* Get Token.
*
- * @param int $pId Token id
- *
- * @return string
+ * @param int $id Token id
*/
- public function getToken($pId = 0)
+ public function getToken(int $id = 0): FormulaToken
{
- if (isset($this->tokens[$pId])) {
- return $this->tokens[$pId];
+ if (isset($this->tokens[$id])) {
+ return $this->tokens[$id];
}
- throw new Exception("Token with id $pId does not exist.");
+ throw new Exception("Token with id $id does not exist.");
}
/**
@@ -492,7 +490,7 @@ class FormulaParser
if (
!(
- (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
)
@@ -506,7 +504,7 @@ class FormulaParser
if (
!(
- (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
+ (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
(($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
)
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
index 4d225de2d5c..68e5eead81b 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
@@ -76,16 +76,16 @@ class FormulaToken
/**
* Create a new FormulaToken.
*
- * @param string $pValue
- * @param string $pTokenType Token type (represented by TOKEN_TYPE_*)
- * @param string $pTokenSubType Token Subtype (represented by TOKEN_SUBTYPE_*)
+ * @param string $value
+ * @param string $tokenType Token type (represented by TOKEN_TYPE_*)
+ * @param string $tokenSubType Token Subtype (represented by TOKEN_SUBTYPE_*)
*/
- public function __construct($pValue, $pTokenType = self::TOKEN_TYPE_UNKNOWN, $pTokenSubType = self::TOKEN_SUBTYPE_NOTHING)
+ public function __construct($value, $tokenType = self::TOKEN_TYPE_UNKNOWN, $tokenSubType = self::TOKEN_SUBTYPE_NOTHING)
{
// Initialise values
- $this->value = $pValue;
- $this->tokenType = $pTokenType;
- $this->tokenSubType = $pTokenSubType;
+ $this->value = $value;
+ $this->tokenType = $tokenType;
+ $this->tokenSubType = $tokenSubType;
}
/**
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
index 2e8a7ecfccc..75d4582b2c7 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
@@ -3,6 +3,8 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Functions
{
@@ -13,12 +15,13 @@ class Functions
*/
const M_2DIVPI = 0.63661977236758134307553505349006;
- /** constants */
const COMPATIBILITY_EXCEL = 'Excel';
const COMPATIBILITY_GNUMERIC = 'Gnumeric';
const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
+ /** Use of RETURNDATE_PHP_NUMERIC is discouraged - not 32-bit Y2038-safe, no timezone. */
const RETURNDATE_PHP_NUMERIC = 'P';
+ /** Use of RETURNDATE_UNIX_TIMESTAMP is discouraged - not 32-bit Y2038-safe, no timezone. */
const RETURNDATE_UNIX_TIMESTAMP = 'P';
const RETURNDATE_PHP_OBJECT = 'O';
const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
@@ -250,11 +253,13 @@ class Functions
$condition = self::flattenSingleValue($condition);
if ($condition === '') {
- $condition = '=""';
+ return '=""';
}
-
if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) {
- if (!is_numeric($condition)) {
+ $condition = self::operandSpecialHandling($condition);
+ if (is_bool($condition)) {
+ return '=' . ($condition ? 'TRUE' : 'FALSE');
+ } elseif (!is_numeric($condition)) {
$condition = Calculation::wrapResult(strtoupper($condition));
}
@@ -263,9 +268,10 @@ class Functions
preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
[, $operator, $operand] = $matches;
+ $operand = self::operandSpecialHandling($operand);
if (is_numeric(trim($operand, '"'))) {
$operand = trim($operand, '"');
- } elseif (!is_numeric($operand)) {
+ } elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') {
$operand = str_replace('"', '""', $operand);
$operand = Calculation::wrapResult(strtoupper($operand));
}
@@ -273,12 +279,33 @@ class Functions
return str_replace('""""', '""', $operator . $operand);
}
+ private static function operandSpecialHandling($operand)
+ {
+ if (is_numeric($operand) || is_bool($operand)) {
+ return $operand;
+ } elseif (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
+ return strtoupper($operand);
+ }
+
+ // Check for percentage
+ if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $operand)) {
+ return ((float) rtrim($operand, '%')) / 100;
+ }
+
+ // Check for dates
+ if (($dateValueOperand = Date::stringToExcel($operand)) !== false) {
+ return $dateValueOperand;
+ }
+
+ return $operand;
+ }
+
/**
* ERROR_TYPE.
*
* @param mixed $value Value to check
*
- * @return bool
+ * @return int|string
*/
public static function errorType($value = '')
{
@@ -551,7 +578,7 @@ class Functions
/**
* Convert a multi-dimensional array to a simple 1-dimensional array.
*
- * @param array $array Array to be flattened
+ * @param array|mixed $array Array to be flattened
*
* @return array Flattened array
*/
@@ -584,7 +611,7 @@ class Functions
/**
* Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing.
*
- * @param array $array Array to be flattened
+ * @param array|mixed $array Array to be flattened
*
* @return array Flattened array
*/
@@ -634,15 +661,17 @@ class Functions
* ISFORMULA.
*
* @param mixed $cellReference The cell to check
- * @param Cell $pCell The current cell (containing this formula)
+ * @param ?Cell $cell The current cell (containing this formula)
*
* @return bool|string
*/
- public static function isFormula($cellReference = '', ?Cell $pCell = null)
+ public static function isFormula($cellReference = '', ?Cell $cell = null)
{
- if ($pCell === null) {
+ if ($cell === null) {
return self::REF();
}
+ $cellReference = self::expandDefinedName((string) $cellReference, $cell);
+ $cellReference = self::trimTrailingRange($cellReference);
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
@@ -650,9 +679,33 @@ class Functions
$worksheetName = str_replace("''", "'", trim($matches[2], "'"));
$worksheet = (!empty($worksheetName))
- ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
- : $pCell->getWorksheet();
+ ? $cell->getWorksheet()->getParent()->getSheetByName($worksheetName)
+ : $cell->getWorksheet();
return $worksheet->getCell($cellReference)->isFormula();
}
+
+ public static function expandDefinedName(string $coordinate, Cell $cell): string
+ {
+ $worksheet = $cell->getWorksheet();
+ $spreadsheet = $worksheet->getParent();
+ // Uppercase coordinate
+ $pCoordinatex = strtoupper($coordinate);
+ // Eliminate leading equal sign
+ $pCoordinatex = Worksheet::pregReplace('/^=/', '', $pCoordinatex);
+ $defined = $spreadsheet->getDefinedName($pCoordinatex, $worksheet);
+ if ($defined !== null) {
+ $worksheet2 = $defined->getWorkSheet();
+ if (!$defined->isFormula() && $worksheet2 !== null) {
+ $coordinate = "'" . $worksheet2->getTitle() . "'!" . Worksheet::pregReplace('/^=/', '', $defined->getValue());
+ }
+ }
+
+ return $coordinate;
+ }
+
+ public static function trimTrailingRange(string $coordinate): string
+ {
+ return Worksheet::pregReplace('/:[\\w\$]+$/', '', $coordinate);
+ }
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
new file mode 100644
index 00000000000..8b53464fc47
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
@@ -0,0 +1,11 @@
+ 0) && ($returnValue == $argCount);
+ return Logical\Operations::logicalAnd(...$args);
}
/**
@@ -114,8 +92,13 @@ class Logical
*
* Boolean arguments are treated as True or False as appropriate
* Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
- * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
- * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @Deprecated 1.17.0
+ *
+ * @see Logical\Operations::logicalOr()
+ * Use the logicalOr() method in the Logical\Operations class instead
*
* @param mixed $args Data values
*
@@ -123,29 +106,15 @@ class Logical
*/
public static function logicalOr(...$args)
{
- $args = Functions::flattenArray($args);
-
- if (count($args) == 0) {
- return Functions::VALUE();
- }
-
- $args = array_filter($args, function ($value) {
- return $value !== null || (is_string($value) && trim($value) == '');
- });
-
- $returnValue = self::countTrueValues($args);
- if (is_string($returnValue)) {
- return $returnValue;
- }
-
- return $returnValue > 0;
+ return Logical\Operations::logicalOr(...$args);
}
/**
* LOGICAL_XOR.
*
* Returns the Exclusive Or logical operation for one or more supplied conditions.
- * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE, and FALSE otherwise.
+ * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE,
+ * and FALSE otherwise.
*
* Excel Function:
* =XOR(logical1[,logical2[, ...]])
@@ -155,8 +124,13 @@ class Logical
*
* Boolean arguments are treated as True or False as appropriate
* Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
- * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
- * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @Deprecated 1.17.0
+ *
+ * @see Logical\Operations::logicalXor()
+ * Use the logicalXor() method in the Logical\Operations class instead
*
* @param mixed $args Data values
*
@@ -164,22 +138,7 @@ class Logical
*/
public static function logicalXor(...$args)
{
- $args = Functions::flattenArray($args);
-
- if (count($args) == 0) {
- return Functions::VALUE();
- }
-
- $args = array_filter($args, function ($value) {
- return $value !== null || (is_string($value) && trim($value) == '');
- });
-
- $returnValue = self::countTrueValues($args);
- if (is_string($returnValue)) {
- return $returnValue;
- }
-
- return $returnValue % 2 == 1;
+ return Logical\Operations::logicalXor(...$args);
}
/**
@@ -194,8 +153,13 @@ class Logical
*
* Boolean arguments are treated as True or False as appropriate
* Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
- * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
- * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @Deprecated 1.17.0
+ *
+ * @see Logical\Operations::NOT()
+ * Use the NOT() method in the Logical\Operations class instead
*
* @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
*
@@ -203,20 +167,7 @@ class Logical
*/
public static function NOT($logical = false)
{
- $logical = Functions::flattenSingleValue($logical);
-
- if (is_string($logical)) {
- $logical = strtoupper($logical);
- if (($logical == 'TRUE') || ($logical == Calculation::getTRUE())) {
- return false;
- } elseif (($logical == 'FALSE') || ($logical == Calculation::getFALSE())) {
- return true;
- }
-
- return Functions::VALUE();
- }
-
- return !$logical;
+ return Logical\Operations::NOT($logical);
}
/**
@@ -232,18 +183,23 @@ class Logical
* the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.
* This argument can use any comparison calculation operator.
* ReturnIfTrue is the value that is returned if condition evaluates to TRUE.
- * For example, if this argument is the text string "Within budget" and the condition argument evaluates to TRUE,
- * then the IF function returns the text "Within budget"
- * If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero). To display the word TRUE, use
- * the logical value TRUE for this argument.
+ * For example, if this argument is the text string "Within budget" and the condition argument
+ * evaluates to TRUE, then the IF function returns the text "Within budget"
+ * If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero).
+ * To display the word TRUE, use the logical value TRUE for this argument.
* ReturnIfTrue can be another formula.
* ReturnIfFalse is the value that is returned if condition evaluates to FALSE.
- * For example, if this argument is the text string "Over budget" and the condition argument evaluates to FALSE,
- * then the IF function returns the text "Over budget".
+ * For example, if this argument is the text string "Over budget" and the condition argument
+ * evaluates to FALSE, then the IF function returns the text "Over budget".
* If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.
* If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.
* ReturnIfFalse can be another formula.
*
+ * @Deprecated 1.17.0
+ *
+ * @see Logical\Conditional::statementIf()
+ * Use the statementIf() method in the Logical\Conditional class instead
+ *
* @param mixed $condition Condition to evaluate
* @param mixed $returnIfTrue Value to return when condition is true
* @param mixed $returnIfFalse Optional value to return when condition is false
@@ -252,15 +208,7 @@ class Logical
*/
public static function statementIf($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
{
- if (Functions::isError($condition)) {
- return $condition;
- }
-
- $condition = ($condition === null) ? true : (bool) Functions::flattenSingleValue($condition);
- $returnIfTrue = ($returnIfTrue === null) ? 0 : Functions::flattenSingleValue($returnIfTrue);
- $returnIfFalse = ($returnIfFalse === null) ? false : Functions::flattenSingleValue($returnIfFalse);
-
- return ($condition) ? $returnIfTrue : $returnIfFalse;
+ return Logical\Conditional::statementIf($condition, $returnIfTrue, $returnIfFalse);
}
/**
@@ -274,11 +222,19 @@ class Logical
* Expression
* The expression to compare to a list of values.
* value1, value2, ... value_n
- * A list of values that are compared to expression. The SWITCH function is looking for the first value that matches the expression.
+ * A list of values that are compared to expression.
+ * The SWITCH function is looking for the first value that matches the expression.
* result1, result2, ... result_n
- * A list of results. The SWITCH function returns the corresponding result when a value matches expression.
+ * A list of results. The SWITCH function returns the corresponding result when a value
+ * matches expression.
* default
- * Optional. It is the default to return if expression does not match any of the values (value1, value2, ... value_n).
+ * Optional. It is the default to return if expression does not match any of the values
+ * (value1, value2, ... value_n).
+ *
+ * @Deprecated 1.17.0
+ *
+ * @see Logical\Conditional::statementSwitch()
+ * Use the statementSwitch() method in the Logical\Conditional class instead
*
* @param mixed $arguments Statement arguments
*
@@ -286,33 +242,7 @@ class Logical
*/
public static function statementSwitch(...$arguments)
{
- $result = Functions::VALUE();
-
- if (count($arguments) > 0) {
- $targetValue = Functions::flattenSingleValue($arguments[0]);
- $argc = count($arguments) - 1;
- $switchCount = floor($argc / 2);
- $switchSatisfied = false;
- $hasDefaultClause = $argc % 2 !== 0;
- $defaultClause = $argc % 2 === 0 ? null : $arguments[count($arguments) - 1];
-
- if ($switchCount) {
- for ($index = 0; $index < $switchCount; ++$index) {
- if ($targetValue == $arguments[$index * 2 + 1]) {
- $result = $arguments[$index * 2 + 2];
- $switchSatisfied = true;
-
- break;
- }
- }
- }
-
- if (!$switchSatisfied) {
- $result = $hasDefaultClause ? $defaultClause : Functions::NA();
- }
- }
-
- return $result;
+ return Logical\Conditional::statementSwitch(...$arguments);
}
/**
@@ -321,6 +251,11 @@ class Logical
* Excel Function:
* =IFERROR(testValue,errorpart)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Logical\Conditional::IFERROR()
+ * Use the IFERROR() method in the Logical\Conditional class instead
+ *
* @param mixed $testValue Value to check, is also the value returned when no error
* @param mixed $errorpart Value to return when testValue is an error condition
*
@@ -328,10 +263,7 @@ class Logical
*/
public static function IFERROR($testValue = '', $errorpart = '')
{
- $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
- $errorpart = ($errorpart === null) ? '' : Functions::flattenSingleValue($errorpart);
-
- return self::statementIf(Functions::isError($testValue), $errorpart, $testValue);
+ return Logical\Conditional::IFERROR($testValue, $errorpart);
}
/**
@@ -340,6 +272,11 @@ class Logical
* Excel Function:
* =IFNA(testValue,napart)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Logical\Conditional::IFNA()
+ * Use the IFNA() method in the Logical\Conditional class instead
+ *
* @param mixed $testValue Value to check, is also the value returned when not an NA
* @param mixed $napart Value to return when testValue is an NA condition
*
@@ -347,10 +284,7 @@ class Logical
*/
public static function IFNA($testValue = '', $napart = '')
{
- $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
- $napart = ($napart === null) ? '' : Functions::flattenSingleValue($napart);
-
- return self::statementIf(Functions::isNa($testValue), $napart, $testValue);
+ return Logical\Conditional::IFNA($testValue, $napart);
}
/**
@@ -364,27 +298,17 @@ class Logical
* returnIfTrue1 ... returnIfTrue_n
* Value returned if corresponding testValue (nth) was true
*
+ * @Deprecated 1.17.0
+ *
+ * @see Logical\Conditional::IFS()
+ * Use the IFS() method in the Logical\Conditional class instead
+ *
* @param mixed ...$arguments Statement arguments
*
* @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
*/
public static function IFS(...$arguments)
{
- if (count($arguments) % 2 != 0) {
- return Functions::NA();
- }
- // We use instance of Exception as a falseValue in order to prevent string collision with value in cell
- $falseValueException = new Exception();
- for ($i = 0; $i < count($arguments); $i += 2) {
- $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]);
- $returnIfTrue = ($arguments[$i + 1] === null) ? '' : Functions::flattenSingleValue($arguments[$i + 1]);
- $result = self::statementIf($testValue, $returnIfTrue, $falseValueException);
-
- if ($result !== $falseValueException) {
- return $result;
- }
- }
-
- return Functions::NA();
+ return Logical\Conditional::IFS(...$arguments);
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Boolean.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Boolean.php
new file mode 100644
index 00000000000..8f1e9354d85
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Boolean.php
@@ -0,0 +1,36 @@
+ 0) {
+ $targetValue = Functions::flattenSingleValue($arguments[0]);
+ $argc = count($arguments) - 1;
+ $switchCount = floor($argc / 2);
+ $hasDefaultClause = $argc % 2 !== 0;
+ $defaultClause = $argc % 2 === 0 ? null : $arguments[$argc];
+
+ $switchSatisfied = false;
+ if ($switchCount > 0) {
+ for ($index = 0; $index < $switchCount; ++$index) {
+ if ($targetValue == $arguments[$index * 2 + 1]) {
+ $result = $arguments[$index * 2 + 2];
+ $switchSatisfied = true;
+
+ break;
+ }
+ }
+ }
+
+ if ($switchSatisfied !== true) {
+ $result = $hasDefaultClause ? $defaultClause : Functions::NA();
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * IFERROR.
+ *
+ * Excel Function:
+ * =IFERROR(testValue,errorpart)
+ *
+ * @param mixed $testValue Value to check, is also the value returned when no error
+ * @param mixed $errorpart Value to return when testValue is an error condition
+ *
+ * @return mixed The value of errorpart or testValue determined by error condition
+ */
+ public static function IFERROR($testValue = '', $errorpart = '')
+ {
+ $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
+ $errorpart = ($errorpart === null) ? '' : Functions::flattenSingleValue($errorpart);
+
+ return self::statementIf(Functions::isError($testValue), $errorpart, $testValue);
+ }
+
+ /**
+ * IFNA.
+ *
+ * Excel Function:
+ * =IFNA(testValue,napart)
+ *
+ * @param mixed $testValue Value to check, is also the value returned when not an NA
+ * @param mixed $napart Value to return when testValue is an NA condition
+ *
+ * @return mixed The value of errorpart or testValue determined by error condition
+ */
+ public static function IFNA($testValue = '', $napart = '')
+ {
+ $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
+ $napart = ($napart === null) ? '' : Functions::flattenSingleValue($napart);
+
+ return self::statementIf(Functions::isNa($testValue), $napart, $testValue);
+ }
+
+ /**
+ * IFS.
+ *
+ * Excel Function:
+ * =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n)
+ *
+ * testValue1 ... testValue_n
+ * Conditions to Evaluate
+ * returnIfTrue1 ... returnIfTrue_n
+ * Value returned if corresponding testValue (nth) was true
+ *
+ * @param mixed ...$arguments Statement arguments
+ *
+ * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
+ */
+ public static function IFS(...$arguments)
+ {
+ $argumentCount = count($arguments);
+
+ if ($argumentCount % 2 != 0) {
+ return Functions::NA();
+ }
+ // We use instance of Exception as a falseValue in order to prevent string collision with value in cell
+ $falseValueException = new Exception();
+ for ($i = 0; $i < $argumentCount; $i += 2) {
+ $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]);
+ $returnIfTrue = ($arguments[$i + 1] === null) ? '' : Functions::flattenSingleValue($arguments[$i + 1]);
+ $result = self::statementIf($testValue, $returnIfTrue, $falseValueException);
+
+ if ($result !== $falseValueException) {
+ return $result;
+ }
+ }
+
+ return Functions::NA();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
new file mode 100644
index 00000000000..6bfb6a545b6
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
@@ -0,0 +1,198 @@
+ 0) && ($returnValue == $argCount);
+ }
+
+ /**
+ * LOGICAL_OR.
+ *
+ * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
+ *
+ * Excel Function:
+ * =OR(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @param mixed $args Data values
+ *
+ * @return bool|string the logical OR of the arguments
+ */
+ public static function logicalOr(...$args)
+ {
+ $args = Functions::flattenArray($args);
+
+ if (count($args) == 0) {
+ return Functions::VALUE();
+ }
+
+ $args = array_filter($args, function ($value) {
+ return $value !== null || (is_string($value) && trim($value) == '');
+ });
+
+ $returnValue = self::countTrueValues($args);
+ if (is_string($returnValue)) {
+ return $returnValue;
+ }
+
+ return $returnValue > 0;
+ }
+
+ /**
+ * LOGICAL_XOR.
+ *
+ * Returns the Exclusive Or logical operation for one or more supplied conditions.
+ * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE,
+ * and FALSE otherwise.
+ *
+ * Excel Function:
+ * =XOR(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @param mixed $args Data values
+ *
+ * @return bool|string the logical XOR of the arguments
+ */
+ public static function logicalXor(...$args)
+ {
+ $args = Functions::flattenArray($args);
+
+ if (count($args) == 0) {
+ return Functions::VALUE();
+ }
+
+ $args = array_filter($args, function ($value) {
+ return $value !== null || (is_string($value) && trim($value) == '');
+ });
+
+ $returnValue = self::countTrueValues($args);
+ if (is_string($returnValue)) {
+ return $returnValue;
+ }
+
+ return $returnValue % 2 == 1;
+ }
+
+ /**
+ * NOT.
+ *
+ * Returns the boolean inverse of the argument.
+ *
+ * Excel Function:
+ * =NOT(logical)
+ *
+ * The argument must evaluate to a logical value such as TRUE or FALSE
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
+ *
+ * @return bool|string the boolean inverse of the argument
+ */
+ public static function NOT($logical = false)
+ {
+ $logical = Functions::flattenSingleValue($logical);
+
+ if (is_string($logical)) {
+ $logical = mb_strtoupper($logical, 'UTF-8');
+ if (($logical == 'TRUE') || ($logical == Calculation::getTRUE())) {
+ return false;
+ } elseif (($logical == 'FALSE') || ($logical == Calculation::getFALSE())) {
+ return true;
+ }
+
+ return Functions::VALUE();
+ }
+
+ return !$logical;
+ }
+
+ /**
+ * @return int|string
+ */
+ private static function countTrueValues(array $args)
+ {
+ $trueValueCount = 0;
+
+ foreach ($args as $arg) {
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $trueValueCount += $arg;
+ } elseif ((is_numeric($arg)) && (!is_string($arg))) {
+ $trueValueCount += ((int) $arg != 0);
+ } elseif (is_string($arg)) {
+ $arg = mb_strtoupper($arg, 'UTF-8');
+ if (($arg == 'TRUE') || ($arg == Calculation::getTRUE())) {
+ $arg = true;
+ } elseif (($arg == 'FALSE') || ($arg == Calculation::getFALSE())) {
+ $arg = false;
+ } else {
+ return Functions::VALUE();
+ }
+ $trueValueCount += ($arg != 0);
+ }
+ }
+
+ return $trueValueCount;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
index 45aa9239641..67650480fdc 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
@@ -2,11 +2,20 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Address;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\HLookup;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Indirect;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Lookup;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Offset;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\RowColumnInformation;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\VLookup;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
-use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
-use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+/**
+ * @deprecated 1.18.0
+ */
class LookupRef
{
/**
@@ -17,15 +26,20 @@ class LookupRef
* Excel Function:
* =ADDRESS(row, column, [relativity], [referenceStyle], [sheetText])
*
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\Address::cell()
+ * Use the cell() method in the LookupRef\Address class instead
+ *
* @param mixed $row Row number to use in the cell reference
* @param mixed $column Column number to use in the cell reference
* @param int $relativity Flag indicating the type of reference to return
* 1 or omitted Absolute
- * 2 Absolute row; relative column
- * 3 Relative row; absolute column
- * 4 Relative
+ * 2 Absolute row; relative column
+ * 3 Relative row; absolute column
+ * 4 Relative
* @param bool $referenceStyle A logical value that specifies the A1 or R1C1 reference style.
- * TRUE or omitted CELL_ADDRESS returns an A1-style reference
+ * TRUE or omitted CELL_ADDRESS returns an A1-style reference
* FALSE CELL_ADDRESS returns an R1C1-style reference
* @param string $sheetText Optional Name of worksheet to use
*
@@ -33,87 +47,34 @@ class LookupRef
*/
public static function cellAddress($row, $column, $relativity = 1, $referenceStyle = true, $sheetText = '')
{
- $row = Functions::flattenSingleValue($row);
- $column = Functions::flattenSingleValue($column);
- $relativity = Functions::flattenSingleValue($relativity);
- $sheetText = Functions::flattenSingleValue($sheetText);
-
- if (($row < 1) || ($column < 1)) {
- return Functions::VALUE();
- }
-
- if ($sheetText > '') {
- if (strpos($sheetText, ' ') !== false) {
- $sheetText = "'" . $sheetText . "'";
- }
- $sheetText .= '!';
- }
- if ((!is_bool($referenceStyle)) || $referenceStyle) {
- $rowRelative = $columnRelative = '$';
- $column = Coordinate::stringFromColumnIndex($column);
- if (($relativity == 2) || ($relativity == 4)) {
- $columnRelative = '';
- }
- if (($relativity == 3) || ($relativity == 4)) {
- $rowRelative = '';
- }
-
- return $sheetText . $columnRelative . $column . $rowRelative . $row;
- }
- if (($relativity == 2) || ($relativity == 4)) {
- $column = '[' . $column . ']';
- }
- if (($relativity == 3) || ($relativity == 4)) {
- $row = '[' . $row . ']';
- }
-
- return $sheetText . 'R' . $row . 'C' . $column;
+ return Address::cell($row, $column, $relativity, $referenceStyle, $sheetText);
}
/**
* COLUMN.
*
* Returns the column number of the given cell reference
- * If the cell reference is a range of cells, COLUMN returns the column numbers of each column in the reference as a horizontal array.
- * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
- * reference of the cell in which the COLUMN function appears; otherwise this function returns 0.
+ * If the cell reference is a range of cells, COLUMN returns the column numbers of each column
+ * in the reference as a horizontal array.
+ * If cell reference is omitted, and the function is being called through the calculation engine,
+ * then it is assumed to be the reference of the cell in which the COLUMN function appears;
+ * otherwise this function returns 1.
*
* Excel Function:
* =COLUMN([cellAddress])
*
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\RowColumnInformation::COLUMN()
+ * Use the COLUMN() method in the LookupRef\RowColumnInformation class instead
+ *
* @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
*
- * @return int|int[]
+ * @return int|int[]|string
*/
- public static function COLUMN($cellAddress = null)
+ public static function COLUMN($cellAddress = null, ?Cell $cell = null)
{
- if ($cellAddress === null || trim($cellAddress) === '') {
- return 0;
- }
-
- if (is_array($cellAddress)) {
- foreach ($cellAddress as $columnKey => $value) {
- $columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
-
- return (int) Coordinate::columnIndexFromString($columnKey);
- }
- } else {
- [$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- if (strpos($cellAddress, ':') !== false) {
- [$startAddress, $endAddress] = explode(':', $cellAddress);
- $startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
- $endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
- $returnValue = [];
- do {
- $returnValue[] = (int) Coordinate::columnIndexFromString($startAddress);
- } while ($startAddress++ != $endAddress);
-
- return $returnValue;
- }
- $cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
-
- return (int) Coordinate::columnIndexFromString($cellAddress);
- }
+ return RowColumnInformation::COLUMN($cellAddress, $cell);
}
/**
@@ -124,73 +85,46 @@ class LookupRef
* Excel Function:
* =COLUMNS(cellAddress)
*
- * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\RowColumnInformation::COLUMNS()
+ * Use the COLUMNS() method in the LookupRef\RowColumnInformation class instead
+ *
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of columns
*
* @return int|string The number of columns in cellAddress, or a string if arguments are invalid
*/
public static function COLUMNS($cellAddress = null)
{
- if ($cellAddress === null || $cellAddress === '') {
- return 1;
- } elseif (!is_array($cellAddress)) {
- return Functions::VALUE();
- }
-
- reset($cellAddress);
- $isMatrix = (is_numeric(key($cellAddress)));
- [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
-
- if ($isMatrix) {
- return $rows;
- }
-
- return $columns;
+ return RowColumnInformation::COLUMNS($cellAddress);
}
/**
* ROW.
*
* Returns the row number of the given cell reference
- * If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference as a vertical array.
- * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
- * reference of the cell in which the ROW function appears; otherwise this function returns 0.
+ * If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference
+ * as a vertical array.
+ * If cell reference is omitted, and the function is being called through the calculation engine,
+ * then it is assumed to be the reference of the cell in which the ROW function appears;
+ * otherwise this function returns 1.
*
* Excel Function:
* =ROW([cellAddress])
*
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\RowColumnInformation::ROW()
+ * Use the ROW() method in the LookupRef\RowColumnInformation class instead
+ *
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
*
* @return int|mixed[]|string
*/
- public static function ROW($cellAddress = null)
+ public static function ROW($cellAddress = null, ?Cell $cell = null)
{
- if ($cellAddress === null || trim($cellAddress) === '') {
- return 0;
- }
-
- if (is_array($cellAddress)) {
- foreach ($cellAddress as $columnKey => $rowValue) {
- foreach ($rowValue as $rowKey => $cellValue) {
- return (int) preg_replace('/\D/', '', $rowKey);
- }
- }
- } else {
- [$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- if (strpos($cellAddress, ':') !== false) {
- [$startAddress, $endAddress] = explode(':', $cellAddress);
- $startAddress = preg_replace('/\D/', '', $startAddress);
- $endAddress = preg_replace('/\D/', '', $endAddress);
- $returnValue = [];
- do {
- $returnValue[][] = (int) $startAddress;
- } while ($startAddress++ != $endAddress);
-
- return $returnValue;
- }
- [$cellAddress] = explode(':', $cellAddress);
-
- return (int) preg_replace('/\D/', '', $cellAddress);
- }
+ return RowColumnInformation::ROW($cellAddress, $cell);
}
/**
@@ -201,27 +135,19 @@ class LookupRef
* Excel Function:
* =ROWS(cellAddress)
*
- * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\RowColumnInformation::ROWS()
+ * Use the ROWS() method in the LookupRef\RowColumnInformation class instead
+ *
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of rows
*
* @return int|string The number of rows in cellAddress, or a string if arguments are invalid
*/
public static function ROWS($cellAddress = null)
{
- if ($cellAddress === null || $cellAddress === '') {
- return 1;
- } elseif (!is_array($cellAddress)) {
- return Functions::VALUE();
- }
-
- reset($cellAddress);
- $isMatrix = (is_numeric(key($cellAddress)));
- [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
-
- if ($isMatrix) {
- return $columns;
- }
-
- return $rows;
+ return RowColumnInformation::ROWS($cellAddress);
}
/**
@@ -230,29 +156,20 @@ class LookupRef
* Excel Function:
* =HYPERLINK(linkURL,displayName)
*
- * @param string $linkURL Value to check, is also the value returned when no error
- * @param string $displayName Value to return when testValue is an error condition
- * @param Cell $pCell The cell to set the hyperlink in
+ * @Deprecated 1.18.0
*
- * @return mixed The value of $displayName (or $linkURL if $displayName was blank)
+ * @param mixed $linkURL Expect string. Value to check, is also the value returned when no error
+ * @param mixed $displayName Expect string. Value to return when testValue is an error condition
+ * @param Cell $cell The cell to set the hyperlink in
+ *
+ * @return string The value of $displayName (or $linkURL if $displayName was blank)
+ *
+ *@see LookupRef\Hyperlink::set()
+ * Use the set() method in the LookupRef\Hyperlink class instead
*/
- public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $pCell = null)
+ public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $cell = null)
{
- $linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL);
- $displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName);
-
- if ((!is_object($pCell)) || (trim($linkURL) == '')) {
- return Functions::REF();
- }
-
- if ((is_object($displayName)) || trim($displayName) == '') {
- $displayName = $linkURL;
- }
-
- $pCell->getHyperlink()->setUrl($linkURL);
- $pCell->getHyperlink()->setTooltip($displayName);
-
- return $displayName;
+ return LookupRef\Hyperlink::set($linkURL, $displayName, $cell);
}
/**
@@ -264,56 +181,21 @@ class LookupRef
* Excel Function:
* =INDIRECT(cellAddress)
*
+ * @Deprecated 1.18.0
+ *
+ * @param array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
+ * @param Cell $cell The current cell (containing this formula)
+ *
+ * @return array|string An array containing a cell or range of cells, or a string on error
+ *
+ *@see LookupRef\Indirect::INDIRECT()
+ * Use the INDIRECT() method in the LookupRef\Indirect class instead
+ *
* NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010
- *
- * @param null|array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
- * @param Cell $pCell The current cell (containing this formula)
- *
- * @return mixed The cells referenced by cellAddress
- *
- * @TODO Support for the optional a1 parameter introduced in Excel 2010
*/
- public static function INDIRECT($cellAddress = null, ?Cell $pCell = null)
+ public static function INDIRECT($cellAddress, Cell $cell)
{
- $cellAddress = Functions::flattenSingleValue($cellAddress);
- if ($cellAddress === null || $cellAddress === '') {
- return Functions::REF();
- }
-
- $cellAddress1 = $cellAddress;
- $cellAddress2 = null;
- if (strpos($cellAddress, ':') !== false) {
- [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
- }
-
- if (
- (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
- (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches)))
- ) {
- if (!preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/i', $cellAddress1, $matches)) {
- return Functions::REF();
- }
-
- if (strpos($cellAddress, '!') !== false) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractNamedRange($cellAddress, $pSheet, false);
- }
-
- if (strpos($cellAddress, '!') !== false) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, false);
+ return Indirect::INDIRECT($cellAddress, true, $cell);
}
/**
@@ -326,87 +208,34 @@ class LookupRef
* Excel Function:
* =OFFSET(cellAddress, rows, cols, [height], [width])
*
- * @param null|string $cellAddress The reference from which you want to base the offset. Reference must refer to a cell or
- * range of adjacent cells; otherwise, OFFSET returns the #VALUE! error value.
- * @param mixed $rows The number of rows, up or down, that you want the upper-left cell to refer to.
- * Using 5 as the rows argument specifies that the upper-left cell in the reference is
- * five rows below reference. Rows can be positive (which means below the starting reference)
- * or negative (which means above the starting reference).
- * @param mixed $columns The number of columns, to the left or right, that you want the upper-left cell of the result
- * to refer to. Using 5 as the cols argument specifies that the upper-left cell in the
- * reference is five columns to the right of reference. Cols can be positive (which means
- * to the right of the starting reference) or negative (which means to the left of the
- * starting reference).
- * @param mixed $height The height, in number of rows, that you want the returned reference to be. Height must be a positive number.
- * @param mixed $width The width, in number of columns, that you want the returned reference to be. Width must be a positive number.
+ * @Deprecated 1.18.0
*
- * @return string A reference to a cell or range of cells
+ * @see LookupRef\Offset::OFFSET()
+ * Use the OFFSET() method in the LookupRef\Offset class instead
+ *
+ * @param null|string $cellAddress The reference from which you want to base the offset.
+ * Reference must refer to a cell or range of adjacent cells;
+ * otherwise, OFFSET returns the #VALUE! error value.
+ * @param mixed $rows The number of rows, up or down, that you want the upper-left cell to refer to.
+ * Using 5 as the rows argument specifies that the upper-left cell in the
+ * reference is five rows below reference. Rows can be positive (which means
+ * below the starting reference) or negative (which means above the starting
+ * reference).
+ * @param mixed $columns The number of columns, to the left or right, that you want the upper-left cell
+ * of the result to refer to. Using 5 as the cols argument specifies that the
+ * upper-left cell in the reference is five columns to the right of reference.
+ * Cols can be positive (which means to the right of the starting reference)
+ * or negative (which means to the left of the starting reference).
+ * @param mixed $height The height, in number of rows, that you want the returned reference to be.
+ * Height must be a positive number.
+ * @param mixed $width The width, in number of columns, that you want the returned reference to be.
+ * Width must be a positive number.
+ *
+ * @return array|string An array containing a cell or range of cells, or a string on error
*/
- public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $pCell = null)
+ public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $cell = null)
{
- $rows = Functions::flattenSingleValue($rows);
- $columns = Functions::flattenSingleValue($columns);
- $height = Functions::flattenSingleValue($height);
- $width = Functions::flattenSingleValue($width);
- if ($cellAddress === null) {
- return 0;
- }
-
- if (!is_object($pCell)) {
- return Functions::REF();
- }
-
- $sheetName = null;
- if (strpos($cellAddress, '!')) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- }
- if (strpos($cellAddress, ':')) {
- [$startCell, $endCell] = explode(':', $cellAddress);
- } else {
- $startCell = $endCell = $cellAddress;
- }
- [$startCellColumn, $startCellRow] = Coordinate::coordinateFromString($startCell);
- [$endCellColumn, $endCellRow] = Coordinate::coordinateFromString($endCell);
-
- $startCellRow += $rows;
- $startCellColumn = Coordinate::columnIndexFromString($startCellColumn) - 1;
- $startCellColumn += $columns;
-
- if (($startCellRow <= 0) || ($startCellColumn < 0)) {
- return Functions::REF();
- }
- $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
- if (($width != null) && (!is_object($width))) {
- $endCellColumn = $startCellColumn + $width - 1;
- } else {
- $endCellColumn += $columns;
- }
- $startCellColumn = Coordinate::stringFromColumnIndex($startCellColumn + 1);
-
- if (($height != null) && (!is_object($height))) {
- $endCellRow = $startCellRow + $height - 1;
- } else {
- $endCellRow += $rows;
- }
-
- if (($endCellRow <= 0) || ($endCellColumn < 0)) {
- return Functions::REF();
- }
- $endCellColumn = Coordinate::stringFromColumnIndex($endCellColumn + 1);
-
- $cellAddress = $startCellColumn . $startCellRow;
- if (($startCellColumn != $endCellColumn) || ($startCellRow != $endCellRow)) {
- $cellAddress .= ':' . $endCellColumn . $endCellRow;
- }
-
- if ($sheetName !== null) {
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, false);
+ return Offset::OFFSET($cellAddress, $rows, $columns, $height, $width, $cell);
}
/**
@@ -418,31 +247,16 @@ class LookupRef
* Excel Function:
* =CHOOSE(index_num, value1, [value2], ...)
*
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\Selection::choose()
+ * Use the choose() method in the LookupRef\Selection class instead
+ *
* @return mixed The selected value
*/
public static function CHOOSE(...$chooseArgs)
{
- $chosenEntry = Functions::flattenArray(array_shift($chooseArgs));
- $entryCount = count($chooseArgs) - 1;
-
- if (is_array($chosenEntry)) {
- $chosenEntry = array_shift($chosenEntry);
- }
- if ((is_numeric($chosenEntry)) && (!is_bool($chosenEntry))) {
- --$chosenEntry;
- } else {
- return Functions::VALUE();
- }
- $chosenEntry = floor($chosenEntry);
- if (($chosenEntry < 0) || ($chosenEntry > $entryCount)) {
- return Functions::VALUE();
- }
-
- if (is_array($chooseArgs[$chosenEntry])) {
- return Functions::flattenArray($chooseArgs[$chosenEntry]);
- }
-
- return $chooseArgs[$chosenEntry];
+ return LookupRef\Selection::choose(...$chooseArgs);
}
/**
@@ -453,6 +267,11 @@ class LookupRef
* Excel Function:
* =MATCH(lookup_value, lookup_array, [match_type])
*
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\ExcelMatch::MATCH()
+ * Use the MATCH() method in the LookupRef\ExcelMatch class instead
+ *
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param mixed $lookupArray The range of cells being searched
* @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below.
@@ -462,145 +281,7 @@ class LookupRef
*/
public static function MATCH($lookupValue, $lookupArray, $matchType = 1)
{
- $lookupArray = Functions::flattenArray($lookupArray);
- $lookupValue = Functions::flattenSingleValue($lookupValue);
- $matchType = ($matchType === null) ? 1 : (int) Functions::flattenSingleValue($matchType);
-
- // MATCH is not case sensitive, so we convert lookup value to be lower cased in case it's string type.
- if (is_string($lookupValue)) {
- $lookupValue = StringHelper::strToLower($lookupValue);
- }
-
- // Lookup_value type has to be number, text, or logical values
- if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
- return Functions::NA();
- }
-
- // Match_type is 0, 1 or -1
- if (($matchType !== 0) && ($matchType !== -1) && ($matchType !== 1)) {
- return Functions::NA();
- }
-
- // Lookup_array should not be empty
- $lookupArraySize = count($lookupArray);
- if ($lookupArraySize <= 0) {
- return Functions::NA();
- }
-
- if ($matchType == 1) {
- // If match_type is 1 the list has to be processed from last to first
-
- $lookupArray = array_reverse($lookupArray);
- $keySet = array_reverse(array_keys($lookupArray));
- }
-
- // Lookup_array should contain only number, text, or logical values, or empty (null) cells
- foreach ($lookupArray as $i => $lookupArrayValue) {
- // check the type of the value
- if (
- (!is_numeric($lookupArrayValue)) && (!is_string($lookupArrayValue)) &&
- (!is_bool($lookupArrayValue)) && ($lookupArrayValue !== null)
- ) {
- return Functions::NA();
- }
- // Convert strings to lowercase for case-insensitive testing
- if (is_string($lookupArrayValue)) {
- $lookupArray[$i] = StringHelper::strToLower($lookupArrayValue);
- }
- if (($lookupArrayValue === null) && (($matchType == 1) || ($matchType == -1))) {
- unset($lookupArray[$i]);
- }
- }
-
- // **
- // find the match
- // **
-
- if ($matchType === 0 || $matchType === 1) {
- foreach ($lookupArray as $i => $lookupArrayValue) {
- $typeMatch = ((gettype($lookupValue) === gettype($lookupArrayValue)) || (is_numeric($lookupValue) && is_numeric($lookupArrayValue)));
- $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
- $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
- $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
-
- if ($matchType === 0) {
- if ($typeMatch && is_string($lookupValue) && (bool) preg_match('/([\?\*])/', $lookupValue)) {
- $splitString = $lookupValue;
- $chars = array_map(function ($i) use ($splitString) {
- return mb_substr($splitString, $i, 1);
- }, range(0, mb_strlen($splitString) - 1));
-
- $length = count($chars);
- $pattern = '/^';
- for ($j = 0; $j < $length; ++$j) {
- if ($chars[$j] === '~') {
- if (isset($chars[$j + 1])) {
- if ($chars[$j + 1] === '*') {
- $pattern .= preg_quote($chars[$j + 1], '/');
- ++$j;
- } elseif ($chars[$j + 1] === '?') {
- $pattern .= preg_quote($chars[$j + 1], '/');
- ++$j;
- }
- } else {
- $pattern .= preg_quote($chars[$j], '/');
- }
- } elseif ($chars[$j] === '*') {
- $pattern .= '.*';
- } elseif ($chars[$j] === '?') {
- $pattern .= '.{1}';
- } else {
- $pattern .= preg_quote($chars[$j], '/');
- }
- }
-
- $pattern .= '$/';
- if ((bool) preg_match($pattern, $lookupArrayValue)) {
- // exact match
- return $i + 1;
- }
- } elseif ($exactMatch) {
- // exact match
- return $i + 1;
- }
- } elseif (($matchType === 1) && $typeMatch && ($lookupArrayValue <= $lookupValue)) {
- $i = array_search($i, $keySet);
-
- // The current value is the (first) match
- return $i + 1;
- }
- }
- } else {
- $maxValueKey = null;
-
- // 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);
- $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
- $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
- $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
-
- if ($exactMatch) {
- // Another "special" case. If a perfect match is found,
- // the algorithm gives up immediately
- return $i + 1;
- } elseif ($typeMatch & $lookupArrayValue >= $lookupValue) {
- $maxValueKey = $i + 1;
- } elseif ($typeMatch & $lookupArrayValue < $lookupValue) {
- //Excel algorithm gives up immediately if the first element is smaller than the searched value
- break;
- }
- }
-
- if ($maxValueKey !== null) {
- return $maxValueKey;
- }
- }
-
- // Unsuccessful in finding a match, return #N/A error value
- return Functions::NA();
+ return LookupRef\ExcelMatch::MATCH($lookupValue, $lookupArray, $matchType);
}
/**
@@ -611,262 +292,99 @@ class LookupRef
* Excel Function:
* =INDEX(range_array, row_num, [column_num])
*
- * @param mixed $arrayValues A range of cells or an array constant
- * @param mixed $rowNum The row in array from which to return a value. If row_num is omitted, column_num is required.
- * @param mixed $columnNum The column in array from which to return a value. If column_num is omitted, row_num is required.
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\Matrix::index()
+ * Use the index() method in the LookupRef\Matrix class instead
+ *
+ * @param mixed $rowNum The row in the array or range from which to return a value.
+ * If row_num is omitted, column_num is required.
+ * @param mixed $columnNum The column in the array or range from which to return a value.
+ * If column_num is omitted, row_num is required.
+ * @param mixed $matrix
*
* @return mixed the value of a specified cell or array of cells
*/
- public static function INDEX($arrayValues, $rowNum = 0, $columnNum = 0)
+ public static function INDEX($matrix, $rowNum = 0, $columnNum = 0)
{
- $rowNum = Functions::flattenSingleValue($rowNum);
- $columnNum = Functions::flattenSingleValue($columnNum);
-
- if (($rowNum < 0) || ($columnNum < 0)) {
- return Functions::VALUE();
- }
-
- if (!is_array($arrayValues) || ($rowNum > count($arrayValues))) {
- return Functions::REF();
- }
-
- $rowKeys = array_keys($arrayValues);
- $columnKeys = @array_keys($arrayValues[$rowKeys[0]]);
-
- if ($columnNum > count($columnKeys)) {
- return Functions::VALUE();
- } elseif ($columnNum == 0) {
- if ($rowNum == 0) {
- return $arrayValues;
- }
- $rowNum = $rowKeys[--$rowNum];
- $returnArray = [];
- foreach ($arrayValues as $arrayColumn) {
- if (is_array($arrayColumn)) {
- if (isset($arrayColumn[$rowNum])) {
- $returnArray[] = $arrayColumn[$rowNum];
- } else {
- return [$rowNum => $arrayValues[$rowNum]];
- }
- } else {
- return $arrayValues[$rowNum];
- }
- }
-
- return $returnArray;
- }
- $columnNum = $columnKeys[--$columnNum];
- if ($rowNum > count($rowKeys)) {
- return Functions::VALUE();
- } elseif ($rowNum == 0) {
- return $arrayValues[$columnNum];
- }
- $rowNum = $rowKeys[--$rowNum];
-
- return $arrayValues[$rowNum][$columnNum];
+ return Matrix::index($matrix, $rowNum, $columnNum);
}
/**
* TRANSPOSE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\Matrix::transpose()
+ * Use the transpose() method in the LookupRef\Matrix class instead
+ *
* @param array $matrixData A matrix of values
*
* @return array
*
- * Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix
+ * Unlike the Excel TRANSPOSE function, which will only work on a single row or column,
+ * this function will transpose a full matrix
*/
public static function TRANSPOSE($matrixData)
{
- $returnMatrix = [];
- if (!is_array($matrixData)) {
- $matrixData = [[$matrixData]];
- }
-
- $column = 0;
- foreach ($matrixData as $matrixRow) {
- $row = 0;
- foreach ($matrixRow as $matrixCell) {
- $returnMatrix[$row][$column] = $matrixCell;
- ++$row;
- }
- ++$column;
- }
-
- return $returnMatrix;
- }
-
- private static function vlookupSort($a, $b)
- {
- reset($a);
- $firstColumn = key($a);
- $aLower = StringHelper::strToLower($a[$firstColumn]);
- $bLower = StringHelper::strToLower($b[$firstColumn]);
- if ($aLower == $bLower) {
- return 0;
- }
-
- return ($aLower < $bLower) ? -1 : 1;
+ return Matrix::transpose($matrixData);
}
/**
* VLOOKUP
- * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value in the same row based on the index_number.
+ * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value
+ * in the same row based on the index_number.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\VLookup::lookup()
+ * Use the lookup() method in the LookupRef\VLookup class instead
*
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_array The range of cells being searched
- * @param mixed $index_number The column number in table_array from which the matching value must be returned. The first column is 1.
+ * @param mixed $index_number The column number in table_array from which the matching value must be returned.
+ * The first column is 1.
* @param mixed $not_exact_match determines if you are looking for an exact match based on lookup_value
*
* @return mixed The value of the found cell
*/
public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
{
- $lookup_value = Functions::flattenSingleValue($lookup_value);
- $index_number = Functions::flattenSingleValue($index_number);
- $not_exact_match = Functions::flattenSingleValue($not_exact_match);
-
- // index_number must be greater than or equal to 1
- if ($index_number < 1) {
- return Functions::VALUE();
- }
-
- // index_number must be less than or equal to the number of columns in lookup_array
- if ((!is_array($lookup_array)) || (empty($lookup_array))) {
- return Functions::REF();
- }
- $f = array_keys($lookup_array);
- $firstRow = array_pop($f);
- if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
- return Functions::REF();
- }
- $columnKeys = array_keys($lookup_array[$firstRow]);
- $returnColumn = $columnKeys[--$index_number];
- $firstColumn = array_shift($columnKeys);
-
- if (!$not_exact_match) {
- uasort($lookup_array, ['self', 'vlookupSort']);
- }
-
- $lookupLower = StringHelper::strToLower($lookup_value);
- $rowNumber = $rowValue = false;
- foreach ($lookup_array as $rowKey => $rowData) {
- $firstLower = StringHelper::strToLower($rowData[$firstColumn]);
-
- // break if we have passed possible keys
- if (
- (is_numeric($lookup_value) && is_numeric($rowData[$firstColumn]) && ($rowData[$firstColumn] > $lookup_value)) ||
- (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && ($firstLower > $lookupLower))
- ) {
- break;
- }
- // remember the last key, but only if datatypes match
- if (
- (is_numeric($lookup_value) && is_numeric($rowData[$firstColumn])) ||
- (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]))
- ) {
- if ($not_exact_match) {
- $rowNumber = $rowKey;
-
- continue;
- } elseif (
- ($firstLower == $lookupLower)
- // Spreadsheets software returns first exact match,
- // we have sorted and we might have broken key orders
- // we want the first one (by its initial index)
- && (($rowNumber == false) || ($rowKey < $rowNumber))
- ) {
- $rowNumber = $rowKey;
- }
- }
- }
-
- if ($rowNumber !== false) {
- // return the appropriate value
- return $lookup_array[$rowNumber][$returnColumn];
- }
-
- return Functions::NA();
+ return VLookup::lookup($lookup_value, $lookup_array, $index_number, $not_exact_match);
}
/**
* HLOOKUP
- * The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value in the same column based on the index_number.
+ * The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value
+ * in the same column based on the index_number.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\HLookup::lookup()
+ * Use the lookup() method in the LookupRef\HLookup class instead
*
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_array The range of cells being searched
- * @param mixed $index_number The row number in table_array from which the matching value must be returned. The first row is 1.
+ * @param mixed $index_number The row number in table_array from which the matching value must be returned.
+ * The first row is 1.
* @param mixed $not_exact_match determines if you are looking for an exact match based on lookup_value
*
* @return mixed The value of the found cell
*/
public static function HLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
{
- $lookup_value = Functions::flattenSingleValue($lookup_value);
- $index_number = Functions::flattenSingleValue($index_number);
- $not_exact_match = Functions::flattenSingleValue($not_exact_match);
-
- // index_number must be greater than or equal to 1
- if ($index_number < 1) {
- return Functions::VALUE();
- }
-
- // index_number must be less than or equal to the number of columns in lookup_array
- if ((!is_array($lookup_array)) || (empty($lookup_array))) {
- return Functions::REF();
- }
- $f = array_keys($lookup_array);
- $firstRow = reset($f);
- if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array))) {
- return Functions::REF();
- }
-
- $firstkey = $f[0] - 1;
- $returnColumn = $firstkey + $index_number;
- $firstColumn = array_shift($f);
- $rowNumber = null;
- foreach ($lookup_array[$firstColumn] as $rowKey => $rowData) {
- // break if we have passed possible keys
- $bothNumeric = is_numeric($lookup_value) && is_numeric($rowData);
- $bothNotNumeric = !is_numeric($lookup_value) && !is_numeric($rowData);
- $lookupLower = StringHelper::strToLower($lookup_value);
- $rowDataLower = StringHelper::strToLower($rowData);
-
- if (
- $not_exact_match && (
- ($bothNumeric && $rowData > $lookup_value) ||
- ($bothNotNumeric && $rowDataLower > $lookupLower)
- )
- ) {
- break;
- }
-
- // Remember the last key, but only if datatypes match (as in VLOOKUP)
- if ($bothNumeric || $bothNotNumeric) {
- if ($not_exact_match) {
- $rowNumber = $rowKey;
-
- continue;
- } elseif (
- $rowDataLower === $lookupLower
- && ($rowNumber === null || $rowKey < $rowNumber)
- ) {
- $rowNumber = $rowKey;
- }
- }
- }
-
- if ($rowNumber !== null) {
- // otherwise return the appropriate value
- return $lookup_array[$returnColumn][$rowNumber];
- }
-
- return Functions::NA();
+ return HLookup::lookup($lookup_value, $lookup_array, $index_number, $not_exact_match);
}
/**
* LOOKUP
* The LOOKUP function searches for value either from a one-row or one-column range or from an array.
*
+ * @Deprecated 1.18.0
+ *
+ * @see LookupRef\Lookup::lookup()
+ * Use the lookup() method in the LookupRef\Lookup class instead
+ *
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_vector The range of cells being searched
* @param null|mixed $result_vector The column from which the matching value must be returned
@@ -875,94 +393,24 @@ class LookupRef
*/
public static function LOOKUP($lookup_value, $lookup_vector, $result_vector = null)
{
- $lookup_value = Functions::flattenSingleValue($lookup_value);
-
- if (!is_array($lookup_vector)) {
- return Functions::NA();
- }
- $hasResultVector = isset($result_vector);
- $lookupRows = count($lookup_vector);
- $l = array_keys($lookup_vector);
- $l = array_shift($l);
- $lookupColumns = count($lookup_vector[$l]);
- // we correctly orient our results
- if (($lookupRows === 1 && $lookupColumns > 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
- $lookup_vector = self::TRANSPOSE($lookup_vector);
- $lookupRows = count($lookup_vector);
- $l = array_keys($lookup_vector);
- $lookupColumns = count($lookup_vector[array_shift($l)]);
- }
-
- if ($result_vector === null) {
- $result_vector = $lookup_vector;
- }
- $resultRows = count($result_vector);
- $l = array_keys($result_vector);
- $l = array_shift($l);
- $resultColumns = count($result_vector[$l]);
- // we correctly orient our results
- if ($resultRows === 1 && $resultColumns > 1) {
- $result_vector = self::TRANSPOSE($result_vector);
- $resultRows = count($result_vector);
- $r = array_keys($result_vector);
- $resultColumns = count($result_vector[array_shift($r)]);
- }
-
- if ($lookupRows === 2 && !$hasResultVector) {
- $result_vector = array_pop($lookup_vector);
- $lookup_vector = array_shift($lookup_vector);
- }
-
- if ($lookupColumns !== 2) {
- foreach ($lookup_vector as &$value) {
- if (is_array($value)) {
- $k = array_keys($value);
- $key1 = $key2 = array_shift($k);
- ++$key2;
- $dataValue1 = $value[$key1];
- } else {
- $key1 = 0;
- $key2 = 1;
- $dataValue1 = $value;
- }
- $dataValue2 = array_shift($result_vector);
- if (is_array($dataValue2)) {
- $dataValue2 = array_shift($dataValue2);
- }
- $value = [$key1 => $dataValue1, $key2 => $dataValue2];
- }
- unset($value);
- }
-
- return self::VLOOKUP($lookup_value, $lookup_vector, 2);
+ return Lookup::lookup($lookup_value, $lookup_vector, $result_vector);
}
/**
* FORMULATEXT.
*
+ * @Deprecated 1.18.0
+ *
* @param mixed $cellReference The cell to check
- * @param Cell $pCell The current cell (containing this formula)
+ * @param Cell $cell The current cell (containing this formula)
*
* @return string
+ *
+ *@see LookupRef\Formula::text()
+ * Use the text() method in the LookupRef\Formula class instead
*/
- public static function FORMULATEXT($cellReference = '', ?Cell $pCell = null)
+ public static function FORMULATEXT($cellReference = '', ?Cell $cell = null)
{
- if ($pCell === null) {
- return Functions::REF();
- }
-
- preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
-
- $cellReference = $matches[6] . $matches[7];
- $worksheetName = trim($matches[3], "'");
- $worksheet = (!empty($worksheetName))
- ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
- : $pCell->getWorksheet();
-
- if (!$worksheet->getCell($cellReference)->isFormula()) {
- return Functions::NA();
- }
-
- return $worksheet->getCell($cellReference)->getValue();
+ return LookupRef\Formula::text($cellReference, $cell);
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php
new file mode 100644
index 00000000000..58215f27481
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php
@@ -0,0 +1,98 @@
+ '') {
+ if (strpos($sheetName, ' ') !== false || strpos($sheetName, '[') !== false) {
+ $sheetName = "'{$sheetName}'";
+ }
+ $sheetName .= '!';
+ }
+
+ return $sheetName;
+ }
+
+ private static function formatAsA1(int $row, int $column, int $relativity, string $sheetName): string
+ {
+ $rowRelative = $columnRelative = '$';
+ if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $columnRelative = '';
+ }
+ if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $rowRelative = '';
+ }
+ $column = Coordinate::stringFromColumnIndex($column);
+
+ return "{$sheetName}{$columnRelative}{$column}{$rowRelative}{$row}";
+ }
+
+ private static function formatAsR1C1(int $row, int $column, int $relativity, string $sheetName): string
+ {
+ if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $column = "[{$column}]";
+ }
+ if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $row = "[{$row}]";
+ }
+
+ return "{$sheetName}R{$row}C{$column}";
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
new file mode 100644
index 00000000000..71358bf30a6
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
@@ -0,0 +1,198 @@
+getMessage();
+ }
+
+ // MATCH() is not case sensitive, so we convert lookup value to be lower cased if it's a string type.
+ if (is_string($lookupValue)) {
+ $lookupValue = StringHelper::strToLower($lookupValue);
+ }
+
+ $valueKey = null;
+ switch ($matchType) {
+ case self::MATCHTYPE_LARGEST_VALUE:
+ $valueKey = self::matchLargestValue($lookupArray, $lookupValue, $keySet);
+
+ break;
+ case self::MATCHTYPE_FIRST_VALUE:
+ $valueKey = self::matchFirstValue($lookupArray, $lookupValue);
+
+ break;
+ case self::MATCHTYPE_SMALLEST_VALUE:
+ default:
+ $valueKey = self::matchSmallestValue($lookupArray, $lookupValue);
+ }
+
+ if ($valueKey !== null) {
+ return ++$valueKey;
+ }
+
+ // Unsuccessful in finding a match, return #N/A error value
+ return Functions::NA();
+ }
+
+ private static function matchFirstValue($lookupArray, $lookupValue)
+ {
+ $wildcardLookup = ((bool) preg_match('/([\?\*])/', $lookupValue));
+ $wildcard = WildcardMatch::wildcard($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)
+ ) {
+ // wildcard match
+ return $i;
+ } elseif ($lookupArrayValue === $lookupValue) {
+ // exact match
+ return $i;
+ }
+ }
+
+ return null;
+ }
+
+ private static function matchLargestValue($lookupArray, $lookupValue, $keySet)
+ {
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ $typeMatch = ((gettype($lookupValue) === gettype($lookupArrayValue)) ||
+ (is_numeric($lookupValue) && is_numeric($lookupArrayValue)));
+
+ if ($typeMatch && ($lookupArrayValue <= $lookupValue)) {
+ return array_search($i, $keySet);
+ }
+ }
+
+ return null;
+ }
+
+ private static function matchSmallestValue($lookupArray, $lookupValue)
+ {
+ $valueKey = null;
+
+ // 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);
+
+ if ($lookupArrayValue === $lookupValue) {
+ // Another "special" case. If a perfect match is found,
+ // the algorithm gives up immediately
+ return $i;
+ } elseif ($typeMatch && $lookupArrayValue >= $lookupValue) {
+ $valueKey = $i;
+ } elseif ($typeMatch && $lookupArrayValue < $lookupValue) {
+ //Excel algorithm gives up immediately if the first element is smaller than the searched value
+ break;
+ }
+ }
+
+ return $valueKey;
+ }
+
+ private static function validateLookupValue($lookupValue): void
+ {
+ // Lookup_value type has to be number, text, or logical values
+ if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
+ throw new Exception(Functions::NA());
+ }
+ }
+
+ private static function validateMatchType($matchType): void
+ {
+ // 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(Functions::NA());
+ }
+ }
+
+ private static function validateLookupArray($lookupArray): void
+ {
+ // Lookup_array should not be empty
+ $lookupArraySize = count($lookupArray);
+ if ($lookupArraySize <= 0) {
+ throw new Exception(Functions::NA());
+ }
+ }
+
+ private static function prepareLookupArray($lookupArray, $matchType)
+ {
+ // Lookup_array should contain only number, text, or logical values, or empty (null) cells
+ foreach ($lookupArray as $i => $value) {
+ // check the type of the value
+ if ((!is_numeric($value)) && (!is_string($value)) && (!is_bool($value)) && ($value !== null)) {
+ throw new Exception(Functions::NA());
+ }
+ // Convert strings to lowercase for case-insensitive testing
+ if (is_string($value)) {
+ $lookupArray[$i] = StringHelper::strToLower($value);
+ }
+ if (
+ ($value === null) &&
+ (($matchType == self::MATCHTYPE_LARGEST_VALUE) || ($matchType == self::MATCHTYPE_SMALLEST_VALUE))
+ ) {
+ unset($lookupArray[$i]);
+ }
+ }
+
+ return $lookupArray;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
new file mode 100644
index 00000000000..f3e6c4199d2
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
@@ -0,0 +1,43 @@
+getWorksheet()->getParent()->getSheetByName($worksheetName)
+ : $cell->getWorksheet();
+
+ if (
+ $worksheet === null ||
+ !$worksheet->cellExists($cellReference) ||
+ !$worksheet->getCell($cellReference)->isFormula()
+ ) {
+ return Functions::NA();
+ }
+
+ return $worksheet->getCell($cellReference)->getValue();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
new file mode 100644
index 00000000000..7db27804408
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
@@ -0,0 +1,116 @@
+getMessage();
+ }
+
+ $f = array_keys($lookupArray);
+ $firstRow = reset($f);
+ if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray))) {
+ return Functions::REF();
+ }
+
+ $firstkey = $f[0] - 1;
+ $returnColumn = $firstkey + $indexNumber;
+ $firstColumn = array_shift($f);
+ $rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
+
+ if ($rowNumber !== null) {
+ // otherwise return the appropriate value
+ return $lookupArray[$returnColumn][Coordinate::stringFromColumnIndex($rowNumber)];
+ }
+
+ return Functions::NA();
+ }
+
+ /**
+ * @param mixed $lookupValue The value that you want to match in lookup_array
+ * @param mixed $column The column to look up
+ * @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
+ */
+ private static function hLookupSearch($lookupValue, array $lookupArray, $column, $notExactMatch): ?int
+ {
+ $lookupLower = StringHelper::strToLower($lookupValue);
+
+ $rowNumber = null;
+ foreach ($lookupArray[$column] as $rowKey => $rowData) {
+ // break if we have passed possible keys
+ $bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
+ $bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
+ $cellDataLower = StringHelper::strToLower($rowData);
+
+ if (
+ $notExactMatch &&
+ (($bothNumeric && $rowData > $lookupValue) || ($bothNotNumeric && $cellDataLower > $lookupLower))
+ ) {
+ break;
+ }
+
+ $rowNumber = self::checkMatch(
+ $bothNumeric,
+ $bothNotNumeric,
+ $notExactMatch,
+ Coordinate::columnIndexFromString($rowKey),
+ $cellDataLower,
+ $lookupLower,
+ $rowNumber
+ );
+ }
+
+ return $rowNumber;
+ }
+
+ private static function convertLiteralArray(array $lookupArray): array
+ {
+ if (array_key_exists(0, $lookupArray)) {
+ $lookupArray2 = [];
+ $row = 0;
+ foreach ($lookupArray as $arrayVal) {
+ ++$row;
+ if (!is_array($arrayVal)) {
+ $arrayVal = [$arrayVal];
+ }
+ $arrayVal2 = [];
+ foreach ($arrayVal as $key2 => $val2) {
+ $index = Coordinate::stringFromColumnIndex($key2 + 1);
+ $arrayVal2[$index] = $val2;
+ }
+ $lookupArray2[$row] = $arrayVal2;
+ }
+ $lookupArray = $lookupArray2;
+ }
+
+ return $lookupArray;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
new file mode 100644
index 00000000000..28e8df890cb
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
@@ -0,0 +1,74 @@
+getWorkSheet();
+ $sheetTitle = ($workSheet === null) ? '' : $workSheet->getTitle();
+ $value = preg_replace('/^=/', '', $namedRange->getValue());
+ self::adjustSheetTitle($sheetTitle, $value);
+ $cellAddress1 = $sheetTitle . $value;
+ $cellAddress = $cellAddress1;
+ $a1 = self::CELLADDRESS_USE_A1;
+ }
+ if (strpos($cellAddress, ':') !== false) {
+ [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
+ }
+ $cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1);
+
+ return [$cellAddress1, $cellAddress2, $cellAddress];
+ }
+
+ public static function extractWorksheet(string $cellAddress, Cell $cell): array
+ {
+ $sheetName = '';
+ if (strpos($cellAddress, '!') !== false) {
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ $sheetName = trim($sheetName, "'");
+ }
+
+ $worksheet = ($sheetName !== '')
+ ? $cell->getWorksheet()->getParent()->getSheetByName($sheetName)
+ : $cell->getWorksheet();
+
+ return [$cellAddress, $worksheet, $sheetName];
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
new file mode 100644
index 00000000000..1448c089a85
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
@@ -0,0 +1,40 @@
+getHyperlink()->setUrl($linkURL);
+ $cell->getHyperlink()->setTooltip($displayName);
+
+ return $displayName;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
new file mode 100644
index 00000000000..b5a35410887
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
@@ -0,0 +1,97 @@
+getMessage();
+ }
+
+ [$cellAddress, $worksheet, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+
+ [$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName);
+
+ if (
+ (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
+ (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches)))
+ ) {
+ return Functions::REF();
+ }
+
+ return self::extractRequiredCells($worksheet, $cellAddress);
+ }
+
+ /**
+ * Extract range values.
+ *
+ * @return mixed Array of values in range if range contains more than one element.
+ * Otherwise, a single value is returned.
+ */
+ private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress)
+ {
+ return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
+ ->extractCellRange($cellAddress, $worksheet, false);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
new file mode 100644
index 00000000000..e21d35dc56d
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
@@ -0,0 +1,105 @@
+ 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
+ $lookupVector = LookupRef\Matrix::transpose($lookupVector);
+ $lookupRows = self::rowCount($lookupVector);
+ $lookupColumns = self::columnCount($lookupVector);
+ }
+
+ $resultVector = self::verifyResultVector($lookupVector, $resultVector);
+
+ if ($lookupRows === 2 && !$hasResultVector) {
+ $resultVector = array_pop($lookupVector);
+ $lookupVector = array_shift($lookupVector);
+ }
+
+ if ($lookupColumns !== 2) {
+ $lookupVector = self::verifyLookupValues($lookupVector, $resultVector);
+ }
+
+ return VLookup::lookup($lookupValue, $lookupVector, 2);
+ }
+
+ private static function verifyLookupValues(array $lookupVector, array $resultVector): array
+ {
+ foreach ($lookupVector as &$value) {
+ if (is_array($value)) {
+ $k = array_keys($value);
+ $key1 = $key2 = array_shift($k);
+ ++$key2;
+ $dataValue1 = $value[$key1];
+ } else {
+ $key1 = 0;
+ $key2 = 1;
+ $dataValue1 = $value;
+ }
+
+ $dataValue2 = array_shift($resultVector);
+ if (is_array($dataValue2)) {
+ $dataValue2 = array_shift($dataValue2);
+ }
+ $value = [$key1 => $dataValue1, $key2 => $dataValue2];
+ }
+ unset($value);
+
+ return $lookupVector;
+ }
+
+ private static function verifyResultVector(array $lookupVector, $resultVector)
+ {
+ if ($resultVector === null) {
+ $resultVector = $lookupVector;
+ }
+
+ $resultRows = self::rowCount($resultVector);
+ $resultColumns = self::columnCount($resultVector);
+
+ // we correctly orient our results
+ if ($resultRows === 1 && $resultColumns > 1) {
+ $resultVector = LookupRef\Matrix::transpose($resultVector);
+ }
+
+ return $resultVector;
+ }
+
+ private static function rowCount(array $dataArray): int
+ {
+ return count($dataArray);
+ }
+
+ private static function columnCount(array $dataArray): int
+ {
+ $rowKeys = array_keys($dataArray);
+ $row = array_shift($rowKeys);
+
+ return count($dataArray[$row]);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
new file mode 100644
index 00000000000..80fc99ad37d
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
@@ -0,0 +1,48 @@
+getMessage();
+ }
+
+ if (!is_array($matrix) || ($rowNum > count($matrix))) {
+ return Functions::REF();
+ }
+
+ $rowKeys = array_keys($matrix);
+ $columnKeys = @array_keys($matrix[$rowKeys[0]]);
+
+ if ($columnNum > count($columnKeys)) {
+ return Functions::REF();
+ }
+
+ if ($columnNum === 0) {
+ return self::extractRowValue($matrix, $rowKeys, $rowNum);
+ }
+
+ $columnNum = $columnKeys[--$columnNum];
+ if ($rowNum === 0) {
+ return array_map(
+ function ($value) {
+ return [$value];
+ },
+ array_column($matrix, $columnNum)
+ );
+ }
+ $rowNum = $rowKeys[--$rowNum];
+
+ return $matrix[$rowNum][$columnNum];
+ }
+
+ private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum)
+ {
+ if ($rowNum === 0) {
+ return $matrix;
+ }
+
+ $rowNum = $rowKeys[--$rowNum];
+ $row = $matrix[$rowNum];
+ if (is_array($row)) {
+ return [$rowNum => $row];
+ }
+
+ return $row;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
new file mode 100644
index 00000000000..7e33f55acaf
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
@@ -0,0 +1,136 @@
+getParent() : null)
+ ->extractCellRange($cellAddress, $worksheet, false);
+ }
+
+ private static function extractWorksheet($cellAddress, Cell $cell): array
+ {
+ $sheetName = '';
+ if (strpos($cellAddress, '!') !== false) {
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ $sheetName = trim($sheetName, "'");
+ }
+
+ $worksheet = ($sheetName !== '')
+ ? $cell->getWorksheet()->getParent()->getSheetByName($sheetName)
+ : $cell->getWorksheet();
+
+ return [$cellAddress, $worksheet];
+ }
+
+ private static function adjustEndCellColumnForWidth(string $endCellColumn, $width, int $startCellColumn, $columns)
+ {
+ $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
+ if (($width !== null) && (!is_object($width))) {
+ $endCellColumn = $startCellColumn + (int) $width - 1;
+ } else {
+ $endCellColumn += (int) $columns;
+ }
+
+ return $endCellColumn;
+ }
+
+ private static function adustEndCellRowForHeight($height, int $startCellRow, $rows, $endCellRow): int
+ {
+ if (($height !== null) && (!is_object($height))) {
+ $endCellRow = $startCellRow + (int) $height - 1;
+ } else {
+ $endCellRow += (int) $rows;
+ }
+
+ return $endCellRow;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
new file mode 100644
index 00000000000..9752d6730aa
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
@@ -0,0 +1,209 @@
+getColumn()) : 1;
+ }
+
+ /**
+ * COLUMN.
+ *
+ * Returns the column number of the given cell reference
+ * If the cell reference is a range of cells, COLUMN returns the column numbers of each column
+ * in the reference as a horizontal array.
+ * If cell reference is omitted, and the function is being called through the calculation engine,
+ * then it is assumed to be the reference of the cell in which the COLUMN function appears;
+ * otherwise this function returns 1.
+ *
+ * Excel Function:
+ * =COLUMN([cellAddress])
+ *
+ * @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
+ *
+ * @return int|int[]
+ */
+ public static function COLUMN($cellAddress = null, ?Cell $cell = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return self::cellColumn($cell);
+ }
+
+ if (is_array($cellAddress)) {
+ foreach ($cellAddress as $columnKey => $value) {
+ $columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
+
+ return (int) Coordinate::columnIndexFromString($columnKey);
+ }
+
+ return self::cellColumn($cell);
+ }
+
+ $cellAddress = $cellAddress ?? '';
+ if ($cell != null) {
+ [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+ [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
+ }
+ [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ if (strpos($cellAddress, ':') !== false) {
+ [$startAddress, $endAddress] = explode(':', $cellAddress);
+ $startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
+ $endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
+
+ return range(
+ (int) Coordinate::columnIndexFromString($startAddress),
+ (int) Coordinate::columnIndexFromString($endAddress)
+ );
+ }
+
+ $cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
+
+ return (int) Coordinate::columnIndexFromString($cellAddress);
+ }
+
+ /**
+ * COLUMNS.
+ *
+ * Returns the number of columns in an array or reference.
+ *
+ * Excel Function:
+ * =COLUMNS(cellAddress)
+ *
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of columns
+ *
+ * @return int|string The number of columns in cellAddress, or a string if arguments are invalid
+ */
+ public static function COLUMNS($cellAddress = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return 1;
+ }
+ if (!is_array($cellAddress)) {
+ return Functions::VALUE();
+ }
+
+ reset($cellAddress);
+ $isMatrix = (is_numeric(key($cellAddress)));
+ [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $rows;
+ }
+
+ return $columns;
+ }
+
+ private static function cellRow(?Cell $cell): int
+ {
+ return ($cell !== null) ? $cell->getRow() : 1;
+ }
+
+ /**
+ * ROW.
+ *
+ * Returns the row number of the given cell reference
+ * If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference
+ * as a vertical array.
+ * If cell reference is omitted, and the function is being called through the calculation engine,
+ * then it is assumed to be the reference of the cell in which the ROW function appears;
+ * otherwise this function returns 1.
+ *
+ * Excel Function:
+ * =ROW([cellAddress])
+ *
+ * @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
+ *
+ * @return int|mixed[]|string
+ */
+ public static function ROW($cellAddress = null, ?Cell $cell = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return self::cellRow($cell);
+ }
+
+ if (is_array($cellAddress)) {
+ foreach ($cellAddress as $rowKey => $rowValue) {
+ foreach ($rowValue as $columnKey => $cellValue) {
+ return (int) preg_replace('/\D/', '', $rowKey);
+ }
+ }
+
+ return self::cellRow($cell);
+ }
+
+ $cellAddress = $cellAddress ?? '';
+ if ($cell !== null) {
+ [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+ [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
+ }
+ [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ if (strpos($cellAddress, ':') !== false) {
+ [$startAddress, $endAddress] = explode(':', $cellAddress);
+ $startAddress = preg_replace('/\D/', '', $startAddress);
+ $endAddress = preg_replace('/\D/', '', $endAddress);
+
+ return array_map(
+ function ($value) {
+ return [$value];
+ },
+ range($startAddress, $endAddress)
+ );
+ }
+ [$cellAddress] = explode(':', $cellAddress);
+
+ return (int) preg_replace('/\D/', '', $cellAddress);
+ }
+
+ /**
+ * ROWS.
+ *
+ * Returns the number of rows in an array or reference.
+ *
+ * Excel Function:
+ * =ROWS(cellAddress)
+ *
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of rows
+ *
+ * @return int|string The number of rows in cellAddress, or a string if arguments are invalid
+ */
+ public static function ROWS($cellAddress = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return 1;
+ }
+ if (!is_array($cellAddress)) {
+ return Functions::VALUE();
+ }
+
+ reset($cellAddress);
+ $isMatrix = (is_numeric(key($cellAddress)));
+ [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $columns;
+ }
+
+ return $rows;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
new file mode 100644
index 00000000000..6c18d73b83e
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
@@ -0,0 +1,46 @@
+ $entryCount)) {
+ return Functions::VALUE();
+ }
+
+ if (is_array($chooseArgs[$chosenEntry])) {
+ return Functions::flattenArray($chooseArgs[$chosenEntry]);
+ }
+
+ return $chooseArgs[$chosenEntry];
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
new file mode 100644
index 00000000000..ddd5d9ee362
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
@@ -0,0 +1,105 @@
+getMessage();
+ }
+
+ $f = array_keys($lookupArray);
+ $firstRow = array_pop($f);
+ if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray[$firstRow]))) {
+ return Functions::REF();
+ }
+ $columnKeys = array_keys($lookupArray[$firstRow]);
+ $returnColumn = $columnKeys[--$indexNumber];
+ $firstColumn = array_shift($columnKeys);
+
+ if (!$notExactMatch) {
+ uasort($lookupArray, ['self', 'vlookupSort']);
+ }
+
+ $rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
+
+ if ($rowNumber !== null) {
+ // return the appropriate value
+ return $lookupArray[$rowNumber][$returnColumn];
+ }
+
+ return Functions::NA();
+ }
+
+ private static function vlookupSort($a, $b)
+ {
+ reset($a);
+ $firstColumn = key($a);
+ $aLower = StringHelper::strToLower($a[$firstColumn]);
+ $bLower = StringHelper::strToLower($b[$firstColumn]);
+
+ if ($aLower == $bLower) {
+ return 0;
+ }
+
+ return ($aLower < $bLower) ? -1 : 1;
+ }
+
+ private static function vLookupSearch($lookupValue, $lookupArray, $column, $notExactMatch)
+ {
+ $lookupLower = StringHelper::strToLower($lookupValue);
+
+ $rowNumber = null;
+ foreach ($lookupArray as $rowKey => $rowData) {
+ $bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
+ $bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
+ $cellDataLower = StringHelper::strToLower($rowData[$column]);
+
+ // break if we have passed possible keys
+ if (
+ $notExactMatch &&
+ (($bothNumeric && ($rowData[$column] > $lookupValue)) ||
+ ($bothNotNumeric && ($cellDataLower > $lookupLower)))
+ ) {
+ break;
+ }
+
+ $rowNumber = self::checkMatch(
+ $bothNumeric,
+ $bothNotNumeric,
+ $notExactMatch,
+ $rowKey,
+ $cellDataLower,
+ $lookupLower,
+ $rowNumber
+ );
+ }
+
+ return $rowNumber;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
index 823f6ef2c8d..ec251f6d25e 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
@@ -2,43 +2,11 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use Exception;
-use Matrix\Exception as MatrixException;
-use Matrix\Matrix;
-
+/**
+ * @deprecated 1.18.0
+ */
class MathTrig
{
- //
- // Private method to return an array of the factors of the input value
- //
- private static function factors($value)
- {
- $startVal = floor(sqrt($value));
-
- $factorArray = [];
- for ($i = $startVal; $i > 1; --$i) {
- if (($value % $i) == 0) {
- $factorArray = array_merge($factorArray, self::factors($value / $i));
- $factorArray = array_merge($factorArray, self::factors($i));
- if ($i <= sqrt($value)) {
- break;
- }
- }
- }
- if (!empty($factorArray)) {
- rsort($factorArray);
-
- return $factorArray;
- }
-
- return [(int) $value];
- }
-
- private static function romanCut($num, $n)
- {
- return ($num - ($num % $n)) / $n;
- }
-
/**
* ARABIC.
*
@@ -47,75 +15,18 @@ class MathTrig
* Excel Function:
* ARABIC(text)
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Arabic::evaluate()
+ * Use the evaluate method in the MathTrig\Arabic class instead
+ *
* @param string $roman
*
* @return int|string the arabic numberal contrived from the roman numeral
*/
public static function ARABIC($roman)
{
- // An empty string should return 0
- $roman = substr(trim(strtoupper((string) Functions::flattenSingleValue($roman))), 0, 255);
- if ($roman === '') {
- return 0;
- }
-
- // Convert the roman numeral to an arabic number
- $negativeNumber = $roman[0] === '-';
- if ($negativeNumber) {
- $roman = substr($roman, 1);
- }
-
- try {
- $arabic = self::calculateArabic(str_split($roman));
- } catch (Exception $e) {
- return Functions::VALUE(); // Invalid character detected
- }
-
- if ($negativeNumber) {
- $arabic *= -1; // The number should be negative
- }
-
- return $arabic;
- }
-
- /**
- * Recursively calculate the arabic value of a roman numeral.
- *
- * @param int $sum
- * @param int $subtract
- *
- * @return int
- */
- protected static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
- {
- $lookup = [
- 'M' => 1000,
- 'D' => 500,
- 'C' => 100,
- 'L' => 50,
- 'X' => 10,
- 'V' => 5,
- 'I' => 1,
- ];
-
- $numeral = array_shift($roman);
- if (!isset($lookup[$numeral])) {
- throw new Exception('Invalid character detected');
- }
-
- $arabic = $lookup[$numeral];
- if (count($roman) > 0 && isset($lookup[$roman[0]]) && $arabic < $lookup[$roman[0]]) {
- $subtract += $arabic;
- } else {
- $sum += ($arabic - $subtract);
- $subtract = 0;
- }
-
- if (count($roman) > 0) {
- self::calculateArabic($roman, $sum, $subtract);
- }
-
- return $sum;
+ return MathTrig\Arabic::evaluate($roman);
}
/**
@@ -134,6 +45,11 @@ class MathTrig
* Excel Function:
* ATAN2(xCoordinate,yCoordinate)
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Tangent::atan2()
+ * Use the atan2 method in the MathTrig\Trig\Tangent class instead
+ *
* @param float $xCoordinate the x-coordinate of the point
* @param float $yCoordinate the y-coordinate of the point
*
@@ -141,27 +57,7 @@ class MathTrig
*/
public static function ATAN2($xCoordinate = null, $yCoordinate = null)
{
- $xCoordinate = Functions::flattenSingleValue($xCoordinate);
- $yCoordinate = Functions::flattenSingleValue($yCoordinate);
-
- $xCoordinate = ($xCoordinate !== null) ? $xCoordinate : 0.0;
- $yCoordinate = ($yCoordinate !== null) ? $yCoordinate : 0.0;
-
- if (
- ((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) &&
- ((is_numeric($yCoordinate))) || (is_bool($yCoordinate))
- ) {
- $xCoordinate = (float) $xCoordinate;
- $yCoordinate = (float) $yCoordinate;
-
- if (($xCoordinate == 0) && ($yCoordinate == 0)) {
- return Functions::DIV0();
- }
-
- return atan2($yCoordinate, $xCoordinate);
- }
-
- return Functions::VALUE();
+ return MathTrig\Trig\Tangent::atan2($xCoordinate, $yCoordinate);
}
/**
@@ -172,6 +68,11 @@ class MathTrig
* Excel Function:
* BASE(Number, Radix [Min_length])
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Base::evaluate()
+ * Use the evaluate method in the MathTrig\Base class instead
+ *
* @param float $number
* @param float $radix
* @param int $minLength
@@ -180,29 +81,7 @@ class MathTrig
*/
public static function BASE($number, $radix, $minLength = null)
{
- $number = Functions::flattenSingleValue($number);
- $radix = Functions::flattenSingleValue($radix);
- $minLength = Functions::flattenSingleValue($minLength);
-
- if (is_numeric($number) && is_numeric($radix) && ($minLength === null || is_numeric($minLength))) {
- // Truncate to an integer
- $number = (int) $number;
- $radix = (int) $radix;
- $minLength = (int) $minLength;
-
- if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
- return Functions::NAN(); // Numeric range constraints
- }
-
- $outcome = strtoupper((string) base_convert($number, 10, $radix));
- if ($minLength !== null) {
- $outcome = str_pad($outcome, $minLength, '0', STR_PAD_LEFT); // String padding
- }
-
- return $outcome;
- }
-
- return Functions::VALUE();
+ return MathTrig\Base::evaluate($number, $radix, $minLength);
}
/**
@@ -216,34 +95,19 @@ class MathTrig
* Excel Function:
* CEILING(number[,significance])
*
+ * @Deprecated 1.17.0
+ *
* @param float $number the number you want to round
* @param float $significance the multiple to which you want to round
*
* @return float|string Rounded Number, or a string containing an error
+ *
+ * @see MathTrig\Ceiling::ceiling()
+ * Use the ceiling() method in the MathTrig\Ceiling class instead
*/
public static function CEILING($number, $significance = null)
{
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
-
- if (
- ($significance === null) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)
- ) {
- $significance = $number / abs($number);
- }
-
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if (($number == 0.0) || ($significance == 0.0)) {
- return 0.0;
- } elseif (self::SIGN($number) == self::SIGN($significance)) {
- return ceil($number / $significance) * $significance;
- }
-
- return Functions::NAN();
- }
-
- return Functions::VALUE();
+ return MathTrig\Ceiling::ceiling($number, $significance);
}
/**
@@ -255,27 +119,19 @@ class MathTrig
* Excel Function:
* COMBIN(numObjs,numInSet)
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\Combinations::withoutRepetition()
+ * Use the withoutRepetition() method in the MathTrig\Combinations class instead
+ *
* @param int $numObjs Number of different objects
* @param int $numInSet Number of objects in each combination
*
- * @return int|string Number of combinations, or a string containing an error
+ * @return float|int|string Number of combinations, or a string containing an error
*/
public static function COMBIN($numObjs, $numInSet)
{
- $numObjs = Functions::flattenSingleValue($numObjs);
- $numInSet = Functions::flattenSingleValue($numInSet);
-
- if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
- if ($numObjs < $numInSet) {
- return Functions::NAN();
- } elseif ($numInSet < 0) {
- return Functions::NAN();
- }
-
- return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
- }
-
- return Functions::VALUE();
+ return MathTrig\Combinations::withoutRepetition($numObjs, $numInSet);
}
/**
@@ -290,27 +146,31 @@ class MathTrig
* Excel Function:
* EVEN(number)
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\Round::even()
+ * Use the even() method in the MathTrig\Round class instead
+ *
* @param float $number Number to round
*
- * @return int|string Rounded Number, or a string containing an error
+ * @return float|int|string Rounded Number, or a string containing an error
*/
public static function EVEN($number)
{
- $number = Functions::flattenSingleValue($number);
+ return MathTrig\Round::even($number);
+ }
- if ($number === null) {
- return 0;
- } elseif (is_bool($number)) {
- $number = (int) $number;
- }
-
- if (is_numeric($number)) {
- $significance = 2 * self::SIGN($number);
-
- return (int) self::CEILING($number, $significance);
- }
-
- return Functions::VALUE();
+ /**
+ * Helper function for Even.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\Helpers::getEven()
+ * Use the evaluate() method in the MathTrig\Helpers class instead
+ */
+ public static function getEven(float $number): int
+ {
+ return (int) MathTrig\Helpers::getEven($number);
}
/**
@@ -322,35 +182,18 @@ class MathTrig
* Excel Function:
* FACT(factVal)
*
+ * @Deprecated 1.18.0
+ *
* @param float $factVal Factorial Value
*
- * @return int|string Factorial, or a string containing an error
+ * @return float|int|string Factorial, or a string containing an error
+ *
+ *@see MathTrig\Factorial::fact()
+ * Use the fact() method in the MathTrig\Factorial class instead
*/
public static function FACT($factVal)
{
- $factVal = Functions::flattenSingleValue($factVal);
-
- if (is_numeric($factVal)) {
- if ($factVal < 0) {
- return Functions::NAN();
- }
- $factLoop = floor($factVal);
- if (
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) &&
- ($factVal > $factLoop)
- ) {
- return Functions::NAN();
- }
-
- $factorial = 1;
- while ($factLoop > 1) {
- $factorial *= $factLoop--;
- }
-
- return $factorial;
- }
-
- return Functions::VALUE();
+ return MathTrig\Factorial::fact($factVal);
}
/**
@@ -361,29 +204,18 @@ class MathTrig
* Excel Function:
* FACTDOUBLE(factVal)
*
+ * @Deprecated 1.18.0
+ *
* @param float $factVal Factorial Value
*
- * @return int|string Double Factorial, or a string containing an error
+ * @return float|int|string Double Factorial, or a string containing an error
+ *
+ *@see MathTrig\Factorial::factDouble()
+ * Use the factDouble() method in the MathTrig\Factorial class instead
*/
public static function FACTDOUBLE($factVal)
{
- $factLoop = Functions::flattenSingleValue($factVal);
-
- if (is_numeric($factLoop)) {
- $factLoop = floor($factLoop);
- if ($factVal < 0) {
- return Functions::NAN();
- }
- $factorial = 1;
- while ($factLoop > 1) {
- $factorial *= $factLoop--;
- --$factLoop;
- }
-
- return $factorial;
- }
-
- return Functions::VALUE();
+ return MathTrig\Factorial::factDouble($factVal);
}
/**
@@ -394,38 +226,19 @@ class MathTrig
* Excel Function:
* FLOOR(number[,significance])
*
+ * @Deprecated 1.17.0
+ *
* @param float $number Number to round
* @param float $significance Significance
*
* @return float|string Rounded Number, or a string containing an error
+ *
+ *@see MathTrig\Floor::floor()
+ * Use the floor() method in the MathTrig\Floor class instead
*/
public static function FLOOR($number, $significance = null)
{
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
-
- if (
- ($significance === null) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)
- ) {
- $significance = $number / abs($number);
- }
-
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if ($significance == 0.0) {
- return Functions::DIV0();
- } elseif ($number == 0.0) {
- return 0.0;
- } elseif (self::SIGN($significance) == 1) {
- return floor($number / $significance) * $significance;
- } elseif (self::SIGN($number) == -1 && self::SIGN($significance) == -1) {
- return floor($number / $significance) * $significance;
- }
-
- return Functions::NAN();
- }
-
- return Functions::VALUE();
+ return MathTrig\Floor::floor($number, $significance);
}
/**
@@ -436,35 +249,20 @@ class MathTrig
* Excel Function:
* FLOOR.MATH(number[,significance[,mode]])
*
+ * @Deprecated 1.17.0
+ *
* @param float $number Number to round
* @param float $significance Significance
* @param int $mode direction to round negative numbers
*
* @return float|string Rounded Number, or a string containing an error
+ *
+ *@see MathTrig\Floor::math()
+ * Use the math() method in the MathTrig\Floor class instead
*/
public static function FLOORMATH($number, $significance = null, $mode = 0)
{
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
- $mode = Functions::flattenSingleValue($mode);
-
- if (is_numeric($number) && $significance === null) {
- $significance = $number / abs($number);
- }
-
- if (is_numeric($number) && is_numeric($significance) && is_numeric($mode)) {
- if ($significance == 0.0) {
- return Functions::DIV0();
- } elseif ($number == 0.0) {
- return 0.0;
- } elseif (self::SIGN($significance) == -1 || (self::SIGN($number) == -1 && !empty($mode))) {
- return ceil($number / $significance) * $significance;
- }
-
- return floor($number / $significance) * $significance;
- }
-
- return Functions::VALUE();
+ return MathTrig\Floor::math($number, $significance, $mode);
}
/**
@@ -475,32 +273,41 @@ class MathTrig
* Excel Function:
* FLOOR.PRECISE(number[,significance])
*
+ * @Deprecated 1.17.0
+ *
* @param float $number Number to round
* @param float $significance Significance
*
* @return float|string Rounded Number, or a string containing an error
+ *
+ *@see MathTrig\Floor::precise()
+ * Use the precise() method in the MathTrig\Floor class instead
*/
public static function FLOORPRECISE($number, $significance = 1)
{
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
-
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if ($significance == 0.0) {
- return Functions::DIV0();
- } elseif ($number == 0.0) {
- return 0.0;
- }
-
- return floor($number / abs($significance)) * abs($significance);
- }
-
- return Functions::VALUE();
+ return MathTrig\Floor::precise($number, $significance);
}
- private static function evaluateGCD($a, $b)
+ /**
+ * INT.
+ *
+ * Casts a floating point value to an integer
+ *
+ * Excel Function:
+ * INT(number)
+ *
+ * @Deprecated 1.17.0
+ *
+ * @see MathTrig\IntClass::evaluate()
+ * Use the evaluate() method in the MathTrig\IntClass class instead
+ *
+ * @param float $number Number to cast to an integer
+ *
+ * @return int|string Integer value, or a string containing an error
+ */
+ public static function INT($number)
{
- return $b ? self::evaluateGCD($b, $a % $b) : $a;
+ return MathTrig\IntClass::evaluate($number);
}
/**
@@ -513,56 +320,18 @@ class MathTrig
* Excel Function:
* GCD(number1[,number2[, ...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\Gcd::evaluate()
+ * Use the evaluate() method in the MathTrig\Gcd class instead
+ *
* @param mixed ...$args Data values
*
* @return int|mixed|string Greatest Common Divisor, or a string containing an error
*/
public static function GCD(...$args)
{
- $args = Functions::flattenArray($args);
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $value) {
- if (!is_numeric($value)) {
- return Functions::VALUE();
- } elseif ($value < 0) {
- return Functions::NAN();
- }
- }
-
- $gcd = (int) array_pop($args);
- do {
- $gcd = self::evaluateGCD($gcd, (int) array_pop($args));
- } while (!empty($args));
-
- return $gcd;
- }
-
- /**
- * INT.
- *
- * Casts a floating point value to an integer
- *
- * Excel Function:
- * INT(number)
- *
- * @param float $number Number to cast to an integer
- *
- * @return int|string Integer value, or a string containing an error
- */
- public static function INT($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if ($number === null) {
- return 0;
- } elseif (is_bool($number)) {
- return (int) $number;
- }
- if (is_numeric($number)) {
- return (int) floor($number);
- }
-
- return Functions::VALUE();
+ return MathTrig\Gcd::evaluate(...$args);
}
/**
@@ -576,45 +345,18 @@ class MathTrig
* Excel Function:
* LCM(number1[,number2[, ...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\Lcm::evaluate()
+ * Use the evaluate() method in the MathTrig\Lcm class instead
+ *
* @param mixed ...$args Data values
*
* @return int|string Lowest Common Multiplier, or a string containing an error
*/
public static function LCM(...$args)
{
- $returnValue = 1;
- $allPoweredFactors = [];
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $value) {
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
- if ($value == 0) {
- return 0;
- } elseif ($value < 0) {
- return Functions::NAN();
- }
- $myFactors = self::factors(floor($value));
- $myCountedFactors = array_count_values($myFactors);
- $myPoweredFactors = [];
- foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
- $myPoweredFactors[$myCountedFactor] = $myCountedFactor ** $myCountedPower;
- }
- foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
- if (isset($allPoweredFactors[$myPoweredValue])) {
- if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
- $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
- }
- } else {
- $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
- }
- }
- }
- foreach ($allPoweredFactors as $allPoweredFactor) {
- $returnValue *= (int) $allPoweredFactor;
- }
-
- return $returnValue;
+ return MathTrig\Lcm::evaluate(...$args);
}
/**
@@ -625,24 +367,19 @@ class MathTrig
* Excel Function:
* LOG(number[,base])
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\Logarithms::withBase()
+ * Use the withBase() method in the MathTrig\Logarithms class instead
+ *
* @param float $number The positive real number for which you want the logarithm
* @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
*
* @return float|string The result, or a string containing an error
*/
- public static function logBase($number = null, $base = 10)
+ public static function logBase($number, $base = 10)
{
- $number = Functions::flattenSingleValue($number);
- $base = ($base === null) ? 10 : (float) Functions::flattenSingleValue($base);
-
- if ((!is_numeric($base)) || (!is_numeric($number))) {
- return Functions::VALUE();
- }
- if (($base <= 0) || ($number <= 0)) {
- return Functions::NAN();
- }
-
- return log($number, $base);
+ return MathTrig\Logarithms::withBase($number, $base);
}
/**
@@ -653,46 +390,18 @@ class MathTrig
* Excel Function:
* MDETERM(array)
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\MatrixFunctions::determinant()
+ * Use the determinant() method in the MathTrig\MatrixFunctions class instead
+ *
* @param array $matrixValues A matrix of values
*
* @return float|string The result, or a string containing an error
*/
public static function MDETERM($matrixValues)
{
- $matrixData = [];
- if (!is_array($matrixValues)) {
- $matrixValues = [[$matrixValues]];
- }
-
- $row = $maxColumn = 0;
- foreach ($matrixValues as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $column = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((is_string($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixData[$row][$column] = $matrixCell;
- ++$column;
- }
- if ($column > $maxColumn) {
- $maxColumn = $column;
- }
- ++$row;
- }
-
- $matrix = new Matrix($matrixData);
- if (!$matrix->isSquare()) {
- return Functions::VALUE();
- }
-
- try {
- return $matrix->determinant();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
+ return MathTrig\MatrixFunctions::determinant($matrixValues);
}
/**
@@ -703,55 +412,28 @@ class MathTrig
* Excel Function:
* MINVERSE(array)
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\MatrixFunctions::inverse()
+ * Use the inverse() method in the MathTrig\MatrixFunctions class instead
+ *
* @param array $matrixValues A matrix of values
*
* @return array|string The result, or a string containing an error
*/
public static function MINVERSE($matrixValues)
{
- $matrixData = [];
- if (!is_array($matrixValues)) {
- $matrixValues = [[$matrixValues]];
- }
-
- $row = $maxColumn = 0;
- foreach ($matrixValues as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $column = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((is_string($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixData[$row][$column] = $matrixCell;
- ++$column;
- }
- if ($column > $maxColumn) {
- $maxColumn = $column;
- }
- ++$row;
- }
-
- $matrix = new Matrix($matrixData);
- if (!$matrix->isSquare()) {
- return Functions::VALUE();
- }
-
- if ($matrix->determinant() == 0.0) {
- return Functions::NAN();
- }
-
- try {
- return $matrix->inverse()->toArray();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
+ return MathTrig\MatrixFunctions::inverse($matrixValues);
}
/**
* MMULT.
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\MatrixFunctions::multiply()
+ * Use the multiply() method in the MathTrig\MatrixFunctions class instead
+ *
* @param array $matrixData1 A matrix of values
* @param array $matrixData2 A matrix of values
*
@@ -759,80 +441,25 @@ class MathTrig
*/
public static function MMULT($matrixData1, $matrixData2)
{
- $matrixAData = $matrixBData = [];
- if (!is_array($matrixData1)) {
- $matrixData1 = [[$matrixData1]];
- }
- if (!is_array($matrixData2)) {
- $matrixData2 = [[$matrixData2]];
- }
-
- try {
- $rowA = 0;
- foreach ($matrixData1 as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $columnA = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixAData[$rowA][$columnA] = $matrixCell;
- ++$columnA;
- }
- ++$rowA;
- }
- $matrixA = new Matrix($matrixAData);
- $rowB = 0;
- foreach ($matrixData2 as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $columnB = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixBData[$rowB][$columnB] = $matrixCell;
- ++$columnB;
- }
- ++$rowB;
- }
- $matrixB = new Matrix($matrixBData);
-
- if ($columnA != $rowB) {
- return Functions::VALUE();
- }
-
- return $matrixA->multiply($matrixB)->toArray();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
+ return MathTrig\MatrixFunctions::multiply($matrixData1, $matrixData2);
}
/**
* MOD.
*
+ * @Deprecated 1.18.0
+ *
+ * @see MathTrig\Operations::mod()
+ * Use the mod() method in the MathTrig\Operations class instead
+ *
* @param int $a Dividend
* @param int $b Divisor
*
- * @return int|string Remainder, or a string containing an error
+ * @return float|int|string Remainder, or a string containing an error
*/
public static function MOD($a = 1, $b = 1)
{
- $a = (float) Functions::flattenSingleValue($a);
- $b = (float) Functions::flattenSingleValue($b);
-
- if ($b == 0.0) {
- return Functions::DIV0();
- } elseif (($a < 0.0) && ($b > 0.0)) {
- return $b - fmod(abs($a), $b);
- } elseif (($a > 0.0) && ($b < 0.0)) {
- return $b + fmod($a, abs($b));
- }
-
- return fmod($a, $b);
+ return MathTrig\Operations::mod($a, $b);
}
/**
@@ -840,30 +467,19 @@ class MathTrig
*
* Rounds a number to the nearest multiple of a specified value
*
+ * @Deprecated 1.17.0
+ *
* @param float $number Number to round
* @param int $multiple Multiple to which you want to round $number
*
* @return float|string Rounded Number, or a string containing an error
+ *
+ *@see MathTrig\Round::multiple()
+ * Use the multiple() method in the MathTrig\Mround class instead
*/
public static function MROUND($number, $multiple)
{
- $number = Functions::flattenSingleValue($number);
- $multiple = Functions::flattenSingleValue($multiple);
-
- if ((is_numeric($number)) && (is_numeric($multiple))) {
- if ($multiple == 0) {
- return 0;
- }
- if ((self::SIGN($number)) == (self::SIGN($multiple))) {
- $multiplier = 1 / $multiple;
-
- return round($number * $multiplier) / $multiplier;
- }
-
- return Functions::NAN();
- }
-
- return Functions::VALUE();
+ return MathTrig\Round::multiple($number, $multiple);
}
/**
@@ -871,36 +487,18 @@ class MathTrig
*
* Returns the ratio of the factorial of a sum of values to the product of factorials.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Factorial::multinomial()
+ * Use the multinomial method in the MathTrig\Factorial class instead
+ *
* @param mixed[] $args An array of mixed values for the Data Series
*
* @return float|string The result, or a string containing an error
*/
public static function MULTINOMIAL(...$args)
{
- $summer = 0;
- $divisor = 1;
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if (is_numeric($arg)) {
- if ($arg < 1) {
- return Functions::NAN();
- }
- $summer += floor($arg);
- $divisor *= self::FACT($arg);
- } else {
- return Functions::VALUE();
- }
- }
-
- // Return
- if ($summer > 0) {
- $summer = self::FACT($summer);
-
- return $summer / $divisor;
- }
-
- return 0;
+ return MathTrig\Factorial::multinomial(...$args);
}
/**
@@ -908,33 +506,18 @@ class MathTrig
*
* Returns number rounded up to the nearest odd integer.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Round::odd()
+ * Use the odd method in the MathTrig\Round class instead
+ *
* @param float $number Number to round
*
- * @return int|string Rounded Number, or a string containing an error
+ * @return float|int|string Rounded Number, or a string containing an error
*/
public static function ODD($number)
{
- $number = Functions::flattenSingleValue($number);
-
- if ($number === null) {
- return 1;
- } elseif (is_bool($number)) {
- return 1;
- } elseif (is_numeric($number)) {
- $significance = self::SIGN($number);
- if ($significance == 0) {
- return 1;
- }
-
- $result = self::CEILING($number, $significance);
- if ($result == self::EVEN($result)) {
- $result += $significance;
- }
-
- return (int) $result;
- }
-
- return Functions::VALUE();
+ return MathTrig\Round::odd($number);
}
/**
@@ -942,27 +525,19 @@ class MathTrig
*
* Computes x raised to the power y.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Operations::power()
+ * Use the evaluate method in the MathTrig\Power class instead
+ *
* @param float $x
* @param float $y
*
- * @return float|string The result, or a string containing an error
+ * @return float|int|string The result, or a string containing an error
*/
public static function POWER($x = 0, $y = 2)
{
- $x = Functions::flattenSingleValue($x);
- $y = Functions::flattenSingleValue($y);
-
- // Validate parameters
- if ($x == 0.0 && $y == 0.0) {
- return Functions::NAN();
- } elseif ($x == 0.0 && $y < 0.0) {
- return Functions::DIV0();
- }
-
- // Return
- $result = $x ** $y;
-
- return (!is_nan($result) && !is_infinite($result)) ? $result : Functions::NAN();
+ return MathTrig\Operations::power($x, $y);
}
/**
@@ -970,36 +545,21 @@ class MathTrig
*
* PRODUCT returns the product of all the values and cells referenced in the argument list.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Operations::product()
+ * Use the product method in the MathTrig\Operations class instead
+ *
* Excel Function:
* PRODUCT(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function PRODUCT(...$args)
{
- // Return value
- $returnValue = null;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = $arg;
- } else {
- $returnValue *= $arg;
- }
- }
- }
-
- // Return
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return MathTrig\Operations::product(...$args);
}
/**
@@ -1008,88 +568,60 @@ class MathTrig
* QUOTIENT function returns the integer portion of a division. Numerator is the divided number
* and denominator is the divisor.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Operations::quotient()
+ * Use the quotient method in the MathTrig\Operations class instead
+ *
* Excel Function:
* QUOTIENT(value1[,value2[, ...]])
*
- * @param mixed ...$args Data values
+ * @param mixed $numerator
+ * @param mixed $denominator
*
- * @return float
+ * @return int|string
*/
- public static function QUOTIENT(...$args)
+ public static function QUOTIENT($numerator, $denominator)
{
- // Return value
- $returnValue = null;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = ($arg == 0) ? 0 : $arg;
- } else {
- if (($returnValue == 0) || ($arg == 0)) {
- $returnValue = 0;
- } else {
- $returnValue /= $arg;
- }
- }
- }
- }
-
- // Return
- return (int) $returnValue;
+ return MathTrig\Operations::quotient($numerator, $denominator);
}
/**
- * RAND.
+ * RAND/RANDBETWEEN.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Random::randBetween()
+ * Use the randBetween or randBetween method in the MathTrig\Random class instead
*
* @param int $min Minimal value
* @param int $max Maximal value
*
- * @return int Random number
+ * @return float|int|string Random number
*/
public static function RAND($min = 0, $max = 0)
{
- $min = Functions::flattenSingleValue($min);
- $max = Functions::flattenSingleValue($max);
-
- if ($min == 0 && $max == 0) {
- return (mt_rand(0, 10000000)) / 10000000;
- }
-
- return mt_rand($min, $max);
+ return MathTrig\Random::randBetween($min, $max);
}
+ /**
+ * ROMAN.
+ *
+ * Converts a number to Roman numeral
+ *
+ * @Deprecated 1.17.0
+ *
+ * @Ssee MathTrig\Roman::evaluate()
+ * Use the evaluate() method in the MathTrig\Roman class instead
+ *
+ * @param mixed $aValue Number to convert
+ * @param mixed $style Number indicating one of five possible forms
+ *
+ * @return string Roman numeral, or a string containing an error
+ */
public static function ROMAN($aValue, $style = 0)
{
- $aValue = Functions::flattenSingleValue($aValue);
- $style = ($style === null) ? 0 : (int) Functions::flattenSingleValue($style);
- if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
- return Functions::VALUE();
- }
- $aValue = (int) $aValue;
- if ($aValue == 0) {
- return '';
- }
-
- $mill = ['', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM'];
- $cent = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
- $tens = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
- $ones = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
-
- $roman = '';
- while ($aValue > 5999) {
- $roman .= 'M';
- $aValue -= 1000;
- }
- $m = self::romanCut($aValue, 1000);
- $aValue %= 1000;
- $c = self::romanCut($aValue, 100);
- $aValue %= 100;
- $t = self::romanCut($aValue, 10);
- $aValue %= 10;
-
- return $roman . $mill[$m] . $cent[$c] . $tens[$t] . $ones[$aValue];
+ return MathTrig\Roman::evaluate($aValue, $style);
}
/**
@@ -1097,6 +629,11 @@ class MathTrig
*
* Rounds a number up to a specified number of decimal places
*
+ * @Deprecated 1.17.0
+ *
+ * @See MathTrig\Round::up()
+ * Use the up() method in the MathTrig\Round class instead
+ *
* @param float $number Number to round
* @param int $digits Number of digits to which you want to round $number
*
@@ -1104,22 +641,7 @@ class MathTrig
*/
public static function ROUNDUP($number, $digits)
{
- $number = Functions::flattenSingleValue($number);
- $digits = Functions::flattenSingleValue($digits);
-
- if ((is_numeric($number)) && (is_numeric($digits))) {
- if ($number == 0.0) {
- return 0.0;
- }
-
- if ($number < 0.0) {
- return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
- }
-
- return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
- }
-
- return Functions::VALUE();
+ return MathTrig\Round::up($number, $digits);
}
/**
@@ -1127,6 +649,11 @@ class MathTrig
*
* Rounds a number down to a specified number of decimal places
*
+ * @Deprecated 1.17.0
+ *
+ * @See MathTrig\Round::down()
+ * Use the down() method in the MathTrig\Round class instead
+ *
* @param float $number Number to round
* @param int $digits Number of digits to which you want to round $number
*
@@ -1134,22 +661,7 @@ class MathTrig
*/
public static function ROUNDDOWN($number, $digits)
{
- $number = Functions::flattenSingleValue($number);
- $digits = Functions::flattenSingleValue($digits);
-
- if ((is_numeric($number)) && (is_numeric($digits))) {
- if ($number == 0.0) {
- return 0.0;
- }
-
- if ($number < 0.0) {
- return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
- }
-
- return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
- }
-
- return Functions::VALUE();
+ return MathTrig\Round::down($number, $digits);
}
/**
@@ -1157,37 +669,21 @@ class MathTrig
*
* Returns the sum of a power series
*
- * @param mixed[] $args An array of mixed values for the Data Series
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\SeriesSum::evaluate()
+ * Use the evaluate method in the MathTrig\SeriesSum class instead
+ *
+ * @param mixed $x Input value
+ * @param mixed $n Initial power
+ * @param mixed $m Step
+ * @param mixed[] $args An array of coefficients for the Data Series
*
* @return float|string The result, or a string containing an error
*/
- public static function SERIESSUM(...$args)
+ public static function SERIESSUM($x, $n, $m, ...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
-
- $x = array_shift($aArgs);
- $n = array_shift($aArgs);
- $m = array_shift($aArgs);
-
- if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
- // Calculate
- $i = 0;
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += $arg * $x ** ($n + ($m * $i++));
- } else {
- return Functions::VALUE();
- }
- }
-
- return $returnValue;
- }
-
- return Functions::VALUE();
+ return MathTrig\SeriesSum::evaluate($x, $n, $m, ...$args);
}
/**
@@ -1196,26 +692,31 @@ class MathTrig
* Determines the sign of a number. Returns 1 if the number is positive, zero (0)
* if the number is 0, and -1 if the number is negative.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Sign::evaluate()
+ * Use the evaluate method in the MathTrig\Sign class instead
+ *
* @param float $number Number to round
*
* @return int|string sign value, or a string containing an error
*/
public static function SIGN($number)
{
- $number = Functions::flattenSingleValue($number);
+ return MathTrig\Sign::evaluate($number);
+ }
- if (is_bool($number)) {
- return (int) $number;
- }
- if (is_numeric($number)) {
- if ($number == 0.0) {
- return 0;
- }
-
- return $number / abs($number);
- }
-
- return Functions::VALUE();
+ /**
+ * returnSign = returns 0/-1/+1.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Helpers::returnSign()
+ * Use the returnSign method in the MathTrig\Helpers class instead
+ */
+ public static function returnSign(float $number): int
+ {
+ return MathTrig\Helpers::returnSign($number);
}
/**
@@ -1223,57 +724,18 @@ class MathTrig
*
* Returns the square root of (number * pi).
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Sqrt::sqrt()
+ * Use the pi method in the MathTrig\Sqrt class instead
+ *
* @param float $number Number
*
* @return float|string Square Root of Number * Pi, or a string containing an error
*/
public static function SQRTPI($number)
{
- $number = Functions::flattenSingleValue($number);
-
- if (is_numeric($number)) {
- if ($number < 0) {
- return Functions::NAN();
- }
-
- return sqrt($number * M_PI);
- }
-
- return Functions::VALUE();
- }
-
- protected static function filterHiddenArgs($cellReference, $args)
- {
- return array_filter(
- $args,
- function ($index) use ($cellReference) {
- [, $row, $column] = explode('.', $index);
-
- return $cellReference->getWorksheet()->getRowDimension($row)->getVisible() &&
- $cellReference->getWorksheet()->getColumnDimension($column)->getVisible();
- },
- ARRAY_FILTER_USE_KEY
- );
- }
-
- protected static function filterFormulaArgs($cellReference, $args)
- {
- return array_filter(
- $args,
- function ($index) use ($cellReference) {
- [, $row, $column] = explode('.', $index);
- if ($cellReference->getWorksheet()->cellExists($column . $row)) {
- //take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
- $isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
- $cellFormula = !preg_match('/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i', $cellReference->getWorksheet()->getCell($column . $row)->getValue());
-
- return !$isFormula || $cellFormula;
- }
-
- return true;
- },
- ARRAY_FILTER_USE_KEY
- );
+ return MathTrig\Sqrt::pi($number);
}
/**
@@ -1281,6 +743,11 @@ class MathTrig
*
* Returns a subtotal in a list or database.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Subtotal::evaluate()
+ * Use the evaluate method in the MathTrig\Subtotal class instead
+ *
* @param int $functionType
* A number 1 to 11 that specifies which function to
* use in calculating subtotals within a range
@@ -1294,45 +761,7 @@ class MathTrig
*/
public static function SUBTOTAL($functionType, ...$args)
{
- $cellReference = array_pop($args);
- $aArgs = Functions::flattenArrayIndexed($args);
- $subtotal = Functions::flattenSingleValue($functionType);
-
- // Calculate
- if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
- if ($subtotal > 100) {
- $aArgs = self::filterHiddenArgs($cellReference, $aArgs);
- $subtotal -= 100;
- }
-
- $aArgs = self::filterFormulaArgs($cellReference, $aArgs);
- switch ($subtotal) {
- case 1:
- return Statistical::AVERAGE($aArgs);
- case 2:
- return Statistical::COUNT($aArgs);
- case 3:
- return Statistical::COUNTA($aArgs);
- case 4:
- return Statistical::MAX($aArgs);
- case 5:
- return Statistical::MIN($aArgs);
- case 6:
- return self::PRODUCT($aArgs);
- case 7:
- return Statistical::STDEV($aArgs);
- case 8:
- return Statistical::STDEVP($aArgs);
- case 9:
- return self::SUM($aArgs);
- case 10:
- return Statistical::VARFunc($aArgs);
- case 11:
- return Statistical::VARP($aArgs);
- }
- }
-
- return Functions::VALUE();
+ return MathTrig\Subtotal::evaluate($functionType, ...$args);
}
/**
@@ -1340,131 +769,67 @@ class MathTrig
*
* SUM computes the sum of all the values and cells referenced in the argument list.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Sum::sumErroringStrings()
+ * Use the sumErroringStrings method in the MathTrig\Sum class instead
+ *
* Excel Function:
* SUM(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function SUM(...$args)
{
- $returnValue = 0;
-
- // Loop through the arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += $arg;
- } elseif (Functions::isError($arg)) {
- return $arg;
- }
- }
-
- return $returnValue;
+ return MathTrig\Sum::sumIgnoringStrings(...$args);
}
/**
* SUMIF.
*
- * Counts the number of cells that contain numbers within the list of arguments
+ * Totals the values of cells that contain numbers within the list of arguments
*
* Excel Function:
- * SUMIF(value1[,value2[, ...]],condition)
+ * SUMIF(range, criteria, [sum_range])
*
- * @param mixed $aArgs Data values
- * @param string $condition the criteria that defines which cells will be summed
- * @param mixed $sumArgs
+ * @Deprecated 1.17.0
*
- * @return float
+ * @see Statistical\Conditional::SUMIF()
+ * Use the SUMIF() method in the Statistical\Conditional class instead
+ *
+ * @param mixed $range Data values
+ * @param string $criteria the criteria that defines which cells will be summed
+ * @param mixed $sumRange
+ *
+ * @return float|string
*/
- public static function SUMIF($aArgs, $condition, $sumArgs = [])
+ public static function SUMIF($range, $criteria, $sumRange = [])
{
- $returnValue = 0;
-
- $aArgs = Functions::flattenArray($aArgs);
- $sumArgs = Functions::flattenArray($sumArgs);
- if (empty($sumArgs)) {
- $sumArgs = $aArgs;
- }
- $condition = Functions::ifCondition($condition);
- // Loop through arguments
- foreach ($aArgs as $key => $arg) {
- if (!is_numeric($arg)) {
- $arg = str_replace('"', '""', $arg);
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
-
- $testCondition = '=' . $arg . $condition;
- $sumValue = array_key_exists($key, $sumArgs) ? $sumArgs[$key] : 0;
-
- if (
- is_numeric($sumValue) &&
- Calculation::getInstance()->_calculateFormulaValue($testCondition)
- ) {
- // Is it a value within our criteria and only numeric can be added to the result
- $returnValue += $sumValue;
- }
- }
-
- return $returnValue;
+ return Statistical\Conditional::SUMIF($range, $criteria, $sumRange);
}
/**
* SUMIFS.
*
- * Counts the number of cells that contain numbers within the list of arguments
+ * Totals the values of cells that contain numbers within the list of arguments
*
* Excel Function:
- * SUMIFS(value1[,value2[, ...]],condition)
+ * SUMIFS(sum_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
+ *
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Conditional::SUMIFS()
+ * Use the SUMIFS() method in the Statistical\Conditional class instead
*
* @param mixed $args Data values
*
- * @return float
+ * @return null|float|string
*/
public static function SUMIFS(...$args)
{
- $arrayList = $args;
-
- // Return value
- $returnValue = 0;
-
- $sumArgs = Functions::flattenArray(array_shift($arrayList));
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each sum and see if arguments and conditions are true
- foreach ($sumArgs as $index => $value) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- $returnValue += $value;
- }
- }
-
- // Return
- return $returnValue;
+ return Statistical\Conditional::SUMIFS(...$args);
}
/**
@@ -1473,39 +838,18 @@ class MathTrig
* Excel Function:
* SUMPRODUCT(value1[,value2[, ...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Sum::product()
+ * Use the product method in the MathTrig\Sum class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function SUMPRODUCT(...$args)
{
- $arrayList = $args;
-
- $wrkArray = Functions::flattenArray(array_shift($arrayList));
- $wrkCellCount = count($wrkArray);
-
- for ($i = 0; $i < $wrkCellCount; ++$i) {
- if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
- $wrkArray[$i] = 0;
- }
- }
-
- foreach ($arrayList as $matrixData) {
- $array2 = Functions::flattenArray($matrixData);
- $count = count($array2);
- if ($wrkCellCount != $count) {
- return Functions::VALUE();
- }
-
- foreach ($array2 as $i => $val) {
- if ((!is_numeric($val)) || (is_string($val))) {
- $val = 0;
- }
- $wrkArray[$i] *= $val;
- }
- }
-
- return array_sum($wrkArray);
+ return MathTrig\Sum::product(...$args);
}
/**
@@ -1513,107 +857,75 @@ class MathTrig
*
* SUMSQ returns the sum of the squares of the arguments
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\SumSquares::sumSquare()
+ * Use the sumSquare method in the MathTrig\SumSquares class instead
+ *
* Excel Function:
* SUMSQ(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function SUMSQ(...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += ($arg * $arg);
- }
- }
-
- return $returnValue;
+ return MathTrig\SumSquares::sumSquare(...$args);
}
/**
* SUMX2MY2.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\SumSquares::sumXSquaredMinusYSquared()
+ * Use the sumXSquaredMinusYSquared method in the MathTrig\SumSquares class instead
+ *
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
- * @return float
+ * @return float|string
*/
public static function SUMX2MY2($matrixData1, $matrixData2)
{
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (
- ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))
- ) {
- $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
- }
- }
-
- return $result;
+ return MathTrig\SumSquares::sumXSquaredMinusYSquared($matrixData1, $matrixData2);
}
/**
* SUMX2PY2.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\SumSquares::sumXSquaredPlusYSquared()
+ * Use the sumXSquaredPlusYSquared method in the MathTrig\SumSquares class instead
+ *
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
- * @return float
+ * @return float|string
*/
public static function SUMX2PY2($matrixData1, $matrixData2)
{
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (
- ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))
- ) {
- $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
- }
- }
-
- return $result;
+ return MathTrig\SumSquares::sumXSquaredPlusYSquared($matrixData1, $matrixData2);
}
/**
* SUMXMY2.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\SumSquares::sumXMinusYSquared()
+ * Use the sumXMinusYSquared method in the MathTrig\SumSquares class instead
+ *
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
- * @return float
+ * @return float|string
*/
public static function SUMXMY2($matrixData1, $matrixData2)
{
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (
- ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))
- ) {
- $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
- }
- }
-
- return $result;
+ return MathTrig\SumSquares::sumXMinusYSquared($matrixData1, $matrixData2);
}
/**
@@ -1621,6 +933,11 @@ class MathTrig
*
* Truncates value to the number of fractional digits by number_digits.
*
+ * @Deprecated 1.17.0
+ *
+ * @see MathTrig\Trunc::evaluate()
+ * Use the evaluate() method in the MathTrig\Trunc class instead
+ *
* @param float $value
* @param int $digits
*
@@ -1628,23 +945,7 @@ class MathTrig
*/
public static function TRUNC($value = 0, $digits = 0)
{
- $value = Functions::flattenSingleValue($value);
- $digits = Functions::flattenSingleValue($digits);
-
- // Validate parameters
- if ((!is_numeric($value)) || (!is_numeric($digits))) {
- return Functions::VALUE();
- }
- $digits = floor($digits);
-
- // Truncate
- $adjust = 10 ** $digits;
-
- if (($digits > 0) && (rtrim((int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
- return $value;
- }
-
- return ((int) ($value * $adjust)) / $adjust;
+ return MathTrig\Trunc::evaluate($value, $digits);
}
/**
@@ -1652,21 +953,18 @@ class MathTrig
*
* Returns the secant of an angle.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Secant::sec()
+ * Use the sec method in the MathTrig\Trig\Secant class instead
+ *
* @param float $angle Number
*
* @return float|string The secant of the angle
*/
public static function SEC($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = cos($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Secant::sec($angle);
}
/**
@@ -1674,21 +972,18 @@ class MathTrig
*
* Returns the hyperbolic secant of an angle.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Secant::sech()
+ * Use the sech method in the MathTrig\Trig\Secant class instead
+ *
* @param float $angle Number
*
* @return float|string The hyperbolic secant of the angle
*/
public static function SECH($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = cosh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Secant::sech($angle);
}
/**
@@ -1696,21 +991,18 @@ class MathTrig
*
* Returns the cosecant of an angle.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cosecant::csc()
+ * Use the csc method in the MathTrig\Trig\Cosecant class instead
+ *
* @param float $angle Number
*
* @return float|string The cosecant of the angle
*/
public static function CSC($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = sin($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Cosecant::csc($angle);
}
/**
@@ -1718,21 +1010,18 @@ class MathTrig
*
* Returns the hyperbolic cosecant of an angle.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cosecant::csch()
+ * Use the csch method in the MathTrig\Trig\Cosecant class instead
+ *
* @param float $angle Number
*
* @return float|string The hyperbolic cosecant of the angle
*/
public static function CSCH($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = sinh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Cosecant::csch($angle);
}
/**
@@ -1740,21 +1029,18 @@ class MathTrig
*
* Returns the cotangent of an angle.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cotangent::cot()
+ * Use the cot method in the MathTrig\Trig\Cotangent class instead
+ *
* @param float $angle Number
*
* @return float|string The cotangent of the angle
*/
public static function COT($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = tan($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Cotangent::cot($angle);
}
/**
@@ -1762,21 +1048,18 @@ class MathTrig
*
* Returns the hyperbolic cotangent of an angle.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cotangent::coth()
+ * Use the coth method in the MathTrig\Trig\Cotangent class instead
+ *
* @param float $angle Number
*
* @return float|string The hyperbolic cotangent of the angle
*/
public static function COTH($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = tanh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Cotangent::coth($angle);
}
/**
@@ -1784,19 +1067,35 @@ class MathTrig
*
* Returns the arccotangent of a number.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cotangent::acot()
+ * Use the acot method in the MathTrig\Trig\Cotangent class instead
+ *
* @param float $number Number
*
* @return float|string The arccotangent of the number
*/
public static function ACOT($number)
{
- $number = Functions::flattenSingleValue($number);
+ return MathTrig\Trig\Cotangent::acot($number);
+ }
- if (!is_numeric($number)) {
- return Functions::VALUE();
- }
-
- return (M_PI / 2) - atan($number);
+ /**
+ * Return NAN or value depending on argument.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Helpers::numberOrNan()
+ * Use the numberOrNan method in the MathTrig\Helpers class instead
+ *
+ * @param float $result Number
+ *
+ * @return float|string
+ */
+ public static function numberOrNan($result)
+ {
+ return MathTrig\Helpers::numberOrNan($result);
}
/**
@@ -1804,20 +1103,418 @@ class MathTrig
*
* Returns the hyperbolic arccotangent of a number.
*
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cotangent::acoth()
+ * Use the acoth method in the MathTrig\Trig\Cotangent class instead
+ *
* @param float $number Number
*
* @return float|string The hyperbolic arccotangent of the number
*/
public static function ACOTH($number)
+ {
+ return MathTrig\Trig\Cotangent::acoth($number);
+ }
+
+ /**
+ * ROUND.
+ *
+ * Returns the result of builtin function round after validating args.
+ *
+ * @Deprecated 1.17.0
+ *
+ * @See MathTrig\Round::round()
+ * Use the round() method in the MathTrig\Round class instead
+ *
+ * @param mixed $number Should be numeric
+ * @param mixed $precision Should be int
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinROUND($number, $precision)
+ {
+ return MathTrig\Round::round($number, $precision);
+ }
+
+ /**
+ * ABS.
+ *
+ * Returns the result of builtin function abs after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Absolute::evaluate()
+ * Use the evaluate method in the MathTrig\Absolute class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|int|string Rounded number
+ */
+ public static function builtinABS($number)
+ {
+ return MathTrig\Absolute::evaluate($number);
+ }
+
+ /**
+ * ACOS.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cosine::acos()
+ * Use the acos method in the MathTrig\Trig\Cosine class instead
+ *
+ * Returns the result of builtin function acos after validating args.
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinACOS($number)
+ {
+ return MathTrig\Trig\Cosine::acos($number);
+ }
+
+ /**
+ * ACOSH.
+ *
+ * Returns the result of builtin function acosh after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cosine::acosh()
+ * Use the acosh method in the MathTrig\Trig\Cosine class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinACOSH($number)
+ {
+ return MathTrig\Trig\Cosine::acosh($number);
+ }
+
+ /**
+ * ASIN.
+ *
+ * Returns the result of builtin function asin after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Sine::asin()
+ * Use the asin method in the MathTrig\Trig\Sine class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinASIN($number)
+ {
+ return MathTrig\Trig\Sine::asin($number);
+ }
+
+ /**
+ * ASINH.
+ *
+ * Returns the result of builtin function asinh after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Sine::asinh()
+ * Use the asinh method in the MathTrig\Trig\Sine class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinASINH($number)
+ {
+ return MathTrig\Trig\Sine::asinh($number);
+ }
+
+ /**
+ * ATAN.
+ *
+ * Returns the result of builtin function atan after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Tangent::atan()
+ * Use the atan method in the MathTrig\Trig\Tangent class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinATAN($number)
+ {
+ return MathTrig\Trig\Tangent::atan($number);
+ }
+
+ /**
+ * ATANH.
+ *
+ * Returns the result of builtin function atanh after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Tangent::atanh()
+ * Use the atanh method in the MathTrig\Trig\Tangent class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinATANH($number)
+ {
+ return MathTrig\Trig\Tangent::atanh($number);
+ }
+
+ /**
+ * COS.
+ *
+ * Returns the result of builtin function cos after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cosine::cos()
+ * Use the cos method in the MathTrig\Trig\Cosine class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinCOS($number)
+ {
+ return MathTrig\Trig\Cosine::cos($number);
+ }
+
+ /**
+ * COSH.
+ *
+ * Returns the result of builtin function cos after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Cosine::cosh()
+ * Use the cosh method in the MathTrig\Trig\Cosine class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinCOSH($number)
+ {
+ return MathTrig\Trig\Cosine::cosh($number);
+ }
+
+ /**
+ * DEGREES.
+ *
+ * Returns the result of builtin function rad2deg after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Angle::toDegrees()
+ * Use the toDegrees method in the MathTrig\Angle class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinDEGREES($number)
+ {
+ return MathTrig\Angle::toDegrees($number);
+ }
+
+ /**
+ * EXP.
+ *
+ * Returns the result of builtin function exp after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Exp::evaluate()
+ * Use the evaluate method in the MathTrig\Exp class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinEXP($number)
+ {
+ return MathTrig\Exp::evaluate($number);
+ }
+
+ /**
+ * LN.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Logarithms::natural()
+ * Use the natural method in the MathTrig\Logarithms class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinLN($number)
+ {
+ return MathTrig\Logarithms::natural($number);
+ }
+
+ /**
+ * LOG10.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Logarithms::base10()
+ * Use the natural method in the MathTrig\Logarithms class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinLOG10($number)
+ {
+ return MathTrig\Logarithms::base10($number);
+ }
+
+ /**
+ * RADIANS.
+ *
+ * Returns the result of builtin function deg2rad after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Angle::toRadians()
+ * Use the toRadians method in the MathTrig\Angle class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinRADIANS($number)
+ {
+ return MathTrig\Angle::toRadians($number);
+ }
+
+ /**
+ * SIN.
+ *
+ * Returns the result of builtin function sin after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Sine::evaluate()
+ * Use the sin method in the MathTrig\Trig\Sine class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string sine
+ */
+ public static function builtinSIN($number)
+ {
+ return MathTrig\Trig\Sine::sin($number);
+ }
+
+ /**
+ * SINH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Sine::sinh()
+ * Use the sinh method in the MathTrig\Trig\Sine class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinSINH($number)
+ {
+ return MathTrig\Trig\Sine::sinh($number);
+ }
+
+ /**
+ * SQRT.
+ *
+ * Returns the result of builtin function sqrt after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Sqrt::sqrt()
+ * Use the sqrt method in the MathTrig\Sqrt class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinSQRT($number)
+ {
+ return MathTrig\Sqrt::sqrt($number);
+ }
+
+ /**
+ * TAN.
+ *
+ * Returns the result of builtin function tan after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Tangent::tan()
+ * Use the tan method in the MathTrig\Trig\Tangent class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinTAN($number)
+ {
+ return MathTrig\Trig\Tangent::tan($number);
+ }
+
+ /**
+ * TANH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Trig\Tangent::tanh()
+ * Use the tanh method in the MathTrig\Trig\Tangent class instead
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function builtinTANH($number)
+ {
+ return MathTrig\Trig\Tangent::tanh($number);
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @See MathTrig\Helpers::validateNumericNullBool()
+ * Use the validateNumericNullBool method in the MathTrig\Helpers class instead
+ *
+ * @param mixed $number
+ */
+ public static function nullFalseTrueToNumber(&$number): void
{
$number = Functions::flattenSingleValue($number);
-
- if (!is_numeric($number)) {
- return Functions::VALUE();
+ if ($number === null) {
+ $number = 0;
+ } elseif (is_bool($number)) {
+ $number = (int) $number;
}
-
- $result = log(($number + 1) / ($number - 1)) / 2;
-
- return is_nan($result) ? Functions::NAN() : $result;
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
new file mode 100644
index 00000000000..9f1bd804970
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
@@ -0,0 +1,28 @@
+getMessage();
+ }
+
+ return abs($number);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php
new file mode 100644
index 00000000000..3062481f0e3
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php
@@ -0,0 +1,48 @@
+getMessage();
+ }
+
+ return rad2deg($number);
+ }
+
+ /**
+ * RADIANS.
+ *
+ * Returns the result of builtin function deg2rad after validating args.
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function toRadians($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return deg2rad($number);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
new file mode 100644
index 00000000000..b852eeacf1d
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
@@ -0,0 +1,103 @@
+ 1000,
+ 'D' => 500,
+ 'C' => 100,
+ 'L' => 50,
+ 'X' => 10,
+ 'V' => 5,
+ 'I' => 1,
+ ];
+
+ /**
+ * Recursively calculate the arabic value of a roman numeral.
+ *
+ * @param int $sum
+ * @param int $subtract
+ *
+ * @return int
+ */
+ private static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
+ {
+ $numeral = array_shift($roman);
+ if (!isset(self::ROMAN_LOOKUP[$numeral])) {
+ throw new Exception('Invalid character detected');
+ }
+
+ $arabic = self::ROMAN_LOOKUP[$numeral];
+ if (count($roman) > 0 && isset(self::ROMAN_LOOKUP[$roman[0]]) && $arabic < self::ROMAN_LOOKUP[$roman[0]]) {
+ $subtract += $arabic;
+ } else {
+ $sum += ($arabic - $subtract);
+ $subtract = 0;
+ }
+
+ if (count($roman) > 0) {
+ self::calculateArabic($roman, $sum, $subtract);
+ }
+
+ return $sum;
+ }
+
+ /**
+ * @param mixed $value
+ */
+ private static function mollifyScrutinizer($value): array
+ {
+ return is_array($value) ? $value : [];
+ }
+
+ private static function strSplit(string $roman): array
+ {
+ $rslt = str_split($roman);
+
+ return self::mollifyScrutinizer($rslt);
+ }
+
+ /**
+ * ARABIC.
+ *
+ * Converts a Roman numeral to an Arabic numeral.
+ *
+ * Excel Function:
+ * ARABIC(text)
+ *
+ * @param string $roman
+ *
+ * @return int|string the arabic numberal contrived from the roman numeral
+ */
+ public static function evaluate($roman)
+ {
+ // An empty string should return 0
+ $roman = substr(trim(strtoupper((string) Functions::flattenSingleValue($roman))), 0, 255);
+ if ($roman === '') {
+ return 0;
+ }
+
+ // Convert the roman numeral to an arabic number
+ $negativeNumber = $roman[0] === '-';
+ if ($negativeNumber) {
+ $roman = substr($roman, 1);
+ }
+
+ try {
+ $arabic = self::calculateArabic(self::strSplit($roman));
+ } catch (Exception $e) {
+ return Functions::VALUE(); // Invalid character detected
+ }
+
+ if ($negativeNumber) {
+ $arabic *= -1; // The number should be negative
+ }
+
+ return $arabic;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php
new file mode 100644
index 00000000000..4be7b7c7986
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php
@@ -0,0 +1,49 @@
+getMessage();
+ }
+ $minLength = Functions::flattenSingleValue($minLength);
+
+ if ($minLength === null || is_numeric($minLength)) {
+ if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
+ return Functions::NAN(); // Numeric range constraints
+ }
+
+ $outcome = strtoupper((string) base_convert("$number", 10, $radix));
+ if ($minLength !== null) {
+ $outcome = str_pad($outcome, (int) $minLength, '0', STR_PAD_LEFT); // String padding
+ }
+
+ return $outcome;
+ }
+
+ return Functions::VALUE();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
new file mode 100644
index 00000000000..73f54a52f69
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
@@ -0,0 +1,138 @@
+getMessage();
+ }
+
+ return self::argumentsOk((float) $number, (float) $significance);
+ }
+
+ /**
+ * CEILING.MATH.
+ *
+ * Round a number down to the nearest integer or to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * CEILING.MATH(number[,significance[,mode]])
+ *
+ * @param mixed $number Number to round
+ * @param mixed $significance Significance
+ * @param int $mode direction to round negative numbers
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function math($number, $significance = null, $mode = 0)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
+ $mode = Helpers::validateNumericNullSubstitution($mode, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (empty($significance * $number)) {
+ return 0.0;
+ }
+ if (self::ceilingMathTest((float) $significance, (float) $number, (int) $mode)) {
+ return floor($number / $significance) * $significance;
+ }
+
+ return ceil($number / $significance) * $significance;
+ }
+
+ /**
+ * CEILING.PRECISE.
+ *
+ * Rounds number up, away from zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * CEILING.PRECISE(number[,significance])
+ *
+ * @param mixed $number the number you want to round
+ * @param float $significance the multiple to which you want to round
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function precise($number, $significance = 1)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (!$significance) {
+ return 0.0;
+ }
+ $result = $number / abs($significance);
+
+ return ceil($result) * $significance * (($significance < 0) ? -1 : 1);
+ }
+
+ /**
+ * Let CEILINGMATH complexity pass Scrutinizer.
+ */
+ private static function ceilingMathTest(float $significance, float $number, int $mode): bool
+ {
+ return ((float) $significance < 0) || ((float) $number < 0 && !empty($mode));
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ *
+ * @return float|string
+ */
+ private static function argumentsOk(float $number, float $significance)
+ {
+ if (empty($number * $significance)) {
+ return 0.0;
+ }
+ if (Helpers::returnSign($number) == Helpers::returnSign($significance)) {
+ return ceil($number / $significance) * $significance;
+ }
+
+ return Functions::NAN();
+ }
+
+ private static function floorCheck1Arg(): void
+ {
+ $compatibility = Functions::getCompatibilityMode();
+ if ($compatibility === Functions::COMPATIBILITY_EXCEL) {
+ throw new Exception('Excel requires 2 arguments for CEILING');
+ }
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
new file mode 100644
index 00000000000..97508bb1ef0
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
@@ -0,0 +1,74 @@
+getMessage();
+ }
+
+ return round(Factorial::fact($numObjs) / Factorial::fact($numObjs - $numInSet)) / Factorial::fact($numInSet);
+ }
+
+ /**
+ * COMBIN.
+ *
+ * Returns the number of combinations for a given number of items. Use COMBIN to
+ * determine the total possible number of groups for a given number of items.
+ *
+ * Excel Function:
+ * COMBIN(numObjs,numInSet)
+ *
+ * @param mixed $numObjs Number of different objects
+ * @param mixed $numInSet Number of objects in each combination
+ *
+ * @return float|int|string Number of combinations, or a string containing an error
+ */
+ public static function withRepetition($numObjs, $numInSet)
+ {
+ try {
+ $numObjs = Helpers::validateNumericNullSubstitution($numObjs, null);
+ $numInSet = Helpers::validateNumericNullSubstitution($numInSet, null);
+ Helpers::validateNotNegative($numInSet);
+ Helpers::validateNotNegative($numObjs);
+ $numObjs = (int) $numObjs;
+ $numInSet = (int) $numInSet;
+ // Microsoft documentation says following is true, but Excel
+ // does not enforce this restriction.
+ //Helpers::validateNotNegative($numObjs - $numInSet);
+ if ($numObjs === 0) {
+ Helpers::validateNotNegative(-$numInSet);
+
+ return 1;
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return round(Factorial::fact($numObjs + $numInSet - 1) / Factorial::fact($numObjs - 1)) / Factorial::fact($numInSet);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php
new file mode 100644
index 00000000000..ce930a83ab3
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php
@@ -0,0 +1,28 @@
+getMessage();
+ }
+
+ return exp($number);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
new file mode 100644
index 00000000000..f443f8e59ca
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
@@ -0,0 +1,110 @@
+getMessage();
+ }
+
+ $factLoop = floor($factVal);
+ if ($factVal > $factLoop) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return Statistical\Distributions\Gamma::gammaValue($factVal + 1);
+ }
+ }
+
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ }
+
+ return $factorial;
+ }
+
+ /**
+ * FACTDOUBLE.
+ *
+ * Returns the double factorial of a number.
+ *
+ * Excel Function:
+ * FACTDOUBLE(factVal)
+ *
+ * @param float $factVal Factorial Value
+ *
+ * @return float|int|string Double Factorial, or a string containing an error
+ */
+ public static function factDouble($factVal)
+ {
+ try {
+ $factVal = Helpers::validateNumericNullSubstitution($factVal, 0);
+ Helpers::validateNotNegative($factVal);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $factLoop = floor($factVal);
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop;
+ $factLoop -= 2;
+ }
+
+ return $factorial;
+ }
+
+ /**
+ * MULTINOMIAL.
+ *
+ * Returns the ratio of the factorial of a sum of values to the product of factorials.
+ *
+ * @param mixed[] $args An array of mixed values for the Data Series
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function multinomial(...$args)
+ {
+ $summer = 0;
+ $divisor = 1;
+
+ try {
+ // Loop through arguments
+ foreach (Functions::flattenArray($args) as $argx) {
+ $arg = Helpers::validateNumericNullSubstitution($argx, null);
+ Helpers::validateNotNegative($arg);
+ $arg = (int) $arg;
+ $summer += $arg;
+ $divisor *= self::fact($arg);
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $summer = self::fact($summer);
+
+ return $summer / $divisor;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php
new file mode 100644
index 00000000000..04e122058d5
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php
@@ -0,0 +1,166 @@
+getMessage();
+ }
+
+ return self::argumentsOk((float) $number, (float) $significance);
+ }
+
+ /**
+ * FLOOR.MATH.
+ *
+ * Round a number down to the nearest integer or to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.MATH(number[,significance[,mode]])
+ *
+ * @param mixed $number Number to round
+ * @param mixed $significance Significance
+ * @param mixed $mode direction to round negative numbers
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function math($number, $significance = null, $mode = 0)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
+ $mode = Helpers::validateNumericNullSubstitution($mode, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::argsOk((float) $number, (float) $significance, (int) $mode);
+ }
+
+ /**
+ * FLOOR.PRECISE.
+ *
+ * Rounds number down, toward zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.PRECISE(number[,significance])
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function precise($number, $significance = 1)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::argumentsOkPrecise((float) $number, (float) $significance);
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ *
+ * @return float|string
+ */
+ private static function argumentsOkPrecise(float $number, float $significance)
+ {
+ if ($significance == 0.0) {
+ return Functions::DIV0();
+ }
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ return floor($number / abs($significance)) * abs($significance);
+ }
+
+ /**
+ * Avoid Scrutinizer complexity problems.
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ private static function argsOk(float $number, float $significance, int $mode)
+ {
+ if (!$significance) {
+ return Functions::DIV0();
+ }
+ if (!$number) {
+ return 0.0;
+ }
+ if (self::floorMathTest($number, $significance, $mode)) {
+ return ceil($number / $significance) * $significance;
+ }
+
+ return floor($number / $significance) * $significance;
+ }
+
+ /**
+ * Let FLOORMATH complexity pass Scrutinizer.
+ */
+ private static function floorMathTest(float $number, float $significance, int $mode): bool
+ {
+ return Helpers::returnSign($significance) == -1 || (Helpers::returnSign($number) == -1 && !empty($mode));
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ *
+ * @return float|string
+ */
+ private static function argumentsOk(float $number, float $significance)
+ {
+ if ($significance == 0.0) {
+ return Functions::DIV0();
+ }
+ if ($number == 0.0) {
+ return 0.0;
+ }
+ if (Helpers::returnSign($significance) == 1) {
+ return floor($number / $significance) * $significance;
+ }
+ if (Helpers::returnSign($number) == -1 && Helpers::returnSign($significance) == -1) {
+ return floor($number / $significance) * $significance;
+ }
+
+ return Functions::NAN();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
new file mode 100644
index 00000000000..1dd52faa752
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
@@ -0,0 +1,69 @@
+getMessage();
+ }
+
+ if (count($arrayArgs) <= 0) {
+ return Functions::VALUE();
+ }
+ $gcd = (int) array_pop($arrayArgs);
+ do {
+ $gcd = self::evaluateGCD($gcd, (int) array_pop($arrayArgs));
+ } while (!empty($arrayArgs));
+
+ return $gcd;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
new file mode 100644
index 00000000000..b89644a971b
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
@@ -0,0 +1,129 @@
+= 0.
+ *
+ * @param float|int $number
+ */
+ public static function validateNotNegative($number, ?string $except = null): void
+ {
+ if ($number >= 0) {
+ return;
+ }
+
+ throw new Exception($except ?? Functions::NAN());
+ }
+
+ /**
+ * Confirm number > 0.
+ *
+ * @param float|int $number
+ */
+ public static function validatePositive($number, ?string $except = null): void
+ {
+ if ($number > 0) {
+ return;
+ }
+
+ throw new Exception($except ?? Functions::NAN());
+ }
+
+ /**
+ * Confirm number != 0.
+ *
+ * @param float|int $number
+ */
+ public static function validateNotZero($number): void
+ {
+ if ($number) {
+ return;
+ }
+
+ throw new Exception(Functions::DIV0());
+ }
+
+ public static function returnSign(float $number): int
+ {
+ return $number ? (($number > 0) ? 1 : -1) : 0;
+ }
+
+ public static function getEven(float $number): float
+ {
+ $significance = 2 * self::returnSign($number);
+
+ return $significance ? (ceil($number / $significance) * $significance) : 0;
+ }
+
+ /**
+ * Return NAN or value depending on argument.
+ *
+ * @param float $result Number
+ *
+ * @return float|string
+ */
+ public static function numberOrNan($result)
+ {
+ return is_nan($result) ? Functions::NAN() : $result;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
new file mode 100644
index 00000000000..7aa3d06ae27
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
@@ -0,0 +1,31 @@
+getMessage();
+ }
+
+ return (int) floor($number);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
new file mode 100644
index 00000000000..46e9816dc68
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
@@ -0,0 +1,110 @@
+ 1; --$i) {
+ if (($value % $i) == 0) {
+ $factorArray = array_merge($factorArray, self::factors($value / $i));
+ $factorArray = array_merge($factorArray, self::factors($i));
+ if ($i <= sqrt($value)) {
+ break;
+ }
+ }
+ }
+ if (!empty($factorArray)) {
+ rsort($factorArray);
+
+ return $factorArray;
+ }
+
+ return [(int) $value];
+ }
+
+ /**
+ * LCM.
+ *
+ * Returns the lowest common multiplier of a series of numbers
+ * The least common multiple is the smallest positive integer that is a multiple
+ * of all integer arguments number1, number2, and so on. Use LCM to add fractions
+ * with different denominators.
+ *
+ * Excel Function:
+ * LCM(number1[,number2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return int|string Lowest Common Multiplier, or a string containing an error
+ */
+ public static function evaluate(...$args)
+ {
+ try {
+ $arrayArgs = [];
+ $anyZeros = 0;
+ $anyNonNulls = 0;
+ foreach (Functions::flattenArray($args) as $value1) {
+ $anyNonNulls += (int) ($value1 !== null);
+ $value = Helpers::validateNumericNullSubstitution($value1, 1);
+ Helpers::validateNotNegative($value);
+ $arrayArgs[] = (int) $value;
+ $anyZeros += (int) !((bool) $value);
+ }
+ self::testNonNulls($anyNonNulls);
+ if ($anyZeros) {
+ return 0;
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $returnValue = 1;
+ $allPoweredFactors = [];
+ // Loop through arguments
+ foreach ($arrayArgs as $value) {
+ $myFactors = self::factors(floor($value));
+ $myCountedFactors = array_count_values($myFactors);
+ $myPoweredFactors = [];
+ foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
+ $myPoweredFactors[$myCountedFactor] = $myCountedFactor ** $myCountedPower;
+ }
+ self::processPoweredFactors($allPoweredFactors, $myPoweredFactors);
+ }
+ foreach ($allPoweredFactors as $allPoweredFactor) {
+ $returnValue *= (int) $allPoweredFactor;
+ }
+
+ return $returnValue;
+ }
+
+ private static function processPoweredFactors(array &$allPoweredFactors, array &$myPoweredFactors): void
+ {
+ foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
+ if (isset($allPoweredFactors[$myPoweredValue])) {
+ if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ } else {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ }
+ }
+
+ private static function testNonNulls(int $anyNonNulls): void
+ {
+ if (!$anyNonNulls) {
+ throw new Exception(Functions::VALUE());
+ }
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
new file mode 100644
index 00000000000..d6878d88c91
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
@@ -0,0 +1,77 @@
+getMessage();
+ }
+
+ return log($number, $base);
+ }
+
+ /**
+ * LOG10.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function base10($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ Helpers::validatePositive($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return log10($number);
+ }
+
+ /**
+ * LN.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string Rounded number
+ */
+ public static function natural($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ Helpers::validatePositive($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return log($number);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
new file mode 100644
index 00000000000..92e1ff8e7bb
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
@@ -0,0 +1,138 @@
+determinant();
+ } catch (MatrixException $ex) {
+ return Functions::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MINVERSE.
+ *
+ * Returns the inverse matrix for the matrix stored in an array.
+ *
+ * Excel Function:
+ * MINVERSE(array)
+ *
+ * @param mixed $matrixValues A matrix of values
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function inverse($matrixValues)
+ {
+ try {
+ $matrix = self::getMatrix($matrixValues);
+
+ return $matrix->inverse()->toArray();
+ } catch (MatrixDiv0Exception $e) {
+ return Functions::NAN();
+ } catch (MatrixException $e) {
+ return Functions::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MMULT.
+ *
+ * @param mixed $matrixData1 A matrix of values
+ * @param mixed $matrixData2 A matrix of values
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function multiply($matrixData1, $matrixData2)
+ {
+ try {
+ $matrixA = self::getMatrix($matrixData1);
+ $matrixB = self::getMatrix($matrixData2);
+
+ return $matrixA->multiply($matrixB)->toArray();
+ } catch (MatrixException $ex) {
+ return Functions::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MUnit.
+ *
+ * @param mixed $dimension Number of rows and columns
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function identity($dimension)
+ {
+ try {
+ $dimension = (int) Helpers::validateNumericNullBool($dimension);
+ Helpers::validatePositive($dimension, Functions::VALUE());
+ $matrix = Builder::createIdentityMatrix($dimension, 0)->toArray();
+
+ return $matrix;
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php
new file mode 100644
index 00000000000..595c7fdccec
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php
@@ -0,0 +1,136 @@
+getMessage();
+ }
+
+ if (($dividend < 0.0) && ($divisor > 0.0)) {
+ return $divisor - fmod(abs($dividend), $divisor);
+ }
+ if (($dividend > 0.0) && ($divisor < 0.0)) {
+ return $divisor + fmod($dividend, abs($divisor));
+ }
+
+ return fmod($dividend, $divisor);
+ }
+
+ /**
+ * POWER.
+ *
+ * Computes x raised to the power y.
+ *
+ * @param float|int $x
+ * @param float|int $y
+ *
+ * @return float|int|string The result, or a string containing an error
+ */
+ public static function power($x, $y)
+ {
+ try {
+ $x = Helpers::validateNumericNullBool($x);
+ $y = Helpers::validateNumericNullBool($y);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if (!$x && !$y) {
+ return Functions::NAN();
+ }
+ if (!$x && $y < 0.0) {
+ return Functions::DIV0();
+ }
+
+ // Return
+ $result = $x ** $y;
+
+ return Helpers::numberOrNan($result);
+ }
+
+ /**
+ * PRODUCT.
+ *
+ * PRODUCT returns the product of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * PRODUCT(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string
+ */
+ public static function product(...$args)
+ {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ foreach (Functions::flattenArray($args) as $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg)) {
+ if ($returnValue === null) {
+ $returnValue = $arg;
+ } else {
+ $returnValue *= $arg;
+ }
+ } else {
+ return Functions::VALUE();
+ }
+ }
+
+ // Return
+ if ($returnValue === null) {
+ return 0;
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * QUOTIENT.
+ *
+ * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
+ * and denominator is the divisor.
+ *
+ * Excel Function:
+ * QUOTIENT(value1,value2)
+ *
+ * @param mixed $numerator Expect float|int
+ * @param mixed $denominator Expect float|int
+ *
+ * @return int|string
+ */
+ public static function quotient($numerator, $denominator)
+ {
+ try {
+ $numerator = Helpers::validateNumericNullSubstitution($numerator, 0);
+ $denominator = Helpers::validateNumericNullSubstitution($denominator, 0);
+ Helpers::validateNotZero($denominator);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (int) ($numerator / $denominator);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
new file mode 100644
index 00000000000..963a789ac52
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
@@ -0,0 +1,39 @@
+getMessage();
+ }
+
+ return mt_rand($min, $max);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
new file mode 100644
index 00000000000..71a6df3ad79
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
@@ -0,0 +1,835 @@
+ ['VL'],
+ 46 => ['VLI'],
+ 47 => ['VLII'],
+ 48 => ['VLIII'],
+ 49 => ['VLIV', 'IL'],
+ 95 => ['VC'],
+ 96 => ['VCI'],
+ 97 => ['VCII'],
+ 98 => ['VCIII'],
+ 99 => ['VCIV', 'IC'],
+ 145 => ['CVL'],
+ 146 => ['CVLI'],
+ 147 => ['CVLII'],
+ 148 => ['CVLIII'],
+ 149 => ['CVLIV', 'CIL'],
+ 195 => ['CVC'],
+ 196 => ['CVCI'],
+ 197 => ['CVCII'],
+ 198 => ['CVCIII'],
+ 199 => ['CVCIV', 'CIC'],
+ 245 => ['CCVL'],
+ 246 => ['CCVLI'],
+ 247 => ['CCVLII'],
+ 248 => ['CCVLIII'],
+ 249 => ['CCVLIV', 'CCIL'],
+ 295 => ['CCVC'],
+ 296 => ['CCVCI'],
+ 297 => ['CCVCII'],
+ 298 => ['CCVCIII'],
+ 299 => ['CCVCIV', 'CCIC'],
+ 345 => ['CCCVL'],
+ 346 => ['CCCVLI'],
+ 347 => ['CCCVLII'],
+ 348 => ['CCCVLIII'],
+ 349 => ['CCCVLIV', 'CCCIL'],
+ 395 => ['CCCVC'],
+ 396 => ['CCCVCI'],
+ 397 => ['CCCVCII'],
+ 398 => ['CCCVCIII'],
+ 399 => ['CCCVCIV', 'CCCIC'],
+ 445 => ['CDVL'],
+ 446 => ['CDVLI'],
+ 447 => ['CDVLII'],
+ 448 => ['CDVLIII'],
+ 449 => ['CDVLIV', 'CDIL'],
+ 450 => ['LD'],
+ 451 => ['LDI'],
+ 452 => ['LDII'],
+ 453 => ['LDIII'],
+ 454 => ['LDIV'],
+ 455 => ['LDV'],
+ 456 => ['LDVI'],
+ 457 => ['LDVII'],
+ 458 => ['LDVIII'],
+ 459 => ['LDIX'],
+ 460 => ['LDX'],
+ 461 => ['LDXI'],
+ 462 => ['LDXII'],
+ 463 => ['LDXIII'],
+ 464 => ['LDXIV'],
+ 465 => ['LDXV'],
+ 466 => ['LDXVI'],
+ 467 => ['LDXVII'],
+ 468 => ['LDXVIII'],
+ 469 => ['LDXIX'],
+ 470 => ['LDXX'],
+ 471 => ['LDXXI'],
+ 472 => ['LDXXII'],
+ 473 => ['LDXXIII'],
+ 474 => ['LDXXIV'],
+ 475 => ['LDXXV'],
+ 476 => ['LDXXVI'],
+ 477 => ['LDXXVII'],
+ 478 => ['LDXXVIII'],
+ 479 => ['LDXXIX'],
+ 480 => ['LDXXX'],
+ 481 => ['LDXXXI'],
+ 482 => ['LDXXXII'],
+ 483 => ['LDXXXIII'],
+ 484 => ['LDXXXIV'],
+ 485 => ['LDXXXV'],
+ 486 => ['LDXXXVI'],
+ 487 => ['LDXXXVII'],
+ 488 => ['LDXXXVIII'],
+ 489 => ['LDXXXIX'],
+ 490 => ['LDXL', 'XD'],
+ 491 => ['LDXLI', 'XDI'],
+ 492 => ['LDXLII', 'XDII'],
+ 493 => ['LDXLIII', 'XDIII'],
+ 494 => ['LDXLIV', 'XDIV'],
+ 495 => ['LDVL', 'XDV', 'VD'],
+ 496 => ['LDVLI', 'XDVI', 'VDI'],
+ 497 => ['LDVLII', 'XDVII', 'VDII'],
+ 498 => ['LDVLIII', 'XDVIII', 'VDIII'],
+ 499 => ['LDVLIV', 'XDIX', 'VDIV', 'ID'],
+ 545 => ['DVL'],
+ 546 => ['DVLI'],
+ 547 => ['DVLII'],
+ 548 => ['DVLIII'],
+ 549 => ['DVLIV', 'DIL'],
+ 595 => ['DVC'],
+ 596 => ['DVCI'],
+ 597 => ['DVCII'],
+ 598 => ['DVCIII'],
+ 599 => ['DVCIV', 'DIC'],
+ 645 => ['DCVL'],
+ 646 => ['DCVLI'],
+ 647 => ['DCVLII'],
+ 648 => ['DCVLIII'],
+ 649 => ['DCVLIV', 'DCIL'],
+ 695 => ['DCVC'],
+ 696 => ['DCVCI'],
+ 697 => ['DCVCII'],
+ 698 => ['DCVCIII'],
+ 699 => ['DCVCIV', 'DCIC'],
+ 745 => ['DCCVL'],
+ 746 => ['DCCVLI'],
+ 747 => ['DCCVLII'],
+ 748 => ['DCCVLIII'],
+ 749 => ['DCCVLIV', 'DCCIL'],
+ 795 => ['DCCVC'],
+ 796 => ['DCCVCI'],
+ 797 => ['DCCVCII'],
+ 798 => ['DCCVCIII'],
+ 799 => ['DCCVCIV', 'DCCIC'],
+ 845 => ['DCCCVL'],
+ 846 => ['DCCCVLI'],
+ 847 => ['DCCCVLII'],
+ 848 => ['DCCCVLIII'],
+ 849 => ['DCCCVLIV', 'DCCCIL'],
+ 895 => ['DCCCVC'],
+ 896 => ['DCCCVCI'],
+ 897 => ['DCCCVCII'],
+ 898 => ['DCCCVCIII'],
+ 899 => ['DCCCVCIV', 'DCCCIC'],
+ 945 => ['CMVL'],
+ 946 => ['CMVLI'],
+ 947 => ['CMVLII'],
+ 948 => ['CMVLIII'],
+ 949 => ['CMVLIV', 'CMIL'],
+ 950 => ['LM'],
+ 951 => ['LMI'],
+ 952 => ['LMII'],
+ 953 => ['LMIII'],
+ 954 => ['LMIV'],
+ 955 => ['LMV'],
+ 956 => ['LMVI'],
+ 957 => ['LMVII'],
+ 958 => ['LMVIII'],
+ 959 => ['LMIX'],
+ 960 => ['LMX'],
+ 961 => ['LMXI'],
+ 962 => ['LMXII'],
+ 963 => ['LMXIII'],
+ 964 => ['LMXIV'],
+ 965 => ['LMXV'],
+ 966 => ['LMXVI'],
+ 967 => ['LMXVII'],
+ 968 => ['LMXVIII'],
+ 969 => ['LMXIX'],
+ 970 => ['LMXX'],
+ 971 => ['LMXXI'],
+ 972 => ['LMXXII'],
+ 973 => ['LMXXIII'],
+ 974 => ['LMXXIV'],
+ 975 => ['LMXXV'],
+ 976 => ['LMXXVI'],
+ 977 => ['LMXXVII'],
+ 978 => ['LMXXVIII'],
+ 979 => ['LMXXIX'],
+ 980 => ['LMXXX'],
+ 981 => ['LMXXXI'],
+ 982 => ['LMXXXII'],
+ 983 => ['LMXXXIII'],
+ 984 => ['LMXXXIV'],
+ 985 => ['LMXXXV'],
+ 986 => ['LMXXXVI'],
+ 987 => ['LMXXXVII'],
+ 988 => ['LMXXXVIII'],
+ 989 => ['LMXXXIX'],
+ 990 => ['LMXL', 'XM'],
+ 991 => ['LMXLI', 'XMI'],
+ 992 => ['LMXLII', 'XMII'],
+ 993 => ['LMXLIII', 'XMIII'],
+ 994 => ['LMXLIV', 'XMIV'],
+ 995 => ['LMVL', 'XMV', 'VM'],
+ 996 => ['LMVLI', 'XMVI', 'VMI'],
+ 997 => ['LMVLII', 'XMVII', 'VMII'],
+ 998 => ['LMVLIII', 'XMVIII', 'VMIII'],
+ 999 => ['LMVLIV', 'XMIX', 'VMIV', 'IM'],
+ 1045 => ['MVL'],
+ 1046 => ['MVLI'],
+ 1047 => ['MVLII'],
+ 1048 => ['MVLIII'],
+ 1049 => ['MVLIV', 'MIL'],
+ 1095 => ['MVC'],
+ 1096 => ['MVCI'],
+ 1097 => ['MVCII'],
+ 1098 => ['MVCIII'],
+ 1099 => ['MVCIV', 'MIC'],
+ 1145 => ['MCVL'],
+ 1146 => ['MCVLI'],
+ 1147 => ['MCVLII'],
+ 1148 => ['MCVLIII'],
+ 1149 => ['MCVLIV', 'MCIL'],
+ 1195 => ['MCVC'],
+ 1196 => ['MCVCI'],
+ 1197 => ['MCVCII'],
+ 1198 => ['MCVCIII'],
+ 1199 => ['MCVCIV', 'MCIC'],
+ 1245 => ['MCCVL'],
+ 1246 => ['MCCVLI'],
+ 1247 => ['MCCVLII'],
+ 1248 => ['MCCVLIII'],
+ 1249 => ['MCCVLIV', 'MCCIL'],
+ 1295 => ['MCCVC'],
+ 1296 => ['MCCVCI'],
+ 1297 => ['MCCVCII'],
+ 1298 => ['MCCVCIII'],
+ 1299 => ['MCCVCIV', 'MCCIC'],
+ 1345 => ['MCCCVL'],
+ 1346 => ['MCCCVLI'],
+ 1347 => ['MCCCVLII'],
+ 1348 => ['MCCCVLIII'],
+ 1349 => ['MCCCVLIV', 'MCCCIL'],
+ 1395 => ['MCCCVC'],
+ 1396 => ['MCCCVCI'],
+ 1397 => ['MCCCVCII'],
+ 1398 => ['MCCCVCIII'],
+ 1399 => ['MCCCVCIV', 'MCCCIC'],
+ 1445 => ['MCDVL'],
+ 1446 => ['MCDVLI'],
+ 1447 => ['MCDVLII'],
+ 1448 => ['MCDVLIII'],
+ 1449 => ['MCDVLIV', 'MCDIL'],
+ 1450 => ['MLD'],
+ 1451 => ['MLDI'],
+ 1452 => ['MLDII'],
+ 1453 => ['MLDIII'],
+ 1454 => ['MLDIV'],
+ 1455 => ['MLDV'],
+ 1456 => ['MLDVI'],
+ 1457 => ['MLDVII'],
+ 1458 => ['MLDVIII'],
+ 1459 => ['MLDIX'],
+ 1460 => ['MLDX'],
+ 1461 => ['MLDXI'],
+ 1462 => ['MLDXII'],
+ 1463 => ['MLDXIII'],
+ 1464 => ['MLDXIV'],
+ 1465 => ['MLDXV'],
+ 1466 => ['MLDXVI'],
+ 1467 => ['MLDXVII'],
+ 1468 => ['MLDXVIII'],
+ 1469 => ['MLDXIX'],
+ 1470 => ['MLDXX'],
+ 1471 => ['MLDXXI'],
+ 1472 => ['MLDXXII'],
+ 1473 => ['MLDXXIII'],
+ 1474 => ['MLDXXIV'],
+ 1475 => ['MLDXXV'],
+ 1476 => ['MLDXXVI'],
+ 1477 => ['MLDXXVII'],
+ 1478 => ['MLDXXVIII'],
+ 1479 => ['MLDXXIX'],
+ 1480 => ['MLDXXX'],
+ 1481 => ['MLDXXXI'],
+ 1482 => ['MLDXXXII'],
+ 1483 => ['MLDXXXIII'],
+ 1484 => ['MLDXXXIV'],
+ 1485 => ['MLDXXXV'],
+ 1486 => ['MLDXXXVI'],
+ 1487 => ['MLDXXXVII'],
+ 1488 => ['MLDXXXVIII'],
+ 1489 => ['MLDXXXIX'],
+ 1490 => ['MLDXL', 'MXD'],
+ 1491 => ['MLDXLI', 'MXDI'],
+ 1492 => ['MLDXLII', 'MXDII'],
+ 1493 => ['MLDXLIII', 'MXDIII'],
+ 1494 => ['MLDXLIV', 'MXDIV'],
+ 1495 => ['MLDVL', 'MXDV', 'MVD'],
+ 1496 => ['MLDVLI', 'MXDVI', 'MVDI'],
+ 1497 => ['MLDVLII', 'MXDVII', 'MVDII'],
+ 1498 => ['MLDVLIII', 'MXDVIII', 'MVDIII'],
+ 1499 => ['MLDVLIV', 'MXDIX', 'MVDIV', 'MID'],
+ 1545 => ['MDVL'],
+ 1546 => ['MDVLI'],
+ 1547 => ['MDVLII'],
+ 1548 => ['MDVLIII'],
+ 1549 => ['MDVLIV', 'MDIL'],
+ 1595 => ['MDVC'],
+ 1596 => ['MDVCI'],
+ 1597 => ['MDVCII'],
+ 1598 => ['MDVCIII'],
+ 1599 => ['MDVCIV', 'MDIC'],
+ 1645 => ['MDCVL'],
+ 1646 => ['MDCVLI'],
+ 1647 => ['MDCVLII'],
+ 1648 => ['MDCVLIII'],
+ 1649 => ['MDCVLIV', 'MDCIL'],
+ 1695 => ['MDCVC'],
+ 1696 => ['MDCVCI'],
+ 1697 => ['MDCVCII'],
+ 1698 => ['MDCVCIII'],
+ 1699 => ['MDCVCIV', 'MDCIC'],
+ 1745 => ['MDCCVL'],
+ 1746 => ['MDCCVLI'],
+ 1747 => ['MDCCVLII'],
+ 1748 => ['MDCCVLIII'],
+ 1749 => ['MDCCVLIV', 'MDCCIL'],
+ 1795 => ['MDCCVC'],
+ 1796 => ['MDCCVCI'],
+ 1797 => ['MDCCVCII'],
+ 1798 => ['MDCCVCIII'],
+ 1799 => ['MDCCVCIV', 'MDCCIC'],
+ 1845 => ['MDCCCVL'],
+ 1846 => ['MDCCCVLI'],
+ 1847 => ['MDCCCVLII'],
+ 1848 => ['MDCCCVLIII'],
+ 1849 => ['MDCCCVLIV', 'MDCCCIL'],
+ 1895 => ['MDCCCVC'],
+ 1896 => ['MDCCCVCI'],
+ 1897 => ['MDCCCVCII'],
+ 1898 => ['MDCCCVCIII'],
+ 1899 => ['MDCCCVCIV', 'MDCCCIC'],
+ 1945 => ['MCMVL'],
+ 1946 => ['MCMVLI'],
+ 1947 => ['MCMVLII'],
+ 1948 => ['MCMVLIII'],
+ 1949 => ['MCMVLIV', 'MCMIL'],
+ 1950 => ['MLM'],
+ 1951 => ['MLMI'],
+ 1952 => ['MLMII'],
+ 1953 => ['MLMIII'],
+ 1954 => ['MLMIV'],
+ 1955 => ['MLMV'],
+ 1956 => ['MLMVI'],
+ 1957 => ['MLMVII'],
+ 1958 => ['MLMVIII'],
+ 1959 => ['MLMIX'],
+ 1960 => ['MLMX'],
+ 1961 => ['MLMXI'],
+ 1962 => ['MLMXII'],
+ 1963 => ['MLMXIII'],
+ 1964 => ['MLMXIV'],
+ 1965 => ['MLMXV'],
+ 1966 => ['MLMXVI'],
+ 1967 => ['MLMXVII'],
+ 1968 => ['MLMXVIII'],
+ 1969 => ['MLMXIX'],
+ 1970 => ['MLMXX'],
+ 1971 => ['MLMXXI'],
+ 1972 => ['MLMXXII'],
+ 1973 => ['MLMXXIII'],
+ 1974 => ['MLMXXIV'],
+ 1975 => ['MLMXXV'],
+ 1976 => ['MLMXXVI'],
+ 1977 => ['MLMXXVII'],
+ 1978 => ['MLMXXVIII'],
+ 1979 => ['MLMXXIX'],
+ 1980 => ['MLMXXX'],
+ 1981 => ['MLMXXXI'],
+ 1982 => ['MLMXXXII'],
+ 1983 => ['MLMXXXIII'],
+ 1984 => ['MLMXXXIV'],
+ 1985 => ['MLMXXXV'],
+ 1986 => ['MLMXXXVI'],
+ 1987 => ['MLMXXXVII'],
+ 1988 => ['MLMXXXVIII'],
+ 1989 => ['MLMXXXIX'],
+ 1990 => ['MLMXL', 'MXM'],
+ 1991 => ['MLMXLI', 'MXMI'],
+ 1992 => ['MLMXLII', 'MXMII'],
+ 1993 => ['MLMXLIII', 'MXMIII'],
+ 1994 => ['MLMXLIV', 'MXMIV'],
+ 1995 => ['MLMVL', 'MXMV', 'MVM'],
+ 1996 => ['MLMVLI', 'MXMVI', 'MVMI'],
+ 1997 => ['MLMVLII', 'MXMVII', 'MVMII'],
+ 1998 => ['MLMVLIII', 'MXMVIII', 'MVMIII'],
+ 1999 => ['MLMVLIV', 'MXMIX', 'MVMIV', 'MIM'],
+ 2045 => ['MMVL'],
+ 2046 => ['MMVLI'],
+ 2047 => ['MMVLII'],
+ 2048 => ['MMVLIII'],
+ 2049 => ['MMVLIV', 'MMIL'],
+ 2095 => ['MMVC'],
+ 2096 => ['MMVCI'],
+ 2097 => ['MMVCII'],
+ 2098 => ['MMVCIII'],
+ 2099 => ['MMVCIV', 'MMIC'],
+ 2145 => ['MMCVL'],
+ 2146 => ['MMCVLI'],
+ 2147 => ['MMCVLII'],
+ 2148 => ['MMCVLIII'],
+ 2149 => ['MMCVLIV', 'MMCIL'],
+ 2195 => ['MMCVC'],
+ 2196 => ['MMCVCI'],
+ 2197 => ['MMCVCII'],
+ 2198 => ['MMCVCIII'],
+ 2199 => ['MMCVCIV', 'MMCIC'],
+ 2245 => ['MMCCVL'],
+ 2246 => ['MMCCVLI'],
+ 2247 => ['MMCCVLII'],
+ 2248 => ['MMCCVLIII'],
+ 2249 => ['MMCCVLIV', 'MMCCIL'],
+ 2295 => ['MMCCVC'],
+ 2296 => ['MMCCVCI'],
+ 2297 => ['MMCCVCII'],
+ 2298 => ['MMCCVCIII'],
+ 2299 => ['MMCCVCIV', 'MMCCIC'],
+ 2345 => ['MMCCCVL'],
+ 2346 => ['MMCCCVLI'],
+ 2347 => ['MMCCCVLII'],
+ 2348 => ['MMCCCVLIII'],
+ 2349 => ['MMCCCVLIV', 'MMCCCIL'],
+ 2395 => ['MMCCCVC'],
+ 2396 => ['MMCCCVCI'],
+ 2397 => ['MMCCCVCII'],
+ 2398 => ['MMCCCVCIII'],
+ 2399 => ['MMCCCVCIV', 'MMCCCIC'],
+ 2445 => ['MMCDVL'],
+ 2446 => ['MMCDVLI'],
+ 2447 => ['MMCDVLII'],
+ 2448 => ['MMCDVLIII'],
+ 2449 => ['MMCDVLIV', 'MMCDIL'],
+ 2450 => ['MMLD'],
+ 2451 => ['MMLDI'],
+ 2452 => ['MMLDII'],
+ 2453 => ['MMLDIII'],
+ 2454 => ['MMLDIV'],
+ 2455 => ['MMLDV'],
+ 2456 => ['MMLDVI'],
+ 2457 => ['MMLDVII'],
+ 2458 => ['MMLDVIII'],
+ 2459 => ['MMLDIX'],
+ 2460 => ['MMLDX'],
+ 2461 => ['MMLDXI'],
+ 2462 => ['MMLDXII'],
+ 2463 => ['MMLDXIII'],
+ 2464 => ['MMLDXIV'],
+ 2465 => ['MMLDXV'],
+ 2466 => ['MMLDXVI'],
+ 2467 => ['MMLDXVII'],
+ 2468 => ['MMLDXVIII'],
+ 2469 => ['MMLDXIX'],
+ 2470 => ['MMLDXX'],
+ 2471 => ['MMLDXXI'],
+ 2472 => ['MMLDXXII'],
+ 2473 => ['MMLDXXIII'],
+ 2474 => ['MMLDXXIV'],
+ 2475 => ['MMLDXXV'],
+ 2476 => ['MMLDXXVI'],
+ 2477 => ['MMLDXXVII'],
+ 2478 => ['MMLDXXVIII'],
+ 2479 => ['MMLDXXIX'],
+ 2480 => ['MMLDXXX'],
+ 2481 => ['MMLDXXXI'],
+ 2482 => ['MMLDXXXII'],
+ 2483 => ['MMLDXXXIII'],
+ 2484 => ['MMLDXXXIV'],
+ 2485 => ['MMLDXXXV'],
+ 2486 => ['MMLDXXXVI'],
+ 2487 => ['MMLDXXXVII'],
+ 2488 => ['MMLDXXXVIII'],
+ 2489 => ['MMLDXXXIX'],
+ 2490 => ['MMLDXL', 'MMXD'],
+ 2491 => ['MMLDXLI', 'MMXDI'],
+ 2492 => ['MMLDXLII', 'MMXDII'],
+ 2493 => ['MMLDXLIII', 'MMXDIII'],
+ 2494 => ['MMLDXLIV', 'MMXDIV'],
+ 2495 => ['MMLDVL', 'MMXDV', 'MMVD'],
+ 2496 => ['MMLDVLI', 'MMXDVI', 'MMVDI'],
+ 2497 => ['MMLDVLII', 'MMXDVII', 'MMVDII'],
+ 2498 => ['MMLDVLIII', 'MMXDVIII', 'MMVDIII'],
+ 2499 => ['MMLDVLIV', 'MMXDIX', 'MMVDIV', 'MMID'],
+ 2545 => ['MMDVL'],
+ 2546 => ['MMDVLI'],
+ 2547 => ['MMDVLII'],
+ 2548 => ['MMDVLIII'],
+ 2549 => ['MMDVLIV', 'MMDIL'],
+ 2595 => ['MMDVC'],
+ 2596 => ['MMDVCI'],
+ 2597 => ['MMDVCII'],
+ 2598 => ['MMDVCIII'],
+ 2599 => ['MMDVCIV', 'MMDIC'],
+ 2645 => ['MMDCVL'],
+ 2646 => ['MMDCVLI'],
+ 2647 => ['MMDCVLII'],
+ 2648 => ['MMDCVLIII'],
+ 2649 => ['MMDCVLIV', 'MMDCIL'],
+ 2695 => ['MMDCVC'],
+ 2696 => ['MMDCVCI'],
+ 2697 => ['MMDCVCII'],
+ 2698 => ['MMDCVCIII'],
+ 2699 => ['MMDCVCIV', 'MMDCIC'],
+ 2745 => ['MMDCCVL'],
+ 2746 => ['MMDCCVLI'],
+ 2747 => ['MMDCCVLII'],
+ 2748 => ['MMDCCVLIII'],
+ 2749 => ['MMDCCVLIV', 'MMDCCIL'],
+ 2795 => ['MMDCCVC'],
+ 2796 => ['MMDCCVCI'],
+ 2797 => ['MMDCCVCII'],
+ 2798 => ['MMDCCVCIII'],
+ 2799 => ['MMDCCVCIV', 'MMDCCIC'],
+ 2845 => ['MMDCCCVL'],
+ 2846 => ['MMDCCCVLI'],
+ 2847 => ['MMDCCCVLII'],
+ 2848 => ['MMDCCCVLIII'],
+ 2849 => ['MMDCCCVLIV', 'MMDCCCIL'],
+ 2895 => ['MMDCCCVC'],
+ 2896 => ['MMDCCCVCI'],
+ 2897 => ['MMDCCCVCII'],
+ 2898 => ['MMDCCCVCIII'],
+ 2899 => ['MMDCCCVCIV', 'MMDCCCIC'],
+ 2945 => ['MMCMVL'],
+ 2946 => ['MMCMVLI'],
+ 2947 => ['MMCMVLII'],
+ 2948 => ['MMCMVLIII'],
+ 2949 => ['MMCMVLIV', 'MMCMIL'],
+ 2950 => ['MMLM'],
+ 2951 => ['MMLMI'],
+ 2952 => ['MMLMII'],
+ 2953 => ['MMLMIII'],
+ 2954 => ['MMLMIV'],
+ 2955 => ['MMLMV'],
+ 2956 => ['MMLMVI'],
+ 2957 => ['MMLMVII'],
+ 2958 => ['MMLMVIII'],
+ 2959 => ['MMLMIX'],
+ 2960 => ['MMLMX'],
+ 2961 => ['MMLMXI'],
+ 2962 => ['MMLMXII'],
+ 2963 => ['MMLMXIII'],
+ 2964 => ['MMLMXIV'],
+ 2965 => ['MMLMXV'],
+ 2966 => ['MMLMXVI'],
+ 2967 => ['MMLMXVII'],
+ 2968 => ['MMLMXVIII'],
+ 2969 => ['MMLMXIX'],
+ 2970 => ['MMLMXX'],
+ 2971 => ['MMLMXXI'],
+ 2972 => ['MMLMXXII'],
+ 2973 => ['MMLMXXIII'],
+ 2974 => ['MMLMXXIV'],
+ 2975 => ['MMLMXXV'],
+ 2976 => ['MMLMXXVI'],
+ 2977 => ['MMLMXXVII'],
+ 2978 => ['MMLMXXVIII'],
+ 2979 => ['MMLMXXIX'],
+ 2980 => ['MMLMXXX'],
+ 2981 => ['MMLMXXXI'],
+ 2982 => ['MMLMXXXII'],
+ 2983 => ['MMLMXXXIII'],
+ 2984 => ['MMLMXXXIV'],
+ 2985 => ['MMLMXXXV'],
+ 2986 => ['MMLMXXXVI'],
+ 2987 => ['MMLMXXXVII'],
+ 2988 => ['MMLMXXXVIII'],
+ 2989 => ['MMLMXXXIX'],
+ 2990 => ['MMLMXL', 'MMXM'],
+ 2991 => ['MMLMXLI', 'MMXMI'],
+ 2992 => ['MMLMXLII', 'MMXMII'],
+ 2993 => ['MMLMXLIII', 'MMXMIII'],
+ 2994 => ['MMLMXLIV', 'MMXMIV'],
+ 2995 => ['MMLMVL', 'MMXMV', 'MMVM'],
+ 2996 => ['MMLMVLI', 'MMXMVI', 'MMVMI'],
+ 2997 => ['MMLMVLII', 'MMXMVII', 'MMVMII'],
+ 2998 => ['MMLMVLIII', 'MMXMVIII', 'MMVMIII'],
+ 2999 => ['MMLMVLIV', 'MMXMIX', 'MMVMIV', 'MMIM'],
+ 3045 => ['MMMVL'],
+ 3046 => ['MMMVLI'],
+ 3047 => ['MMMVLII'],
+ 3048 => ['MMMVLIII'],
+ 3049 => ['MMMVLIV', 'MMMIL'],
+ 3095 => ['MMMVC'],
+ 3096 => ['MMMVCI'],
+ 3097 => ['MMMVCII'],
+ 3098 => ['MMMVCIII'],
+ 3099 => ['MMMVCIV', 'MMMIC'],
+ 3145 => ['MMMCVL'],
+ 3146 => ['MMMCVLI'],
+ 3147 => ['MMMCVLII'],
+ 3148 => ['MMMCVLIII'],
+ 3149 => ['MMMCVLIV', 'MMMCIL'],
+ 3195 => ['MMMCVC'],
+ 3196 => ['MMMCVCI'],
+ 3197 => ['MMMCVCII'],
+ 3198 => ['MMMCVCIII'],
+ 3199 => ['MMMCVCIV', 'MMMCIC'],
+ 3245 => ['MMMCCVL'],
+ 3246 => ['MMMCCVLI'],
+ 3247 => ['MMMCCVLII'],
+ 3248 => ['MMMCCVLIII'],
+ 3249 => ['MMMCCVLIV', 'MMMCCIL'],
+ 3295 => ['MMMCCVC'],
+ 3296 => ['MMMCCVCI'],
+ 3297 => ['MMMCCVCII'],
+ 3298 => ['MMMCCVCIII'],
+ 3299 => ['MMMCCVCIV', 'MMMCCIC'],
+ 3345 => ['MMMCCCVL'],
+ 3346 => ['MMMCCCVLI'],
+ 3347 => ['MMMCCCVLII'],
+ 3348 => ['MMMCCCVLIII'],
+ 3349 => ['MMMCCCVLIV', 'MMMCCCIL'],
+ 3395 => ['MMMCCCVC'],
+ 3396 => ['MMMCCCVCI'],
+ 3397 => ['MMMCCCVCII'],
+ 3398 => ['MMMCCCVCIII'],
+ 3399 => ['MMMCCCVCIV', 'MMMCCCIC'],
+ 3445 => ['MMMCDVL'],
+ 3446 => ['MMMCDVLI'],
+ 3447 => ['MMMCDVLII'],
+ 3448 => ['MMMCDVLIII'],
+ 3449 => ['MMMCDVLIV', 'MMMCDIL'],
+ 3450 => ['MMMLD'],
+ 3451 => ['MMMLDI'],
+ 3452 => ['MMMLDII'],
+ 3453 => ['MMMLDIII'],
+ 3454 => ['MMMLDIV'],
+ 3455 => ['MMMLDV'],
+ 3456 => ['MMMLDVI'],
+ 3457 => ['MMMLDVII'],
+ 3458 => ['MMMLDVIII'],
+ 3459 => ['MMMLDIX'],
+ 3460 => ['MMMLDX'],
+ 3461 => ['MMMLDXI'],
+ 3462 => ['MMMLDXII'],
+ 3463 => ['MMMLDXIII'],
+ 3464 => ['MMMLDXIV'],
+ 3465 => ['MMMLDXV'],
+ 3466 => ['MMMLDXVI'],
+ 3467 => ['MMMLDXVII'],
+ 3468 => ['MMMLDXVIII'],
+ 3469 => ['MMMLDXIX'],
+ 3470 => ['MMMLDXX'],
+ 3471 => ['MMMLDXXI'],
+ 3472 => ['MMMLDXXII'],
+ 3473 => ['MMMLDXXIII'],
+ 3474 => ['MMMLDXXIV'],
+ 3475 => ['MMMLDXXV'],
+ 3476 => ['MMMLDXXVI'],
+ 3477 => ['MMMLDXXVII'],
+ 3478 => ['MMMLDXXVIII'],
+ 3479 => ['MMMLDXXIX'],
+ 3480 => ['MMMLDXXX'],
+ 3481 => ['MMMLDXXXI'],
+ 3482 => ['MMMLDXXXII'],
+ 3483 => ['MMMLDXXXIII'],
+ 3484 => ['MMMLDXXXIV'],
+ 3485 => ['MMMLDXXXV'],
+ 3486 => ['MMMLDXXXVI'],
+ 3487 => ['MMMLDXXXVII'],
+ 3488 => ['MMMLDXXXVIII'],
+ 3489 => ['MMMLDXXXIX'],
+ 3490 => ['MMMLDXL', 'MMMXD'],
+ 3491 => ['MMMLDXLI', 'MMMXDI'],
+ 3492 => ['MMMLDXLII', 'MMMXDII'],
+ 3493 => ['MMMLDXLIII', 'MMMXDIII'],
+ 3494 => ['MMMLDXLIV', 'MMMXDIV'],
+ 3495 => ['MMMLDVL', 'MMMXDV', 'MMMVD'],
+ 3496 => ['MMMLDVLI', 'MMMXDVI', 'MMMVDI'],
+ 3497 => ['MMMLDVLII', 'MMMXDVII', 'MMMVDII'],
+ 3498 => ['MMMLDVLIII', 'MMMXDVIII', 'MMMVDIII'],
+ 3499 => ['MMMLDVLIV', 'MMMXDIX', 'MMMVDIV', 'MMMID'],
+ 3545 => ['MMMDVL'],
+ 3546 => ['MMMDVLI'],
+ 3547 => ['MMMDVLII'],
+ 3548 => ['MMMDVLIII'],
+ 3549 => ['MMMDVLIV', 'MMMDIL'],
+ 3595 => ['MMMDVC'],
+ 3596 => ['MMMDVCI'],
+ 3597 => ['MMMDVCII'],
+ 3598 => ['MMMDVCIII'],
+ 3599 => ['MMMDVCIV', 'MMMDIC'],
+ 3645 => ['MMMDCVL'],
+ 3646 => ['MMMDCVLI'],
+ 3647 => ['MMMDCVLII'],
+ 3648 => ['MMMDCVLIII'],
+ 3649 => ['MMMDCVLIV', 'MMMDCIL'],
+ 3695 => ['MMMDCVC'],
+ 3696 => ['MMMDCVCI'],
+ 3697 => ['MMMDCVCII'],
+ 3698 => ['MMMDCVCIII'],
+ 3699 => ['MMMDCVCIV', 'MMMDCIC'],
+ 3745 => ['MMMDCCVL'],
+ 3746 => ['MMMDCCVLI'],
+ 3747 => ['MMMDCCVLII'],
+ 3748 => ['MMMDCCVLIII'],
+ 3749 => ['MMMDCCVLIV', 'MMMDCCIL'],
+ 3795 => ['MMMDCCVC'],
+ 3796 => ['MMMDCCVCI'],
+ 3797 => ['MMMDCCVCII'],
+ 3798 => ['MMMDCCVCIII'],
+ 3799 => ['MMMDCCVCIV', 'MMMDCCIC'],
+ 3845 => ['MMMDCCCVL'],
+ 3846 => ['MMMDCCCVLI'],
+ 3847 => ['MMMDCCCVLII'],
+ 3848 => ['MMMDCCCVLIII'],
+ 3849 => ['MMMDCCCVLIV', 'MMMDCCCIL'],
+ 3895 => ['MMMDCCCVC'],
+ 3896 => ['MMMDCCCVCI'],
+ 3897 => ['MMMDCCCVCII'],
+ 3898 => ['MMMDCCCVCIII'],
+ 3899 => ['MMMDCCCVCIV', 'MMMDCCCIC'],
+ 3945 => ['MMMCMVL'],
+ 3946 => ['MMMCMVLI'],
+ 3947 => ['MMMCMVLII'],
+ 3948 => ['MMMCMVLIII'],
+ 3949 => ['MMMCMVLIV', 'MMMCMIL'],
+ 3950 => ['MMMLM'],
+ 3951 => ['MMMLMI'],
+ 3952 => ['MMMLMII'],
+ 3953 => ['MMMLMIII'],
+ 3954 => ['MMMLMIV'],
+ 3955 => ['MMMLMV'],
+ 3956 => ['MMMLMVI'],
+ 3957 => ['MMMLMVII'],
+ 3958 => ['MMMLMVIII'],
+ 3959 => ['MMMLMIX'],
+ 3960 => ['MMMLMX'],
+ 3961 => ['MMMLMXI'],
+ 3962 => ['MMMLMXII'],
+ 3963 => ['MMMLMXIII'],
+ 3964 => ['MMMLMXIV'],
+ 3965 => ['MMMLMXV'],
+ 3966 => ['MMMLMXVI'],
+ 3967 => ['MMMLMXVII'],
+ 3968 => ['MMMLMXVIII'],
+ 3969 => ['MMMLMXIX'],
+ 3970 => ['MMMLMXX'],
+ 3971 => ['MMMLMXXI'],
+ 3972 => ['MMMLMXXII'],
+ 3973 => ['MMMLMXXIII'],
+ 3974 => ['MMMLMXXIV'],
+ 3975 => ['MMMLMXXV'],
+ 3976 => ['MMMLMXXVI'],
+ 3977 => ['MMMLMXXVII'],
+ 3978 => ['MMMLMXXVIII'],
+ 3979 => ['MMMLMXXIX'],
+ 3980 => ['MMMLMXXX'],
+ 3981 => ['MMMLMXXXI'],
+ 3982 => ['MMMLMXXXII'],
+ 3983 => ['MMMLMXXXIII'],
+ 3984 => ['MMMLMXXXIV'],
+ 3985 => ['MMMLMXXXV'],
+ 3986 => ['MMMLMXXXVI'],
+ 3987 => ['MMMLMXXXVII'],
+ 3988 => ['MMMLMXXXVIII'],
+ 3989 => ['MMMLMXXXIX'],
+ 3990 => ['MMMLMXL', 'MMMXM'],
+ 3991 => ['MMMLMXLI', 'MMMXMI'],
+ 3992 => ['MMMLMXLII', 'MMMXMII'],
+ 3993 => ['MMMLMXLIII', 'MMMXMIII'],
+ 3994 => ['MMMLMXLIV', 'MMMXMIV'],
+ 3995 => ['MMMLMVL', 'MMMXMV', 'MMMVM'],
+ 3996 => ['MMMLMVLI', 'MMMXMVI', 'MMMVMI'],
+ 3997 => ['MMMLMVLII', 'MMMXMVII', 'MMMVMII'],
+ 3998 => ['MMMLMVLIII', 'MMMXMVIII', 'MMMVMIII'],
+ 3999 => ['MMMLMVLIV', 'MMMXMIX', 'MMMVMIV', 'MMMIM'],
+ ];
+
+ private const THOUSANDS = ['', 'M', 'MM', 'MMM'];
+ private const HUNDREDS = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
+ private const TENS = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
+ private const ONES = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
+ const MAX_ROMAN_VALUE = 3999;
+ const MAX_ROMAN_STYLE = 4;
+
+ private static function valueOk(int $aValue, int $style): string
+ {
+ $origValue = $aValue;
+ $m = \intdiv($aValue, 1000);
+ $aValue %= 1000;
+ $c = \intdiv($aValue, 100);
+ $aValue %= 100;
+ $t = \intdiv($aValue, 10);
+ $aValue %= 10;
+ $result = self::THOUSANDS[$m] . self::HUNDREDS[$c] . self::TENS[$t] . self::ONES[$aValue];
+ if ($style > 0) {
+ if (array_key_exists($origValue, self::VALUES)) {
+ $arr = self::VALUES[$origValue];
+ $idx = min($style, count($arr)) - 1;
+ $result = $arr[$idx];
+ }
+ }
+
+ return $result;
+ }
+
+ private static function styleOk(int $aValue, int $style): string
+ {
+ return ($aValue < 0 || $aValue > self::MAX_ROMAN_VALUE) ? Functions::VALUE() : self::valueOk($aValue, $style);
+ }
+
+ public static function calculateRoman(int $aValue, int $style): string
+ {
+ return ($style < 0 || $style > self::MAX_ROMAN_STYLE) ? Functions::VALUE() : self::styleOk($aValue, $style);
+ }
+
+ /**
+ * ROMAN.
+ *
+ * Converts a number to Roman numeral
+ *
+ * @param mixed $aValue Number to convert
+ * @param mixed $style Number indicating one of five possible forms
+ *
+ * @return string Roman numeral, or a string containing an error
+ */
+ public static function evaluate($aValue, $style = 0)
+ {
+ try {
+ $aValue = Helpers::validateNumericNullBool($aValue);
+ if (is_bool($style)) {
+ $style = $style ? 0 : 4;
+ }
+ $style = Helpers::validateNumericNullSubstitution($style, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::calculateRoman((int) $aValue, (int) $style);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
new file mode 100644
index 00000000000..2ddde90008e
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
@@ -0,0 +1,179 @@
+getMessage();
+ }
+
+ return round($number, (int) $precision);
+ }
+
+ /**
+ * ROUNDUP.
+ *
+ * Rounds a number up to a specified number of decimal places
+ *
+ * @param float $number Number to round
+ * @param int $digits Number of digits to which you want to round $number
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function up($number, $digits)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ if ($number < 0.0) {
+ return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+ }
+
+ return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+ }
+
+ /**
+ * ROUNDDOWN.
+ *
+ * Rounds a number down to a specified number of decimal places
+ *
+ * @param float $number Number to round
+ * @param int $digits Number of digits to which you want to round $number
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function down($number, $digits)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ if ($number < 0.0) {
+ return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+ }
+
+ return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+ }
+
+ /**
+ * MROUND.
+ *
+ * Rounds a number to the nearest multiple of a specified value
+ *
+ * @param mixed $number Expect float. Number to round.
+ * @param mixed $multiple Expect int. Multiple to which you want to round.
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function multiple($number, $multiple)
+ {
+ try {
+ $number = Helpers::validateNumericNullSubstitution($number, 0);
+ $multiple = Helpers::validateNumericNullSubstitution($multiple, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0 || $multiple == 0) {
+ return 0;
+ }
+ if ((Helpers::returnSign($number)) == (Helpers::returnSign($multiple))) {
+ $multiplier = 1 / $multiple;
+
+ return round($number * $multiplier) / $multiplier;
+ }
+
+ return Functions::NAN();
+ }
+
+ /**
+ * EVEN.
+ *
+ * Returns number rounded up to the nearest even integer.
+ * You can use this function for processing items that come in twos. For example,
+ * a packing crate accepts rows of one or two items. The crate is full when
+ * the number of items, rounded up to the nearest two, matches the crate's
+ * capacity.
+ *
+ * Excel Function:
+ * EVEN(number)
+ *
+ * @param float $number Number to round
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function even($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::getEven($number);
+ }
+
+ /**
+ * ODD.
+ *
+ * Returns number rounded up to the nearest odd integer.
+ *
+ * @param float $number Number to round
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function odd($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $significance = Helpers::returnSign($number);
+ if ($significance == 0) {
+ return 1;
+ }
+
+ $result = ceil($number / $significance) * $significance;
+ if ($result == Helpers::getEven($result)) {
+ $result += $significance;
+ }
+
+ return $result;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
new file mode 100644
index 00000000000..2ada9df4ea9
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
@@ -0,0 +1,46 @@
+getMessage();
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
new file mode 100644
index 00000000000..a48cf0f9bb6
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
@@ -0,0 +1,29 @@
+getMessage();
+ }
+
+ return Helpers::returnSign($number);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
new file mode 100644
index 00000000000..8ead578e325
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
@@ -0,0 +1,49 @@
+getMessage();
+ }
+
+ return Helpers::numberOrNan(sqrt($number));
+ }
+
+ /**
+ * SQRTPI.
+ *
+ * Returns the square root of (number * pi).
+ *
+ * @param float $number Number
+ *
+ * @return float|string Square Root of Number * Pi, or a string containing an error
+ */
+ public static function pi($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullSubstitution($number, 0);
+ Helpers::validateNotNegative($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return sqrt($number * M_PI);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
new file mode 100644
index 00000000000..2edb86f7e76
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
@@ -0,0 +1,111 @@
+getWorksheet()->getRowDimension($row)->getVisible();
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ /**
+ * @param mixed $cellReference
+ * @param mixed $args
+ */
+ protected static function filterFormulaArgs($cellReference, $args): array
+ {
+ return array_filter(
+ $args,
+ function ($index) use ($cellReference) {
+ [, $row, $column] = explode('.', $index);
+ $retVal = true;
+ if ($cellReference->getWorksheet()->cellExists($column . $row)) {
+ //take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
+ $isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
+ $cellFormula = !preg_match('/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i', $cellReference->getWorksheet()->getCell($column . $row)->getValue());
+
+ $retVal = !$isFormula || $cellFormula;
+ }
+
+ return $retVal;
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ /** @var callable[] */
+ private const CALL_FUNCTIONS = [
+ 1 => [Statistical\Averages::class, 'average'],
+ [Statistical\Counts::class, 'COUNT'], // 2
+ [Statistical\Counts::class, 'COUNTA'], // 3
+ [Statistical\Maximum::class, 'max'], // 4
+ [Statistical\Minimum::class, 'min'], // 5
+ [Operations::class, 'product'], // 6
+ [Statistical\StandardDeviations::class, 'STDEV'], // 7
+ [Statistical\StandardDeviations::class, 'STDEVP'], // 8
+ [Sum::class, 'sumIgnoringStrings'], // 9
+ [Statistical\Variances::class, 'VAR'], // 10
+ [Statistical\Variances::class, 'VARP'], // 11
+ ];
+
+ /**
+ * SUBTOTAL.
+ *
+ * Returns a subtotal in a list or database.
+ *
+ * @param mixed $functionType
+ * A number 1 to 11 that specifies which function to
+ * use in calculating subtotals within a range
+ * list
+ * Numbers 101 to 111 shadow the functions of 1 to 11
+ * but ignore any values in the range that are
+ * in hidden rows
+ * @param mixed[] $args A mixed data series of values
+ *
+ * @return float|string
+ */
+ public static function evaluate($functionType, ...$args)
+ {
+ $cellReference = array_pop($args);
+ $aArgs = Functions::flattenArrayIndexed($args);
+
+ try {
+ $subtotal = (int) Helpers::validateNumericNullBool($functionType);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Calculate
+ if ($subtotal > 100) {
+ $aArgs = self::filterHiddenArgs($cellReference, $aArgs);
+ $subtotal -= 100;
+ }
+
+ $aArgs = self::filterFormulaArgs($cellReference, $aArgs);
+ if (array_key_exists($subtotal, self::CALL_FUNCTIONS)) {
+ /** @var callable */
+ $call = self::CALL_FUNCTIONS[$subtotal];
+
+ return call_user_func_array($call, $aArgs);
+ }
+
+ return Functions::VALUE();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
new file mode 100644
index 00000000000..741734d90a7
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
@@ -0,0 +1,115 @@
+ $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg) || empty($arg)) {
+ if (is_string($arg)) {
+ $arg = (int) $arg;
+ }
+ $returnValue += $arg;
+ } elseif (is_bool($arg)) {
+ $returnValue += (int) $arg;
+ } elseif (Functions::isError($arg)) {
+ return $arg;
+ // ignore non-numerics from cell, but fail as literals (except null)
+ } elseif ($arg !== null && !Functions::isCellValue($k)) {
+ return Functions::VALUE();
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * SUMPRODUCT.
+ *
+ * Excel Function:
+ * SUMPRODUCT(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function product(...$args)
+ {
+ $arrayList = $args;
+
+ $wrkArray = Functions::flattenArray(array_shift($arrayList));
+ $wrkCellCount = count($wrkArray);
+
+ for ($i = 0; $i < $wrkCellCount; ++$i) {
+ if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
+ $wrkArray[$i] = 0;
+ }
+ }
+
+ foreach ($arrayList as $matrixData) {
+ $array2 = Functions::flattenArray($matrixData);
+ $count = count($array2);
+ if ($wrkCellCount != $count) {
+ return Functions::VALUE();
+ }
+
+ foreach ($array2 as $i => $val) {
+ if ((!is_numeric($val)) || (is_string($val))) {
+ $val = 0;
+ }
+ $wrkArray[$i] *= $val;
+ }
+ }
+
+ return array_sum($wrkArray);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
new file mode 100644
index 00000000000..49fa6381123
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
@@ -0,0 +1,142 @@
+getMessage();
+ }
+
+ return $returnValue;
+ }
+
+ private static function getCount(array $array1, array $array2): int
+ {
+ $count = count($array1);
+ if ($count !== count($array2)) {
+ throw new Exception(Functions::NA());
+ }
+
+ return $count;
+ }
+
+ /**
+ * These functions accept only numeric arguments, not even strings which are numeric.
+ *
+ * @param mixed $item
+ */
+ private static function numericNotString($item): bool
+ {
+ return is_numeric($item) && !is_string($item);
+ }
+
+ /**
+ * SUMX2MY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXSquaredMinusYSquared($matrixData1, $matrixData2)
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+
+ /**
+ * SUMX2PY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXSquaredPlusYSquared($matrixData1, $matrixData2)
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+
+ /**
+ * SUMXMY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXMinusYSquared($matrixData1, $matrixData2)
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php
new file mode 100644
index 00000000000..3038e6cc0e5
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php
@@ -0,0 +1,49 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, sin($angle));
+ }
+
+ /**
+ * CSCH.
+ *
+ * Returns the hyperbolic cosecant of an angle.
+ *
+ * @param float $angle Number
+ *
+ * @return float|string The hyperbolic cosecant of the angle
+ */
+ public static function csch($angle)
+ {
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, sinh($angle));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
new file mode 100644
index 00000000000..6c69e126d73
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
@@ -0,0 +1,89 @@
+getMessage();
+ }
+
+ return cos($number);
+ }
+
+ /**
+ * COSH.
+ *
+ * Returns the result of builtin function cosh after validating args.
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return float|string hyperbolic cosine
+ */
+ public static function cosh($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return cosh($number);
+ }
+
+ /**
+ * ACOS.
+ *
+ * Returns the arccosine of a number.
+ *
+ * @param float $number Number
+ *
+ * @return float|string The arccosine of the number
+ */
+ public static function acos($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(acos($number));
+ }
+
+ /**
+ * ACOSH.
+ *
+ * Returns the arc inverse hyperbolic cosine of a number.
+ *
+ * @param float $number Number
+ *
+ * @return float|string The inverse hyperbolic cosine of the number, or an error string
+ */
+ public static function acosh($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(acosh($number));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
new file mode 100644
index 00000000000..1b796f50f65
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
@@ -0,0 +1,91 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(cos($angle), sin($angle));
+ }
+
+ /**
+ * COTH.
+ *
+ * Returns the hyperbolic cotangent of an angle.
+ *
+ * @param float $angle Number
+ *
+ * @return float|string The hyperbolic cotangent of the angle
+ */
+ public static function coth($angle)
+ {
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, tanh($angle));
+ }
+
+ /**
+ * ACOT.
+ *
+ * Returns the arccotangent of a number.
+ *
+ * @param float $number Number
+ *
+ * @return float|string The arccotangent of the number
+ */
+ public static function acot($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (M_PI / 2) - atan($number);
+ }
+
+ /**
+ * ACOTH.
+ *
+ * Returns the hyperbolic arccotangent of a number.
+ *
+ * @param float $number Number
+ *
+ * @return float|string The hyperbolic arccotangent of the number
+ */
+ public static function acoth($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = ($number === 1) ? NAN : (log(($number + 1) / ($number - 1)) / 2);
+
+ return Helpers::numberOrNan($result);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
new file mode 100644
index 00000000000..70299cb7e31
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
@@ -0,0 +1,49 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, cos($angle));
+ }
+
+ /**
+ * SECH.
+ *
+ * Returns the hyperbolic secant of an angle.
+ *
+ * @param float $angle Number
+ *
+ * @return float|string The hyperbolic secant of the angle
+ */
+ public static function sech($angle)
+ {
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, cosh($angle));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
new file mode 100644
index 00000000000..2c6a8a0718f
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
@@ -0,0 +1,89 @@
+getMessage();
+ }
+
+ return sin($angle);
+ }
+
+ /**
+ * SINH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @param mixed $angle Should be numeric
+ *
+ * @return float|string hyperbolic sine
+ */
+ public static function sinh($angle)
+ {
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return sinh($angle);
+ }
+
+ /**
+ * ASIN.
+ *
+ * Returns the arcsine of a number.
+ *
+ * @param float $number Number
+ *
+ * @return float|string The arcsine of the number
+ */
+ public static function asin($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(asin($number));
+ }
+
+ /**
+ * ASINH.
+ *
+ * Returns the inverse hyperbolic sine of a number.
+ *
+ * @param float $number Number
+ *
+ * @return float|string The inverse hyperbolic sine of the number
+ */
+ public static function asinh($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(asinh($number));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
new file mode 100644
index 00000000000..6cd235fb702
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
@@ -0,0 +1,127 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(sin($angle), cos($angle));
+ }
+
+ /**
+ * TANH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @param mixed $angle Should be numeric
+ *
+ * @return float|string hyperbolic tangent
+ */
+ public static function tanh($angle)
+ {
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return tanh($angle);
+ }
+
+ /**
+ * ATAN.
+ *
+ * Returns the arctangent of a number.
+ *
+ * @param float $number Number
+ *
+ * @return float|string The arctangent of the number
+ */
+ public static function atan($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(atan($number));
+ }
+
+ /**
+ * ATANH.
+ *
+ * Returns the inverse hyperbolic tangent of a number.
+ *
+ * @param float $number Number
+ *
+ * @return float|string The inverse hyperbolic tangent of the number
+ */
+ public static function atanh($number)
+ {
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(atanh($number));
+ }
+
+ /**
+ * ATAN2.
+ *
+ * This function calculates the arc tangent of the two variables x and y. It is similar to
+ * calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
+ * to determine the quadrant of the result.
+ * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
+ * point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
+ * -pi and pi, excluding -pi.
+ *
+ * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
+ * PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
+ *
+ * Excel Function:
+ * ATAN2(xCoordinate,yCoordinate)
+ *
+ * @param mixed $xCoordinate should be float, the x-coordinate of the point
+ * @param mixed $yCoordinate should be float, the y-coordinate of the point
+ *
+ * @return float|string the inverse tangent of the specified x- and y-coordinates, or a string containing an error
+ */
+ public static function atan2($xCoordinate, $yCoordinate)
+ {
+ try {
+ $xCoordinate = Helpers::validateNumericNullBool($xCoordinate);
+ $yCoordinate = Helpers::validateNumericNullBool($yCoordinate);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($xCoordinate == 0) && ($yCoordinate == 0)) {
+ return Functions::DIV0();
+ }
+
+ return atan2($yCoordinate, $xCoordinate);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
new file mode 100644
index 00000000000..4b3670f680d
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
@@ -0,0 +1,39 @@
+getMessage();
+ }
+
+ $digits = floor($digits);
+
+ // Truncate
+ $adjust = 10 ** $digits;
+
+ if (($digits > 0) && (rtrim((string) (int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
+ return $value;
+ }
+
+ return ((int) ($value * $adjust)) / $adjust;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
index 19f40f2d4a8..d43a85f9045 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
@@ -2,564 +2,27 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use PhpOffice\PhpSpreadsheet\Shared\Trend\Trend;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Conditional;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Confidence;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Counts;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Maximum;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Minimum;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Permutations;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances;
+/**
+ * @deprecated 1.18.0
+ */
class Statistical
{
const LOG_GAMMA_X_MAX_VALUE = 2.55e305;
- const XMININ = 2.23e-308;
const EPS = 2.22e-16;
const MAX_VALUE = 1.2e308;
- const MAX_ITERATIONS = 256;
const SQRT2PI = 2.5066282746310005024157652848110452530069867406099;
- private static function checkTrendArrays(&$array1, &$array2)
- {
- if (!is_array($array1)) {
- $array1 = [$array1];
- }
- if (!is_array($array2)) {
- $array2 = [$array2];
- }
-
- $array1 = Functions::flattenArray($array1);
- $array2 = Functions::flattenArray($array2);
- foreach ($array1 as $key => $value) {
- if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
- unset($array1[$key], $array2[$key]);
- }
- }
- foreach ($array2 as $key => $value) {
- if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
- unset($array1[$key], $array2[$key]);
- }
- }
- $array1 = array_merge($array1);
- $array2 = array_merge($array2);
-
- return true;
- }
-
- /**
- * Incomplete beta function.
- *
- * @author Jaco van Kooten
- * @author Paul Meagher
- *
- * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
- *
- * @param mixed $x require 0<=x<=1
- * @param mixed $p require p>0
- * @param mixed $q require q>0
- *
- * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
- */
- private static function incompleteBeta($x, $p, $q)
- {
- if ($x <= 0.0) {
- return 0.0;
- } elseif ($x >= 1.0) {
- return 1.0;
- } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
- return 0.0;
- }
- $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
- if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
- return $beta_gam * self::betaFraction($x, $p, $q) / $p;
- }
-
- return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
- }
-
- // Function cache for logBeta function
- private static $logBetaCacheP = 0.0;
-
- private static $logBetaCacheQ = 0.0;
-
- private static $logBetaCacheResult = 0.0;
-
- /**
- * The natural logarithm of the beta function.
- *
- * @param mixed $p require p>0
- * @param mixed $q require q>0
- *
- * @return float 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
- *
- * @author Jaco van Kooten
- */
- private static function logBeta($p, $q)
- {
- if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
- self::$logBetaCacheP = $p;
- self::$logBetaCacheQ = $q;
- if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
- self::$logBetaCacheResult = 0.0;
- } else {
- self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
- }
- }
-
- return self::$logBetaCacheResult;
- }
-
- /**
- * Evaluates of continued fraction part of incomplete beta function.
- * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
- *
- * @author Jaco van Kooten
- *
- * @param mixed $x
- * @param mixed $p
- * @param mixed $q
- *
- * @return float
- */
- private static function betaFraction($x, $p, $q)
- {
- $c = 1.0;
- $sum_pq = $p + $q;
- $p_plus = $p + 1.0;
- $p_minus = $p - 1.0;
- $h = 1.0 - $sum_pq * $x / $p_plus;
- if (abs($h) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $frac = $h;
- $m = 1;
- $delta = 0.0;
- while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) {
- $m2 = 2 * $m;
- // even index for d
- $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
- $h = 1.0 + $d * $h;
- if (abs($h) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $c = 1.0 + $d / $c;
- if (abs($c) < self::XMININ) {
- $c = self::XMININ;
- }
- $frac *= $h * $c;
- // odd index for d
- $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
- $h = 1.0 + $d * $h;
- if (abs($h) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $c = 1.0 + $d / $c;
- if (abs($c) < self::XMININ) {
- $c = self::XMININ;
- }
- $delta = $h * $c;
- $frac *= $delta;
- ++$m;
- }
-
- return $frac;
- }
-
- /**
- * logGamma function.
- *
- * @version 1.1
- *
- * @author Jaco van Kooten
- *
- * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
- *
- * The natural logarithm of the gamma function.
- * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
- * Applied Mathematics Division
- * Argonne National Laboratory
- * Argonne, IL 60439
- *
- * References:
- *
- * - W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
- * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.
- * - K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.
- * - Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.
- *
- *
- *
- * From the original documentation:
- *
- *
- * This routine calculates the LOG(GAMMA) function for a positive real argument X.
- * Computation is based on an algorithm outlined in references 1 and 2.
- * The program uses rational functions that theoretically approximate LOG(GAMMA)
- * to at least 18 significant decimal digits. The approximation for X > 12 is from
- * reference 3, while approximations for X < 12.0 are similar to those in reference
- * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
- * the compiler, the intrinsic functions, and proper selection of the
- * machine-dependent constants.
- *
- *
- * Error returns:
- * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
- * The computation is believed to be free of underflow and overflow.
- *
- *
- * @return float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
- */
-
- // Function cache for logGamma
- private static $logGammaCacheResult = 0.0;
-
- private static $logGammaCacheX = 0.0;
-
- private static function logGamma($x)
- {
- // Log Gamma related constants
- static $lg_d1 = -0.5772156649015328605195174;
- static $lg_d2 = 0.4227843350984671393993777;
- static $lg_d4 = 1.791759469228055000094023;
-
- static $lg_p1 = [
- 4.945235359296727046734888,
- 201.8112620856775083915565,
- 2290.838373831346393026739,
- 11319.67205903380828685045,
- 28557.24635671635335736389,
- 38484.96228443793359990269,
- 26377.48787624195437963534,
- 7225.813979700288197698961,
- ];
- static $lg_p2 = [
- 4.974607845568932035012064,
- 542.4138599891070494101986,
- 15506.93864978364947665077,
- 184793.2904445632425417223,
- 1088204.76946882876749847,
- 3338152.967987029735917223,
- 5106661.678927352456275255,
- 3074109.054850539556250927,
- ];
- static $lg_p4 = [
- 14745.02166059939948905062,
- 2426813.369486704502836312,
- 121475557.4045093227939592,
- 2663432449.630976949898078,
- 29403789566.34553899906876,
- 170266573776.5398868392998,
- 492612579337.743088758812,
- 560625185622.3951465078242,
- ];
- static $lg_q1 = [
- 67.48212550303777196073036,
- 1113.332393857199323513008,
- 7738.757056935398733233834,
- 27639.87074403340708898585,
- 54993.10206226157329794414,
- 61611.22180066002127833352,
- 36351.27591501940507276287,
- 8785.536302431013170870835,
- ];
- static $lg_q2 = [
- 183.0328399370592604055942,
- 7765.049321445005871323047,
- 133190.3827966074194402448,
- 1136705.821321969608938755,
- 5267964.117437946917577538,
- 13467014.54311101692290052,
- 17827365.30353274213975932,
- 9533095.591844353613395747,
- ];
- static $lg_q4 = [
- 2690.530175870899333379843,
- 639388.5654300092398984238,
- 41355999.30241388052042842,
- 1120872109.61614794137657,
- 14886137286.78813811542398,
- 101680358627.2438228077304,
- 341747634550.7377132798597,
- 446315818741.9713286462081,
- ];
- static $lg_c = [
- -0.001910444077728,
- 8.4171387781295e-4,
- -5.952379913043012e-4,
- 7.93650793500350248e-4,
- -0.002777777777777681622553,
- 0.08333333333333333331554247,
- 0.0057083835261,
- ];
-
- // Rough estimate of the fourth root of logGamma_xBig
- static $lg_frtbig = 2.25e76;
- static $pnt68 = 0.6796875;
-
- if ($x == self::$logGammaCacheX) {
- return self::$logGammaCacheResult;
- }
- $y = $x;
- if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
- if ($y <= self::EPS) {
- $res = -log($y);
- } elseif ($y <= 1.5) {
- // ---------------------
- // EPS .LT. X .LE. 1.5
- // ---------------------
- if ($y < $pnt68) {
- $corr = -log($y);
- $xm1 = $y;
- } else {
- $corr = 0.0;
- $xm1 = $y - 1.0;
- }
- if ($y <= 0.5 || $y >= $pnt68) {
- $xden = 1.0;
- $xnum = 0.0;
- for ($i = 0; $i < 8; ++$i) {
- $xnum = $xnum * $xm1 + $lg_p1[$i];
- $xden = $xden * $xm1 + $lg_q1[$i];
- }
- $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
- } else {
- $xm2 = $y - 1.0;
- $xden = 1.0;
- $xnum = 0.0;
- for ($i = 0; $i < 8; ++$i) {
- $xnum = $xnum * $xm2 + $lg_p2[$i];
- $xden = $xden * $xm2 + $lg_q2[$i];
- }
- $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
- }
- } elseif ($y <= 4.0) {
- // ---------------------
- // 1.5 .LT. X .LE. 4.0
- // ---------------------
- $xm2 = $y - 2.0;
- $xden = 1.0;
- $xnum = 0.0;
- for ($i = 0; $i < 8; ++$i) {
- $xnum = $xnum * $xm2 + $lg_p2[$i];
- $xden = $xden * $xm2 + $lg_q2[$i];
- }
- $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
- } elseif ($y <= 12.0) {
- // ----------------------
- // 4.0 .LT. X .LE. 12.0
- // ----------------------
- $xm4 = $y - 4.0;
- $xden = -1.0;
- $xnum = 0.0;
- for ($i = 0; $i < 8; ++$i) {
- $xnum = $xnum * $xm4 + $lg_p4[$i];
- $xden = $xden * $xm4 + $lg_q4[$i];
- }
- $res = $lg_d4 + $xm4 * ($xnum / $xden);
- } else {
- // ---------------------------------
- // Evaluate for argument .GE. 12.0
- // ---------------------------------
- $res = 0.0;
- if ($y <= $lg_frtbig) {
- $res = $lg_c[6];
- $ysq = $y * $y;
- for ($i = 0; $i < 6; ++$i) {
- $res = $res / $ysq + $lg_c[$i];
- }
- $res /= $y;
- $corr = log($y);
- $res = $res + log(self::SQRT2PI) - 0.5 * $corr;
- $res += $y * ($corr - 1.0);
- }
- }
- } else {
- // --------------------------
- // Return for bad arguments
- // --------------------------
- $res = self::MAX_VALUE;
- }
- // ------------------------------
- // Final adjustments and return
- // ------------------------------
- self::$logGammaCacheX = $x;
- self::$logGammaCacheResult = $res;
-
- return $res;
- }
-
- //
- // Private implementation of the incomplete Gamma function
- //
- private static function incompleteGamma($a, $x)
- {
- static $max = 32;
- $summer = 0;
- for ($n = 0; $n <= $max; ++$n) {
- $divisor = $a;
- for ($i = 1; $i <= $n; ++$i) {
- $divisor *= ($a + $i);
- }
- $summer += ($x ** $n / $divisor);
- }
-
- return $x ** $a * exp(0 - $x) * $summer;
- }
-
- //
- // Private implementation of the Gamma function
- //
- private static function gamma($data)
- {
- if ($data == 0.0) {
- return 0;
- }
-
- static $p0 = 1.000000000190015;
- static $p = [
- 1 => 76.18009172947146,
- 2 => -86.50532032941677,
- 3 => 24.01409824083091,
- 4 => -1.231739572450155,
- 5 => 1.208650973866179e-3,
- 6 => -5.395239384953e-6,
- ];
-
- $y = $x = $data;
- $tmp = $x + 5.5;
- $tmp -= ($x + 0.5) * log($tmp);
-
- $summer = $p0;
- for ($j = 1; $j <= 6; ++$j) {
- $summer += ($p[$j] / ++$y);
- }
-
- return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x));
- }
-
- /*
- * inverse_ncdf.php
- * -------------------
- * begin : Friday, January 16, 2004
- * copyright : (C) 2004 Michael Nickerson
- * email : nickersonm@yahoo.com
- *
- */
- private static function inverseNcdf($p)
- {
- // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
- // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
- // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
- // I have not checked the accuracy of this implementation. Be aware that PHP
- // will truncate the coeficcients to 14 digits.
-
- // You have permission to use and distribute this function freely for
- // whatever purpose you want, but please show common courtesy and give credit
- // where credit is due.
-
- // Input paramater is $p - probability - where 0 < p < 1.
-
- // Coefficients in rational approximations
- static $a = [
- 1 => -3.969683028665376e+01,
- 2 => 2.209460984245205e+02,
- 3 => -2.759285104469687e+02,
- 4 => 1.383577518672690e+02,
- 5 => -3.066479806614716e+01,
- 6 => 2.506628277459239e+00,
- ];
-
- static $b = [
- 1 => -5.447609879822406e+01,
- 2 => 1.615858368580409e+02,
- 3 => -1.556989798598866e+02,
- 4 => 6.680131188771972e+01,
- 5 => -1.328068155288572e+01,
- ];
-
- static $c = [
- 1 => -7.784894002430293e-03,
- 2 => -3.223964580411365e-01,
- 3 => -2.400758277161838e+00,
- 4 => -2.549732539343734e+00,
- 5 => 4.374664141464968e+00,
- 6 => 2.938163982698783e+00,
- ];
-
- static $d = [
- 1 => 7.784695709041462e-03,
- 2 => 3.224671290700398e-01,
- 3 => 2.445134137142996e+00,
- 4 => 3.754408661907416e+00,
- ];
-
- // Define lower and upper region break-points.
- $p_low = 0.02425; //Use lower region approx. below this
- $p_high = 1 - $p_low; //Use upper region approx. above this
-
- if (0 < $p && $p < $p_low) {
- // Rational approximation for lower region.
- $q = sqrt(-2 * log($p));
-
- return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
- (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
- } elseif ($p_low <= $p && $p <= $p_high) {
- // Rational approximation for central region.
- $q = $p - 0.5;
- $r = $q * $q;
-
- return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
- ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
- } elseif ($p_high < $p && $p < 1) {
- // Rational approximation for upper region.
- $q = sqrt(-2 * log(1 - $p));
-
- return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
- (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
- }
- // If 0 < p < 1, return a null value
- return Functions::NULL();
- }
-
- /**
- * MS Excel does not count Booleans if passed as cell values, but they are counted if passed as literals.
- * OpenOffice Calc always counts Booleans.
- * Gnumeric never counts Booleans.
- *
- * @param mixed $arg
- * @param mixed $k
- *
- * @return int|mixed
- */
- private static function testAcceptedBoolean($arg, $k)
- {
- if (
- (is_bool($arg)) &&
- ((!Functions::isCellValue($k) && (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_EXCEL)) ||
- (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE))
- ) {
- $arg = (int) $arg;
- }
-
- return $arg;
- }
-
- /**
- * @param mixed $arg
- * @param mixed $k
- *
- * @return bool
- */
- private static function isAcceptedCountable($arg, $k)
- {
- if (
- ((is_numeric($arg)) && (!is_string($arg))) ||
- ((is_numeric($arg)) && (!Functions::isCellValue($k)) &&
- (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_GNUMERIC))
- ) {
- return true;
- }
-
- return false;
- }
-
/**
* AVEDEV.
*
@@ -569,45 +32,18 @@ class Statistical
* Excel Function:
* AVEDEV(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Averages::averageDeviations()
+ * Use the averageDeviations() method in the Statistical\Averages class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function AVEDEV(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- // Return value
- $returnValue = 0;
-
- $aMean = self::AVERAGE(...$args);
- if ($aMean === Functions::DIV0()) {
- return Functions::NAN();
- } elseif ($aMean === Functions::VALUE()) {
- return Functions::VALUE();
- }
-
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- $arg = self::testAcceptedBoolean($arg, $k);
- // Is it a numeric value?
- // Strings containing numeric values are only counted if they are string literals (not cell values)
- // and then only in MS Excel and in Open Office, not in Gnumeric
- if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
- return Functions::VALUE();
- }
- if (self::isAcceptedCountable($arg, $k)) {
- $returnValue += abs($arg - $aMean);
- ++$aCount;
- }
- }
-
- // Return
- if ($aCount === 0) {
- return Functions::DIV0();
- }
-
- return $returnValue / $aCount;
+ return Averages::averageDeviations(...$args);
}
/**
@@ -618,35 +54,18 @@ class Statistical
* Excel Function:
* AVERAGE(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Averages::average()
+ * Use the average() method in the Statistical\Averages class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function AVERAGE(...$args)
{
- $returnValue = $aCount = 0;
-
- // Loop through arguments
- foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
- $arg = self::testAcceptedBoolean($arg, $k);
- // Is it a numeric value?
- // Strings containing numeric values are only counted if they are string literals (not cell values)
- // and then only in MS Excel and in Open Office, not in Gnumeric
- if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
- return Functions::VALUE();
- }
- if (self::isAcceptedCountable($arg, $k)) {
- $returnValue += $arg;
- ++$aCount;
- }
- }
-
- // Return
- if ($aCount > 0) {
- return $returnValue / $aCount;
- }
-
- return Functions::DIV0();
+ return Averages::average(...$args);
}
/**
@@ -657,39 +76,18 @@ class Statistical
* Excel Function:
* AVERAGEA(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Averages::averageA()
+ * Use the averageA() method in the Statistical\Averages class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function AVERAGEA(...$args)
{
- $returnValue = null;
-
- $aCount = 0;
- // Loop through arguments
- foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
- if (
- (is_bool($arg)) &&
- (!Functions::isMatrixValue($k))
- ) {
- } else {
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- $returnValue += $arg;
- ++$aCount;
- }
- }
- }
-
- if ($aCount > 0) {
- return $returnValue / $aCount;
- }
-
- return Functions::DIV0();
+ return Averages::averageA(...$args);
}
/**
@@ -700,47 +98,20 @@ class Statistical
* Excel Function:
* AVERAGEIF(value1[,value2[, ...]],condition)
*
- * @param mixed $aArgs Data values
- * @param string $condition the criteria that defines which cells will be checked
- * @param mixed[] $averageArgs Data values
+ * @Deprecated 1.17.0
*
- * @return float|string
+ * @see Statistical\Conditional::AVERAGEIF()
+ * Use the AVERAGEIF() method in the Statistical\Conditional class instead
+ *
+ * @param mixed $range Data values
+ * @param string $condition the criteria that defines which cells will be checked
+ * @param mixed[] $averageRange Data values
+ *
+ * @return null|float|string
*/
- public static function AVERAGEIF($aArgs, $condition, $averageArgs = [])
+ public static function AVERAGEIF($range, $condition, $averageRange = [])
{
- $returnValue = 0;
-
- $aArgs = Functions::flattenArray($aArgs);
- $averageArgs = Functions::flattenArray($averageArgs);
- if (empty($averageArgs)) {
- $averageArgs = $aArgs;
- }
- $condition = Functions::ifCondition($condition);
- $conditionIsNumeric = strpos($condition, '"') === false;
-
- // Loop through arguments
- $aCount = 0;
- foreach ($aArgs as $key => $arg) {
- if (!is_numeric($arg)) {
- if ($conditionIsNumeric) {
- continue;
- }
- $arg = Calculation::wrapResult(strtoupper($arg));
- } elseif (!$conditionIsNumeric) {
- continue;
- }
- $testCondition = '=' . $arg . $condition;
- if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- $returnValue += $averageArgs[$key];
- ++$aCount;
- }
- }
-
- if ($aCount > 0) {
- return $returnValue / $aCount;
- }
-
- return Functions::DIV0();
+ return Conditional::AVERAGEIF($range, $condition, $averageRange);
}
/**
@@ -748,6 +119,11 @@ class Statistical
*
* Returns the beta distribution.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Beta::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Beta class instead
+ *
* @param float $value Value at which you want to evaluate the distribution
* @param float $alpha Parameter to the distribution
* @param float $beta Parameter to the distribution
@@ -758,28 +134,7 @@ class Statistical
*/
public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
{
- $value = Functions::flattenSingleValue($value);
- $alpha = Functions::flattenSingleValue($alpha);
- $beta = Functions::flattenSingleValue($beta);
- $rMin = Functions::flattenSingleValue($rMin);
- $rMax = Functions::flattenSingleValue($rMax);
-
- if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
- if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
- return Functions::NAN();
- }
- if ($rMin > $rMax) {
- $tmp = $rMin;
- $rMin = $rMax;
- $rMax = $tmp;
- }
- $value -= $rMin;
- $value /= ($rMax - $rMin);
-
- return self::incompleteBeta($value, $alpha, $beta);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Beta::distribution($value, $alpha, $beta, $rMin, $rMax);
}
/**
@@ -787,6 +142,11 @@ class Statistical
*
* Returns the inverse of the Beta distribution.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Beta::inverse()
+ * Use the inverse() method in the Statistical\Distributions\Beta class instead
+ *
* @param float $probability Probability at which you want to evaluate the distribution
* @param float $alpha Parameter to the distribution
* @param float $beta Parameter to the distribution
@@ -797,44 +157,7 @@ class Statistical
*/
public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
{
- $probability = Functions::flattenSingleValue($probability);
- $alpha = Functions::flattenSingleValue($alpha);
- $beta = Functions::flattenSingleValue($beta);
- $rMin = Functions::flattenSingleValue($rMin);
- $rMax = Functions::flattenSingleValue($rMax);
-
- if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
- if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
- return Functions::NAN();
- }
- if ($rMin > $rMax) {
- $tmp = $rMin;
- $rMin = $rMax;
- $rMax = $tmp;
- }
- $a = 0;
- $b = 2;
-
- $i = 0;
- while ((($b - $a) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
- $guess = ($a + $b) / 2;
- $result = self::BETADIST($guess, $alpha, $beta);
- if (($result == $probability) || ($result == 0)) {
- $b = $a;
- } elseif ($result > $probability) {
- $b = $guess;
- } else {
- $a = $guess;
- }
- }
- if ($i == self::MAX_ITERATIONS) {
- return Functions::NA();
- }
-
- return round($rMin + $guess * ($rMax - $rMin), 12);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Beta::inverse($probability, $alpha, $beta, $rMin, $rMax);
}
/**
@@ -846,43 +169,21 @@ class Statistical
* experiment. For example, BINOMDIST can calculate the probability that two of the next three
* babies born are male.
*
- * @param float $value Number of successes in trials
- * @param float $trials Number of trials
- * @param float $probability Probability of success on each trial
- * @param bool $cumulative
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Binomial::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Binomial class instead
+ *
+ * @param mixed $value Number of successes in trials
+ * @param mixed $trials Number of trials
+ * @param mixed $probability Probability of success on each trial
+ * @param mixed $cumulative
*
* @return float|string
*/
public static function BINOMDIST($value, $trials, $probability, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $trials = Functions::flattenSingleValue($trials);
- $probability = Functions::flattenSingleValue($probability);
-
- if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
- $value = floor($value);
- $trials = floor($trials);
- if (($value < 0) || ($value > $trials)) {
- return Functions::NAN();
- }
- if (($probability < 0) || ($probability > 1)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- $summer = 0;
- for ($i = 0; $i <= $value; ++$i) {
- $summer += MathTrig::COMBIN($trials, $i) * $probability ** $i * (1 - $probability) ** ($trials - $i);
- }
-
- return $summer;
- }
-
- return MathTrig::COMBIN($trials, $value) * $probability ** $value * (1 - $probability) ** ($trials - $value);
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Binomial::distribution($value, $trials, $probability, $cumulative);
}
/**
@@ -890,6 +191,11 @@ class Statistical
*
* Returns the one-tailed probability of the chi-squared distribution.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\ChiSquared::distributionRightTail()
+ * Use the distributionRightTail() method in the Statistical\Distributions\ChiSquared class instead
+ *
* @param float $value Value for the function
* @param float $degrees degrees of freedom
*
@@ -897,26 +203,7 @@ class Statistical
*/
public static function CHIDIST($value, $degrees)
{
- $value = Functions::flattenSingleValue($value);
- $degrees = Functions::flattenSingleValue($degrees);
-
- if ((is_numeric($value)) && (is_numeric($degrees))) {
- $degrees = floor($degrees);
- if ($degrees < 1) {
- return Functions::NAN();
- }
- if ($value < 0) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- return 1;
- }
-
- return Functions::NAN();
- }
-
- return 1 - (self::incompleteGamma($degrees / 2, $value / 2) / self::gamma($degrees / 2));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\ChiSquared::distributionRightTail($value, $degrees);
}
/**
@@ -924,6 +211,11 @@ class Statistical
*
* Returns the one-tailed probability of the chi-squared distribution.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\ChiSquared::inverseRightTail()
+ * Use the inverseRightTail() method in the Statistical\Distributions\ChiSquared class instead
+ *
* @param float $probability Probability for the function
* @param float $degrees degrees of freedom
*
@@ -931,52 +223,7 @@ class Statistical
*/
public static function CHIINV($probability, $degrees)
{
- $probability = Functions::flattenSingleValue($probability);
- $degrees = Functions::flattenSingleValue($degrees);
-
- if ((is_numeric($probability)) && (is_numeric($degrees))) {
- $degrees = floor($degrees);
-
- $xLo = 100;
- $xHi = 0;
-
- $x = $xNew = 1;
- $dx = 1;
- $i = 0;
-
- while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
- // Apply Newton-Raphson step
- $result = 1 - (self::incompleteGamma($degrees / 2, $x / 2) / self::gamma($degrees / 2));
- $error = $result - $probability;
- if ($error == 0.0) {
- $dx = 0;
- } elseif ($error < 0.0) {
- $xLo = $x;
- } else {
- $xHi = $x;
- }
- // Avoid division by zero
- if ($result != 0.0) {
- $dx = $error / $result;
- $xNew = $x - $dx;
- }
- // If the NR fails to converge (which for example may be the
- // case if the initial guess is too rough) we apply a bisection
- // step to determine a more narrow interval around the root.
- if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
- $xNew = ($xLo + $xHi) / 2;
- $dx = $xNew - $x;
- }
- $x = $xNew;
- }
- if ($i == self::MAX_ITERATIONS) {
- return Functions::NA();
- }
-
- return round($x, 12);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\ChiSquared::inverseRightTail($probability, $degrees);
}
/**
@@ -984,6 +231,11 @@ class Statistical
*
* Returns the confidence interval for a population mean
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Confidence::CONFIDENCE()
+ * Use the CONFIDENCE() method in the Statistical\Confidence class instead
+ *
* @param float $alpha
* @param float $stdDev Standard Deviation
* @param float $size
@@ -992,23 +244,7 @@ class Statistical
*/
public static function CONFIDENCE($alpha, $stdDev, $size)
{
- $alpha = Functions::flattenSingleValue($alpha);
- $stdDev = Functions::flattenSingleValue($stdDev);
- $size = Functions::flattenSingleValue($size);
-
- if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
- $size = floor($size);
- if (($alpha <= 0) || ($alpha >= 1)) {
- return Functions::NAN();
- }
- if (($stdDev <= 0) || ($size < 1)) {
- return Functions::NAN();
- }
-
- return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
- }
-
- return Functions::VALUE();
+ return Confidence::CONFIDENCE($alpha, $stdDev, $size);
}
/**
@@ -1016,6 +252,11 @@ class Statistical
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::CORREL()
+ * Use the CORREL() method in the Statistical\Trends class instead
+ *
* @param mixed $yValues array of mixed Data Series Y
* @param null|mixed $xValues array of mixed Data Series X
*
@@ -1023,24 +264,7 @@ class Statistical
*/
public static function CORREL($yValues, $xValues = null)
{
- if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
- return Functions::VALUE();
- }
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getCorrelation();
+ return Trends::CORREL($xValues, $yValues);
}
/**
@@ -1051,27 +275,18 @@ class Statistical
* Excel Function:
* COUNT(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Counts::COUNT()
+ * Use the COUNT() method in the Statistical\Counts class instead
+ *
* @param mixed ...$args Data values
*
* @return int
*/
public static function COUNT(...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArrayIndexed($args);
- foreach ($aArgs as $k => $arg) {
- $arg = self::testAcceptedBoolean($arg, $k);
- // Is it a numeric value?
- // Strings containing numeric values are only counted if they are string literals (not cell values)
- // and then only in MS Excel and in Open Office, not in Gnumeric
- if (self::isAcceptedCountable($arg, $k)) {
- ++$returnValue;
- }
- }
-
- return $returnValue;
+ return Counts::COUNT(...$args);
}
/**
@@ -1082,24 +297,18 @@ class Statistical
* Excel Function:
* COUNTA(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Counts::COUNTA()
+ * Use the COUNTA() method in the Statistical\Counts class instead
+ *
* @param mixed ...$args Data values
*
* @return int
*/
public static function COUNTA(...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArrayIndexed($args);
- foreach ($aArgs as $k => $arg) {
- // Nulls are counted if literals, but not if cell values
- if ($arg !== null || (!Functions::isCellValue($k))) {
- ++$returnValue;
- }
- }
-
- return $returnValue;
+ return Counts::COUNTA(...$args);
}
/**
@@ -1110,24 +319,18 @@ class Statistical
* Excel Function:
* COUNTBLANK(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Counts::COUNTBLANK()
+ * Use the COUNTBLANK() method in the Statistical\Counts class instead
+ *
* @param mixed ...$args Data values
*
* @return int
*/
public static function COUNTBLANK(...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a blank cell?
- if (($arg === null) || ((is_string($arg)) && ($arg == ''))) {
- ++$returnValue;
- }
- }
-
- return $returnValue;
+ return Counts::COUNTBLANK(...$args);
}
/**
@@ -1136,38 +339,21 @@ class Statistical
* Counts the number of cells that contain numbers within the list of arguments
*
* Excel Function:
- * COUNTIF(value1[,value2[, ...]],condition)
+ * COUNTIF(range,condition)
*
- * @param mixed $aArgs Data values
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Conditional::COUNTIF()
+ * Use the COUNTIF() method in the Statistical\Conditional class instead
+ *
+ * @param mixed $range Data values
* @param string $condition the criteria that defines which cells will be counted
*
* @return int
*/
- public static function COUNTIF($aArgs, $condition)
+ public static function COUNTIF($range, $condition)
{
- $returnValue = 0;
-
- $aArgs = Functions::flattenArray($aArgs);
- $condition = Functions::ifCondition($condition);
- $conditionIsNumeric = strpos($condition, '"') === false;
- // Loop through arguments
- foreach ($aArgs as $arg) {
- if (!is_numeric($arg)) {
- if ($conditionIsNumeric) {
- continue;
- }
- $arg = Calculation::wrapResult(strtoupper($arg));
- } elseif (!$conditionIsNumeric) {
- continue;
- }
- $testCondition = '=' . $arg . $condition;
- if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is it a value within our criteria
- ++$returnValue;
- }
- }
-
- return $returnValue;
+ return Conditional::COUNTIF($range, $condition);
}
/**
@@ -1178,66 +364,18 @@ class Statistical
* Excel Function:
* COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…)
*
- * @param mixed $args Criterias
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Conditional::COUNTIFS()
+ * Use the COUNTIFS() method in the Statistical\Conditional class instead
+ *
+ * @param mixed $args Pairs of Ranges and Criteria
*
* @return int
*/
public static function COUNTIFS(...$args)
{
- $arrayList = $args;
-
- // Return value
- $returnValue = 0;
-
- if (empty($arrayList)) {
- return $returnValue;
- }
-
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each arg and see if arguments and conditions are true
- foreach (array_keys($aArgsArray[0]) as $index) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $conditionIsNumeric = strpos($condition, '"') === false;
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- if ($conditionIsNumeric) {
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- $arg = Calculation::wrapResult(strtoupper($arg));
- } elseif (!$conditionIsNumeric) {
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- ++$returnValue;
- }
- }
-
- // Return
- return $returnValue;
+ return Conditional::COUNTIFS(...$args);
}
/**
@@ -1245,6 +383,11 @@ class Statistical
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::COVAR()
+ * Use the COVAR() method in the Statistical\Trends class instead
+ *
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues array of mixed Data Series X
*
@@ -1252,21 +395,7 @@ class Statistical
*/
public static function COVAR($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getCovariance();
+ return Trends::COVAR($yValues, $xValues);
}
/**
@@ -1277,123 +406,20 @@ class Statistical
*
* See https://support.microsoft.com/en-us/help/828117/ for details of the algorithm used
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Binomial::inverse()
+ * Use the inverse() method in the Statistical\Distributions\Binomial class instead
+ *
* @param float $trials number of Bernoulli trials
* @param float $probability probability of a success on each trial
* @param float $alpha criterion value
*
* @return int|string
- *
- * @TODO Warning. This implementation differs from the algorithm detailed on the MS
- * web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
- * This eliminates a potential endless loop error, but may have an adverse affect on the
- * accuracy of the function (although all my tests have so far returned correct results).
*/
public static function CRITBINOM($trials, $probability, $alpha)
{
- $trials = floor(Functions::flattenSingleValue($trials));
- $probability = Functions::flattenSingleValue($probability);
- $alpha = Functions::flattenSingleValue($alpha);
-
- if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
- $trials = (int) $trials;
- if ($trials < 0) {
- return Functions::NAN();
- } elseif (($probability < 0.0) || ($probability > 1.0)) {
- return Functions::NAN();
- } elseif (($alpha < 0.0) || ($alpha > 1.0)) {
- return Functions::NAN();
- }
-
- if ($alpha <= 0.5) {
- $t = sqrt(log(1 / ($alpha * $alpha)));
- $trialsApprox = 0 - ($t + (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t));
- } else {
- $t = sqrt(log(1 / (1 - $alpha) ** 2));
- $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
- }
-
- $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
- if ($Guess < 0) {
- $Guess = 0;
- } elseif ($Guess > $trials) {
- $Guess = $trials;
- }
-
- $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
- $EssentiallyZero = 10e-12;
-
- $m = floor($trials * $probability);
- ++$TotalUnscaledProbability;
- if ($m == $Guess) {
- ++$UnscaledPGuess;
- }
- if ($m <= $Guess) {
- ++$UnscaledCumPGuess;
- }
-
- $PreviousValue = 1;
- $Done = false;
- $k = $m + 1;
- while ((!$Done) && ($k <= $trials)) {
- $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
- $TotalUnscaledProbability += $CurrentValue;
- if ($k == $Guess) {
- $UnscaledPGuess += $CurrentValue;
- }
- if ($k <= $Guess) {
- $UnscaledCumPGuess += $CurrentValue;
- }
- if ($CurrentValue <= $EssentiallyZero) {
- $Done = true;
- }
- $PreviousValue = $CurrentValue;
- ++$k;
- }
-
- $PreviousValue = 1;
- $Done = false;
- $k = $m - 1;
- while ((!$Done) && ($k >= 0)) {
- $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
- $TotalUnscaledProbability += $CurrentValue;
- if ($k == $Guess) {
- $UnscaledPGuess += $CurrentValue;
- }
- if ($k <= $Guess) {
- $UnscaledCumPGuess += $CurrentValue;
- }
- if ($CurrentValue <= $EssentiallyZero) {
- $Done = true;
- }
- $PreviousValue = $CurrentValue;
- --$k;
- }
-
- $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
- $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
-
- $CumPGuessMinus1 = $CumPGuess - 1;
-
- while (true) {
- if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
- return $Guess;
- } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
- $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
- $CumPGuessMinus1 = $CumPGuess;
- $CumPGuess = $CumPGuess + $PGuessPlus1;
- $PGuess = $PGuessPlus1;
- ++$Guess;
- } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
- $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
- $CumPGuess = $CumPGuessMinus1;
- $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
- $PGuess = $PGuessMinus1;
- --$Guess;
- }
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Binomial::inverse($trials, $probability, $alpha);
}
/**
@@ -1404,48 +430,18 @@ class Statistical
* Excel Function:
* DEVSQ(value1[,value2[, ...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Deviations::sumSquares()
+ * Use the sumSquares() method in the Statistical\Deviations class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function DEVSQ(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- // Return value
- $returnValue = null;
-
- $aMean = self::AVERAGE($aArgs);
- if ($aMean != Functions::DIV0()) {
- $aCount = -1;
- foreach ($aArgs as $k => $arg) {
- // Is it a numeric value?
- if (
- (is_bool($arg)) &&
- ((!Functions::isCellValue($k)) ||
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
- ) {
- $arg = (int) $arg;
- }
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = ($arg - $aMean) ** 2;
- } else {
- $returnValue += ($arg - $aMean) ** 2;
- }
- ++$aCount;
- }
- }
-
- // Return
- if ($returnValue === null) {
- return Functions::NAN();
- }
-
- return $returnValue;
- }
-
- return Functions::NA();
+ return Statistical\Deviations::sumSquares(...$args);
}
/**
@@ -1455,6 +451,11 @@ class Statistical
* such as how long an automated bank teller takes to deliver cash. For example, you can
* use EXPONDIST to determine the probability that the process takes at most 1 minute.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Exponential::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Exponential class instead
+ *
* @param float $value Value of the function
* @param float $lambda The parameter value
* @param bool $cumulative
@@ -1463,34 +464,7 @@ class Statistical
*/
public static function EXPONDIST($value, $lambda, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $lambda = Functions::flattenSingleValue($lambda);
- $cumulative = Functions::flattenSingleValue($cumulative);
-
- if ((is_numeric($value)) && (is_numeric($lambda))) {
- if (($value < 0) || ($lambda < 0)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- return 1 - exp(0 - $value * $lambda);
- }
-
- return $lambda * exp(0 - $value * $lambda);
- }
- }
-
- return Functions::VALUE();
- }
-
- private static function betaFunction($a, $b)
- {
- return (self::gamma($a) * self::gamma($b)) / self::gamma($a + $b);
- }
-
- private static function regularizedIncompleteBeta($value, $a, $b)
- {
- return self::incompleteBeta($value, $a, $b) / self::betaFunction($a, $b);
+ return Statistical\Distributions\Exponential::distribution($value, $lambda, $cumulative);
}
/**
@@ -1501,6 +475,11 @@ class Statistical
* For example, you can examine the test scores of men and women entering high school, and determine
* if the variability in the females is different from that found in the males.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\F::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Exponential class instead
+ *
* @param float $value Value of the function
* @param int $u The numerator degrees of freedom
* @param int $v The denominator degrees of freedom
@@ -1511,32 +490,7 @@ class Statistical
*/
public static function FDIST2($value, $u, $v, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $u = Functions::flattenSingleValue($u);
- $v = Functions::flattenSingleValue($v);
- $cumulative = Functions::flattenSingleValue($cumulative);
-
- if (is_numeric($value) && is_numeric($u) && is_numeric($v)) {
- if ($value < 0 || $u < 1 || $v < 1) {
- return Functions::NAN();
- }
-
- $cumulative = (bool) $cumulative;
- $u = (int) $u;
- $v = (int) $v;
-
- if ($cumulative) {
- $adjustedValue = ($u * $value) / ($u * $value + $v);
-
- return self::incompleteBeta($adjustedValue, $u / 2, $v / 2);
- }
-
- return (self::gamma(($v + $u) / 2) / (self::gamma($u / 2) * self::gamma($v / 2))) *
- (($u / $v) ** ($u / 2)) *
- (($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2)));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\F::distribution($value, $u, $v, $cumulative);
}
/**
@@ -1546,23 +500,18 @@ class Statistical
* is normally distributed rather than skewed. Use this function to perform hypothesis
* testing on the correlation coefficient.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Fisher::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Fisher class instead
+ *
* @param float $value
*
* @return float|string
*/
public static function FISHER($value)
{
- $value = Functions::flattenSingleValue($value);
-
- if (is_numeric($value)) {
- if (($value <= -1) || ($value >= 1)) {
- return Functions::NAN();
- }
-
- return 0.5 * log((1 + $value) / (1 - $value));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Fisher::distribution($value);
}
/**
@@ -1572,19 +521,18 @@ class Statistical
* analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
* FISHERINV(y) = x.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Fisher::inverse()
+ * Use the inverse() method in the Statistical\Distributions\Fisher class instead
+ *
* @param float $value
*
* @return float|string
*/
public static function FISHERINV($value)
{
- $value = Functions::flattenSingleValue($value);
-
- if (is_numeric($value)) {
- return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Fisher::inverse($value);
}
/**
@@ -1592,6 +540,11 @@ class Statistical
*
* Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::FORECAST()
+ * Use the FORECAST() method in the Statistical\Trends class instead
+ *
* @param float $xValue Value of X for which we want to find Y
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues of mixed Data Series X
@@ -1600,30 +553,18 @@ class Statistical
*/
public static function FORECAST($xValue, $yValues, $xValues)
{
- $xValue = Functions::flattenSingleValue($xValue);
- if (!is_numeric($xValue)) {
- return Functions::VALUE();
- } elseif (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getValueOfYForX($xValue);
+ return Trends::FORECAST($xValue, $yValues, $xValues);
}
/**
* GAMMA.
*
- * Return the gamma function value.
+ * Returns the gamma function value.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Gamma::gamma()
+ * Use the gamma() method in the Statistical\Distributions\Gamma class instead
*
* @param float $value
*
@@ -1631,14 +572,7 @@ class Statistical
*/
public static function GAMMAFunction($value)
{
- $value = Functions::flattenSingleValue($value);
- if (!is_numeric($value)) {
- return Functions::VALUE();
- } elseif ((((int) $value) == ((float) $value)) && $value <= 0.0) {
- return Functions::NAN();
- }
-
- return self::gamma($value);
+ return Statistical\Distributions\Gamma::gamma($value);
}
/**
@@ -1646,6 +580,11 @@ class Statistical
*
* Returns the gamma distribution.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Gamma::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Gamma class instead
+ *
* @param float $value Value at which you want to evaluate the distribution
* @param float $a Parameter to the distribution
* @param float $b Parameter to the distribution
@@ -1655,24 +594,7 @@ class Statistical
*/
public static function GAMMADIST($value, $a, $b, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $a = Functions::flattenSingleValue($a);
- $b = Functions::flattenSingleValue($b);
-
- if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
- if (($value < 0) || ($a <= 0) || ($b <= 0)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- return self::incompleteGamma($a, $value / $b) / self::gamma($a);
- }
-
- return (1 / ($b ** $a * self::gamma($a))) * $value ** ($a - 1) * exp(0 - ($value / $b));
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Gamma::distribution($value, $a, $b, $cumulative);
}
/**
@@ -1680,6 +602,11 @@ class Statistical
*
* Returns the inverse of the Gamma distribution.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Gamma::inverse()
+ * Use the inverse() method in the Statistical\Distributions\Gamma class instead
+ *
* @param float $probability Probability at which you want to evaluate the distribution
* @param float $alpha Parameter to the distribution
* @param float $beta Parameter to the distribution
@@ -1688,53 +615,7 @@ class Statistical
*/
public static function GAMMAINV($probability, $alpha, $beta)
{
- $probability = Functions::flattenSingleValue($probability);
- $alpha = Functions::flattenSingleValue($alpha);
- $beta = Functions::flattenSingleValue($beta);
-
- if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
- if (($alpha <= 0) || ($beta <= 0) || ($probability < 0) || ($probability > 1)) {
- return Functions::NAN();
- }
-
- $xLo = 0;
- $xHi = $alpha * $beta * 5;
-
- $x = $xNew = 1;
- $dx = 1024;
- $i = 0;
-
- while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
- // Apply Newton-Raphson step
- $error = self::GAMMADIST($x, $alpha, $beta, true) - $probability;
- if ($error < 0.0) {
- $xLo = $x;
- } else {
- $xHi = $x;
- }
- $pdf = self::GAMMADIST($x, $alpha, $beta, false);
- // Avoid division by zero
- if ($pdf != 0.0) {
- $dx = $error / $pdf;
- $xNew = $x - $dx;
- }
- // If the NR fails to converge (which for example may be the
- // case if the initial guess is too rough) we apply a bisection
- // step to determine a more narrow interval around the root.
- if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
- $xNew = ($xLo + $xHi) / 2;
- $dx = $xNew - $x;
- }
- $x = $xNew;
- }
- if ($i == self::MAX_ITERATIONS) {
- return Functions::NA();
- }
-
- return $x;
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Gamma::inverse($probability, $alpha, $beta);
}
/**
@@ -1742,23 +623,18 @@ class Statistical
*
* Returns the natural logarithm of the gamma function.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Gamma::ln()
+ * Use the ln() method in the Statistical\Distributions\Gamma class instead
+ *
* @param float $value
*
* @return float|string
*/
public static function GAMMALN($value)
{
- $value = Functions::flattenSingleValue($value);
-
- if (is_numeric($value)) {
- if ($value <= 0) {
- return Functions::NAN();
- }
-
- return log(self::gamma($value));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Gamma::ln($value);
}
/**
@@ -1767,18 +643,18 @@ class Statistical
* Calculates the probability that a member of a standard normal population will fall between
* the mean and z standard deviations from the mean.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\StandardNormal::gauss()
+ * Use the gauss() method in the Statistical\Distributions\StandardNormal class instead
+ *
* @param float $value
*
* @return float|string The result, or a string containing an error
*/
public static function GAUSS($value)
{
- $value = Functions::flattenSingleValue($value);
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
-
- return self::NORMDIST($value, 0, 1, true) - 0.5;
+ return Statistical\Distributions\StandardNormal::gauss($value);
}
/**
@@ -1791,23 +667,18 @@ class Statistical
* Excel Function:
* GEOMEAN(value1[,value2[, ...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Averages\Mean::geometric()
+ * Use the geometric() method in the Statistical\Averages\Mean class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function GEOMEAN(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- $aMean = MathTrig::PRODUCT($aArgs);
- if (is_numeric($aMean) && ($aMean > 0)) {
- $aCount = self::COUNT($aArgs);
- if (self::MIN($aArgs) > 0) {
- return $aMean ** (1 / $aCount);
- }
- }
-
- return Functions::NAN();
+ return Statistical\Averages\Mean::geometric(...$args);
}
/**
@@ -1815,31 +686,21 @@ class Statistical
*
* Returns values along a predicted exponential Trend
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::GROWTH()
+ * Use the GROWTH() method in the Statistical\Trends class instead
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param bool $const a logical value specifying whether to force the intersect to equal 0
*
- * @return array of float
+ * @return float[]
*/
public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
{
- $yValues = Functions::flattenArray($yValues);
- $xValues = Functions::flattenArray($xValues);
- $newValues = Functions::flattenArray($newValues);
- $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
-
- $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
- if (empty($newValues)) {
- $newValues = $bestFitExponential->getXValues();
- }
-
- $returnArray = [];
- foreach ($newValues as $xValue) {
- $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
- }
-
- return $returnArray;
+ return Trends::GROWTH($yValues, $xValues, $newValues, $const);
}
/**
@@ -1851,38 +712,18 @@ class Statistical
* Excel Function:
* HARMEAN(value1[,value2[, ...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Averages\Mean::harmonic()
+ * Use the harmonic() method in the Statistical\Averages\Mean class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function HARMEAN(...$args)
{
- // Return value
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- if (self::MIN($aArgs) < 0) {
- return Functions::NAN();
- }
- $aCount = 0;
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($arg <= 0) {
- return Functions::NAN();
- }
- $returnValue += (1 / $arg);
- ++$aCount;
- }
- }
-
- // Return
- if ($aCount > 0) {
- return 1 / ($returnValue / $aCount);
- }
-
- return Functions::NA();
+ return Statistical\Averages\Mean::harmonic(...$args);
}
/**
@@ -1891,42 +732,26 @@ class Statistical
* Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
* sample successes, given the sample size, population successes, and population size.
*
- * @param float $sampleSuccesses Number of successes in the sample
- * @param float $sampleNumber Size of the sample
- * @param float $populationSuccesses Number of successes in the population
- * @param float $populationNumber Population size
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\HyperGeometric::distribution()
+ * Use the distribution() method in the Statistical\Distributions\HyperGeometric class instead
+ *
+ * @param mixed $sampleSuccesses Number of successes in the sample
+ * @param mixed $sampleNumber Size of the sample
+ * @param mixed $populationSuccesses Number of successes in the population
+ * @param mixed $populationNumber Population size
*
* @return float|string
*/
public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber)
{
- $sampleSuccesses = Functions::flattenSingleValue($sampleSuccesses);
- $sampleNumber = Functions::flattenSingleValue($sampleNumber);
- $populationSuccesses = Functions::flattenSingleValue($populationSuccesses);
- $populationNumber = Functions::flattenSingleValue($populationNumber);
-
- if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
- $sampleSuccesses = floor($sampleSuccesses);
- $sampleNumber = floor($sampleNumber);
- $populationSuccesses = floor($populationSuccesses);
- $populationNumber = floor($populationNumber);
-
- if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
- return Functions::NAN();
- }
- if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
- return Functions::NAN();
- }
- if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
- return Functions::NAN();
- }
-
- return MathTrig::COMBIN($populationSuccesses, $sampleSuccesses) *
- MathTrig::COMBIN($populationNumber - $populationSuccesses, $sampleNumber - $sampleSuccesses) /
- MathTrig::COMBIN($populationNumber, $sampleNumber);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\HyperGeometric::distribution(
+ $sampleSuccesses,
+ $sampleNumber,
+ $populationSuccesses,
+ $populationNumber
+ );
}
/**
@@ -1934,6 +759,11 @@ class Statistical
*
* Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::INTERCEPT()
+ * Use the INTERCEPT() method in the Statistical\Trends class instead
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
@@ -1941,21 +771,7 @@ class Statistical
*/
public static function INTERCEPT($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getIntersect();
+ return Trends::INTERCEPT($yValues, $xValues);
}
/**
@@ -1966,40 +782,18 @@ class Statistical
* kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
* relatively flat distribution.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Deviations::kurtosis()
+ * Use the kurtosis() method in the Statistical\Deviations class instead
+ *
* @param array ...$args Data Series
*
* @return float|string
*/
public static function KURT(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
- $mean = self::AVERAGE($aArgs);
- $stdDev = self::STDEV($aArgs);
-
- if ($stdDev > 0) {
- $count = $summer = 0;
- // Loop through arguments
- foreach ($aArgs as $k => $arg) {
- if (
- (is_bool($arg)) &&
- (!Functions::isMatrixValue($k))
- ) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $summer += (($arg - $mean) / $stdDev) ** 4;
- ++$count;
- }
- }
- }
-
- // Return
- if ($count > 3) {
- return $summer * ($count * ($count + 1) / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 / (($count - 2) * ($count - 3)));
- }
- }
-
- return Functions::DIV0();
+ return Statistical\Deviations::kurtosis(...$args);
}
/**
@@ -2011,37 +805,18 @@ class Statistical
* Excel Function:
* LARGE(value1[,value2[, ...]],entry)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Size::large()
+ * Use the large() method in the Statistical\Size class instead
+ *
* @param mixed $args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function LARGE(...$args)
{
- $aArgs = Functions::flattenArray($args);
- $entry = array_pop($aArgs);
-
- if ((is_numeric($entry)) && (!is_string($entry))) {
- $entry = (int) floor($entry);
-
- // Calculate
- $mArgs = [];
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $mArgs[] = $arg;
- }
- }
- $count = self::COUNT($mArgs);
- --$entry;
- if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
- return Functions::NAN();
- }
- rsort($mArgs);
-
- return $mArgs[$entry];
- }
-
- return Functions::VALUE();
+ return Statistical\Size::large(...$args);
}
/**
@@ -2050,6 +825,11 @@ class Statistical
* Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
* and then returns an array that describes the line.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::LINEST()
+ * Use the LINEST() method in the Statistical\Trends class instead
+ *
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param bool $const a logical value specifying whether to force the intersect to equal 0
@@ -2059,48 +839,7 @@ class Statistical
*/
public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
{
- $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
- $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
- if ($xValues === null) {
- $xValues = range(1, count(Functions::flattenArray($yValues)));
- }
-
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return 0;
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
- if ($stats) {
- return [
- [
- $bestFitLinear->getSlope(),
- $bestFitLinear->getSlopeSE(),
- $bestFitLinear->getGoodnessOfFit(),
- $bestFitLinear->getF(),
- $bestFitLinear->getSSRegression(),
- ],
- [
- $bestFitLinear->getIntersect(),
- $bestFitLinear->getIntersectSE(),
- $bestFitLinear->getStdevOfResiduals(),
- $bestFitLinear->getDFResiduals(),
- $bestFitLinear->getSSResiduals(),
- ],
- ];
- }
-
- return [
- $bestFitLinear->getSlope(),
- $bestFitLinear->getIntersect(),
- ];
+ return Trends::LINEST($yValues, $xValues, $const, $stats);
}
/**
@@ -2109,6 +848,11 @@ class Statistical
* Calculates an exponential curve that best fits the X and Y data series,
* and then returns an array that describes the line.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::LOGEST()
+ * Use the LOGEST() method in the Statistical\Trends class instead
+ *
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param bool $const a logical value specifying whether to force the intersect to equal 0
@@ -2118,54 +862,7 @@ class Statistical
*/
public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
{
- $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
- $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
- if ($xValues === null) {
- $xValues = range(1, count(Functions::flattenArray($yValues)));
- }
-
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- foreach ($yValues as $value) {
- if ($value <= 0.0) {
- return Functions::NAN();
- }
- }
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return 1;
- }
-
- $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
- if ($stats) {
- return [
- [
- $bestFitExponential->getSlope(),
- $bestFitExponential->getSlopeSE(),
- $bestFitExponential->getGoodnessOfFit(),
- $bestFitExponential->getF(),
- $bestFitExponential->getSSRegression(),
- ],
- [
- $bestFitExponential->getIntersect(),
- $bestFitExponential->getIntersectSE(),
- $bestFitExponential->getStdevOfResiduals(),
- $bestFitExponential->getDFResiduals(),
- $bestFitExponential->getSSResiduals(),
- ],
- ];
- }
-
- return [
- $bestFitExponential->getSlope(),
- $bestFitExponential->getIntersect(),
- ];
+ return Trends::LOGEST($yValues, $xValues, $const, $stats);
}
/**
@@ -2173,6 +870,11 @@ class Statistical
*
* Returns the inverse of the normal cumulative distribution
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\LogNormal::inverse()
+ * Use the inverse() method in the Statistical\Distributions\LogNormal class instead
+ *
* @param float $probability
* @param float $mean
* @param float $stdDev
@@ -2185,19 +887,7 @@ class Statistical
*/
public static function LOGINV($probability, $mean, $stdDev)
{
- $probability = Functions::flattenSingleValue($probability);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
- return Functions::NAN();
- }
-
- return exp($mean + $stdDev * self::NORMSINV($probability));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\LogNormal::inverse($probability, $mean, $stdDev);
}
/**
@@ -2206,6 +896,11 @@ class Statistical
* Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
* with parameters mean and standard_dev.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\LogNormal::cumulative()
+ * Use the cumulative() method in the Statistical\Distributions\LogNormal class instead
+ *
* @param float $value
* @param float $mean
* @param float $stdDev
@@ -2214,19 +909,7 @@ class Statistical
*/
public static function LOGNORMDIST($value, $mean, $stdDev)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if (($value <= 0) || ($stdDev <= 0)) {
- return Functions::NAN();
- }
-
- return self::NORMSDIST((log($value) - $mean) / $stdDev);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\LogNormal::cumulative($value, $mean, $stdDev);
}
/**
@@ -2235,6 +918,11 @@ class Statistical
* Returns the lognormal distribution of x, where ln(x) is normally distributed
* with parameters mean and standard_dev.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\LogNormal::distribution()
+ * Use the distribution() method in the Statistical\Distributions\LogNormal class instead
+ *
* @param float $value
* @param float $mean
* @param float $stdDev
@@ -2244,25 +932,7 @@ class Statistical
*/
public static function LOGNORMDIST2($value, $mean, $stdDev, $cumulative = false)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
- $cumulative = (bool) Functions::flattenSingleValue($cumulative);
-
- if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if (($value <= 0) || ($stdDev <= 0)) {
- return Functions::NAN();
- }
-
- if ($cumulative === true) {
- return self::NORMSDIST2((log($value) - $mean) / $stdDev, true);
- }
-
- return (1 / (sqrt(2 * M_PI) * $stdDev * $value)) *
- exp(0 - ((log($value) - $mean) ** 2 / (2 * $stdDev ** 2)));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\LogNormal::distribution($value, $mean, $stdDev, $cumulative);
}
/**
@@ -2272,32 +942,20 @@ class Statistical
* with negative numbers considered smaller than positive numbers.
*
* Excel Function:
- * MAX(value1[,value2[, ...]])
+ * max(value1[,value2[, ...]])
+ *
+ * @Deprecated 1.17.0
*
* @param mixed ...$args Data values
*
* @return float
+ *
+ *@see Statistical\Maximum::max()
+ * Use the MAX() method in the Statistical\Maximum class instead
*/
public static function MAX(...$args)
{
- $returnValue = null;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if (($returnValue === null) || ($arg > $returnValue)) {
- $returnValue = $arg;
- }
- }
- }
-
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return Maximum::max(...$args);
}
/**
@@ -2306,37 +964,20 @@ class Statistical
* Returns the greatest value in a list of arguments, including numbers, text, and logical values
*
* Excel Function:
- * MAXA(value1[,value2[, ...]])
+ * maxA(value1[,value2[, ...]])
+ *
+ * @Deprecated 1.17.0
*
* @param mixed ...$args Data values
*
* @return float
+ *
+ *@see Statistical\Maximum::maxA()
+ * Use the MAXA() method in the Statistical\Maximum class instead
*/
public static function MAXA(...$args)
{
- $returnValue = null;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- if (($returnValue === null) || ($arg > $returnValue)) {
- $returnValue = $arg;
- }
- }
- }
-
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return Maximum::maxA(...$args);
}
/**
@@ -2347,53 +988,18 @@ class Statistical
* Excel Function:
* MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Conditional::MAXIFS()
+ * Use the MAXIFS() method in the Statistical\Conditional class instead
+ *
* @param mixed $args Data range and criterias
*
* @return float
*/
public static function MAXIFS(...$args)
{
- $arrayList = $args;
-
- // Return value
- $returnValue = null;
-
- $maxArgs = Functions::flattenArray(array_shift($arrayList));
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each arg and see if arguments and conditions are true
- foreach ($maxArgs as $index => $value) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- $returnValue = $returnValue === null ? $value : max($value, $returnValue);
- }
- }
-
- // Return
- return $returnValue;
+ return Conditional::MAXIFS(...$args);
}
/**
@@ -2404,37 +1010,18 @@ class Statistical
* Excel Function:
* MEDIAN(value1[,value2[, ...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Averages::median()
+ * Use the median() method in the Statistical\Averages class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function MEDIAN(...$args)
{
- $returnValue = Functions::NAN();
-
- $mArgs = [];
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $mArgs[] = $arg;
- }
- }
-
- $mValueCount = count($mArgs);
- if ($mValueCount > 0) {
- sort($mArgs, SORT_NUMERIC);
- $mValueCount = $mValueCount / 2;
- if ($mValueCount == floor($mValueCount)) {
- $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
- } else {
- $mValueCount = floor($mValueCount);
- $returnValue = $mArgs[$mValueCount];
- }
- }
-
- return $returnValue;
+ return Statistical\Averages::median(...$args);
}
/**
@@ -2446,30 +1033,18 @@ class Statistical
* Excel Function:
* MIN(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
* @param mixed ...$args Data values
*
* @return float
+ *
+ *@see Statistical\Minimum::min()
+ * Use the min() method in the Statistical\Minimum class instead
*/
public static function MIN(...$args)
{
- $returnValue = null;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if (($returnValue === null) || ($arg < $returnValue)) {
- $returnValue = $arg;
- }
- }
- }
-
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return Minimum::min(...$args);
}
/**
@@ -2480,35 +1055,18 @@ class Statistical
* Excel Function:
* MINA(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
* @param mixed ...$args Data values
*
* @return float
+ *
+ *@see Statistical\Minimum::minA()
+ * Use the minA() method in the Statistical\Minimum class instead
*/
public static function MINA(...$args)
{
- $returnValue = null;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- if (($returnValue === null) || ($arg < $returnValue)) {
- $returnValue = $arg;
- }
- }
- }
-
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return Minimum::minA(...$args);
}
/**
@@ -2519,102 +1077,18 @@ class Statistical
* Excel Function:
* MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Conditional::MINIFS()
+ * Use the MINIFS() method in the Statistical\Conditional class instead
+ *
* @param mixed $args Data range and criterias
*
* @return float
*/
public static function MINIFS(...$args)
{
- $arrayList = $args;
-
- // Return value
- $returnValue = null;
-
- $minArgs = Functions::flattenArray(array_shift($arrayList));
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each arg and see if arguments and conditions are true
- foreach ($minArgs as $index => $value) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- $returnValue = $returnValue === null ? $value : min($value, $returnValue);
- }
- }
-
- // Return
- return $returnValue;
- }
-
- //
- // Special variant of array_count_values that isn't limited to strings and integers,
- // but can work with floating point numbers as values
- //
- private static function modeCalc($data)
- {
- $frequencyArray = [];
- $index = 0;
- $maxfreq = 0;
- $maxfreqkey = '';
- $maxfreqdatum = '';
- foreach ($data as $datum) {
- $found = false;
- ++$index;
- foreach ($frequencyArray as $key => $value) {
- if ((string) $value['value'] == (string) $datum) {
- ++$frequencyArray[$key]['frequency'];
- $freq = $frequencyArray[$key]['frequency'];
- if ($freq > $maxfreq) {
- $maxfreq = $freq;
- $maxfreqkey = $key;
- $maxfreqdatum = $datum;
- } elseif ($freq == $maxfreq) {
- if ($frequencyArray[$key]['index'] < $frequencyArray[$maxfreqkey]['index']) {
- $maxfreqkey = $key;
- $maxfreqdatum = $datum;
- }
- }
- $found = true;
-
- break;
- }
- }
- if (!$found) {
- $frequencyArray[] = [
- 'value' => $datum,
- 'frequency' => 1,
- 'index' => $index,
- ];
- }
- }
-
- if ($maxfreq <= 1) {
- return Functions::NA();
- }
-
- return $maxfreqdatum;
+ return Conditional::MINIFS(...$args);
}
/**
@@ -2625,30 +1099,18 @@ class Statistical
* Excel Function:
* MODE(value1[,value2[, ...]])
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Averages::mode()
+ * Use the mode() method in the Statistical\Averages class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function MODE(...$args)
{
- $returnValue = Functions::NA();
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
-
- $mArgs = [];
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $mArgs[] = $arg;
- }
- }
-
- if (!empty($mArgs)) {
- return self::modeCalc($mArgs);
- }
-
- return $returnValue;
+ return Statistical\Averages::mode(...$args);
}
/**
@@ -2660,34 +1122,20 @@ class Statistical
* distribution, except that the number of successes is fixed, and the number of trials is
* variable. Like the binomial, trials are assumed to be independent.
*
- * @param float $failures Number of Failures
- * @param float $successes Threshold number of Successes
- * @param float $probability Probability of success on each trial
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Binomial::negative()
+ * Use the negative() method in the Statistical\Distributions\Binomial class instead
+ *
+ * @param mixed $failures Number of Failures
+ * @param mixed $successes Threshold number of Successes
+ * @param mixed $probability Probability of success on each trial
*
* @return float|string The result, or a string containing an error
*/
public static function NEGBINOMDIST($failures, $successes, $probability)
{
- $failures = floor(Functions::flattenSingleValue($failures));
- $successes = floor(Functions::flattenSingleValue($successes));
- $probability = Functions::flattenSingleValue($probability);
-
- if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
- if (($failures < 0) || ($successes < 1)) {
- return Functions::NAN();
- } elseif (($probability < 0) || ($probability > 1)) {
- return Functions::NAN();
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- if (($failures + $successes - 1) <= 0) {
- return Functions::NAN();
- }
- }
-
- return (MathTrig::COMBIN($failures + $successes - 1, $successes - 1)) * ($probability ** $successes) * ((1 - $probability) ** $failures);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Binomial::negative($failures, $successes, $probability);
}
/**
@@ -2697,33 +1145,21 @@ class Statistical
* function has a very wide range of applications in statistics, including hypothesis
* testing.
*
- * @param float $value
- * @param float $mean Mean Value
- * @param float $stdDev Standard Deviation
- * @param bool $cumulative
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Normal::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Normal class instead
+ *
+ * @param mixed $value
+ * @param mixed $mean Mean Value
+ * @param mixed $stdDev Standard Deviation
+ * @param mixed $cumulative
*
* @return float|string The result, or a string containing an error
*/
public static function NORMDIST($value, $mean, $stdDev, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if ($stdDev < 0) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- return 0.5 * (1 + Engineering::erfVal(($value - $mean) / ($stdDev * sqrt(2))));
- }
-
- return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (($value - $mean) ** 2 / (2 * ($stdDev * $stdDev))));
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Normal::distribution($value, $mean, $stdDev, $cumulative);
}
/**
@@ -2731,30 +1167,20 @@ class Statistical
*
* Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
*
- * @param float $probability
- * @param float $mean Mean Value
- * @param float $stdDev Standard Deviation
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Normal::inverse()
+ * Use the inverse() method in the Statistical\Distributions\Normal class instead
+ *
+ * @param mixed $probability
+ * @param mixed $mean Mean Value
+ * @param mixed $stdDev Standard Deviation
*
* @return float|string The result, or a string containing an error
*/
public static function NORMINV($probability, $mean, $stdDev)
{
- $probability = Functions::flattenSingleValue($probability);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if (($probability < 0) || ($probability > 1)) {
- return Functions::NAN();
- }
- if ($stdDev < 0) {
- return Functions::NAN();
- }
-
- return (self::inverseNcdf($probability) * $stdDev) + $mean;
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Normal::inverse($probability, $mean, $stdDev);
}
/**
@@ -2764,18 +1190,18 @@ class Statistical
* a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
* table of standard normal curve areas.
*
- * @param float $value
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\StandardNormal::cumulative()
+ * Use the cumulative() method in the Statistical\Distributions\StandardNormal class instead
+ *
+ * @param mixed $value
*
* @return float|string The result, or a string containing an error
*/
public static function NORMSDIST($value)
{
- $value = Functions::flattenSingleValue($value);
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
-
- return self::NORMDIST($value, 0, 1, true);
+ return Statistical\Distributions\StandardNormal::cumulative($value);
}
/**
@@ -2785,20 +1211,19 @@ class Statistical
* a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
* table of standard normal curve areas.
*
- * @param float $value
- * @param bool $cumulative
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\StandardNormal::distribution()
+ * Use the distribution() method in the Statistical\Distributions\StandardNormal class instead
+ *
+ * @param mixed $value
+ * @param mixed $cumulative
*
* @return float|string The result, or a string containing an error
*/
public static function NORMSDIST2($value, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
- $cumulative = (bool) Functions::flattenSingleValue($cumulative);
-
- return self::NORMDIST($value, 0, 1, $cumulative);
+ return Statistical\Distributions\StandardNormal::distribution($value, $cumulative);
}
/**
@@ -2806,13 +1231,18 @@ class Statistical
*
* Returns the inverse of the standard normal cumulative distribution
*
- * @param float $value
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\StandardNormal::inverse()
+ * Use the inverse() method in the Statistical\Distributions\StandardNormal class instead
+ *
+ * @param mixed $value
*
* @return float|string The result, or a string containing an error
*/
public static function NORMSINV($value)
{
- return self::NORMINV($value, 0, 1);
+ return Statistical\Distributions\StandardNormal::inverse($value);
}
/**
@@ -2823,92 +1253,42 @@ class Statistical
* Excel Function:
* PERCENTILE(value1[,value2[, ...]],entry)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Percentiles::PERCENTILE()
+ * Use the PERCENTILE() method in the Statistical\Percentiles class instead
+ *
* @param mixed $args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function PERCENTILE(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $entry = array_pop($aArgs);
-
- if ((is_numeric($entry)) && (!is_string($entry))) {
- if (($entry < 0) || ($entry > 1)) {
- return Functions::NAN();
- }
- $mArgs = [];
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $mArgs[] = $arg;
- }
- }
- $mValueCount = count($mArgs);
- if ($mValueCount > 0) {
- sort($mArgs);
- $count = self::COUNT($mArgs);
- $index = $entry * ($count - 1);
- $iBase = floor($index);
- if ($index == $iBase) {
- return $mArgs[$index];
- }
- $iNext = $iBase + 1;
- $iProportion = $index - $iBase;
-
- return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Percentiles::PERCENTILE(...$args);
}
/**
* PERCENTRANK.
*
* Returns the rank of a value in a data set as a percentage of the data set.
+ * Note that the returned rank is simply rounded to the appropriate significant digits,
+ * rather than floored (as MS Excel), so value 3 for a value set of 1, 2, 3, 4 will return
+ * 0.667 rather than 0.666
*
- * @param float[] $valueSet An array of, or a reference to, a list of numbers
- * @param int $value the number whose rank you want to find
- * @param int $significance the number of significant digits for the returned percentage value
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Percentiles::PERCENTRANK()
+ * Use the PERCENTRANK() method in the Statistical\Percentiles class instead
+ *
+ * @param mixed $valueSet An array of, or a reference to, a list of numbers
+ * @param mixed $value the number whose rank you want to find
+ * @param mixed $significance the number of significant digits for the returned percentage value
*
* @return float|string (string if result is an error)
*/
public static function PERCENTRANK($valueSet, $value, $significance = 3)
{
- $valueSet = Functions::flattenArray($valueSet);
- $value = Functions::flattenSingleValue($value);
- $significance = ($significance === null) ? 3 : (int) Functions::flattenSingleValue($significance);
-
- foreach ($valueSet as $key => $valueEntry) {
- if (!is_numeric($valueEntry)) {
- unset($valueSet[$key]);
- }
- }
- sort($valueSet, SORT_NUMERIC);
- $valueCount = count($valueSet);
- if ($valueCount == 0) {
- return Functions::NAN();
- }
-
- $valueAdjustor = $valueCount - 1;
- if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
- return Functions::NA();
- }
-
- $pos = array_search($value, $valueSet);
- if ($pos === false) {
- $pos = 0;
- $testValue = $valueSet[0];
- while ($testValue < $value) {
- $testValue = $valueSet[++$pos];
- }
- --$pos;
- $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
- }
-
- return round($pos / $valueAdjustor, $significance);
+ return Statistical\Percentiles::PERCENTRANK($valueSet, $value, $significance);
}
/**
@@ -2920,26 +1300,19 @@ class Statistical
* combinations, for which the internal order is not significant. Use this function
* for lottery-style probability calculations.
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Permutations::PERMUT()
+ * Use the PERMUT() method in the Statistical\Permutations class instead
+ *
* @param int $numObjs Number of different objects
* @param int $numInSet Number of objects in each permutation
*
- * @return int|string Number of permutations, or a string containing an error
+ * @return float|int|string Number of permutations, or a string containing an error
*/
public static function PERMUT($numObjs, $numInSet)
{
- $numObjs = Functions::flattenSingleValue($numObjs);
- $numInSet = Functions::flattenSingleValue($numInSet);
-
- if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
- $numInSet = floor($numInSet);
- if ($numObjs < $numInSet) {
- return Functions::NAN();
- }
-
- return round(MathTrig::FACT($numObjs) / MathTrig::FACT($numObjs - $numInSet));
- }
-
- return Functions::VALUE();
+ return Permutations::PERMUT($numObjs, $numInSet);
}
/**
@@ -2949,37 +1322,20 @@ class Statistical
* is predicting the number of events over a specific time, such as the number of
* cars arriving at a toll plaza in 1 minute.
*
- * @param float $value
- * @param float $mean Mean Value
- * @param bool $cumulative
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Poisson::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Poisson class instead
+ *
+ * @param mixed $value
+ * @param mixed $mean Mean Value
+ * @param mixed $cumulative
*
* @return float|string The result, or a string containing an error
*/
public static function POISSON($value, $mean, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
-
- if ((is_numeric($value)) && (is_numeric($mean))) {
- if (($value < 0) || ($mean <= 0)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- $summer = 0;
- $floor = floor($value);
- for ($i = 0; $i <= $floor; ++$i) {
- $summer += $mean ** $i / MathTrig::FACT($i);
- }
-
- return exp(0 - $mean) * $summer;
- }
-
- return (exp(0 - $mean) * $mean ** $value) / MathTrig::FACT($value);
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Poisson::distribution($value, $mean, $cumulative);
}
/**
@@ -2990,27 +1346,18 @@ class Statistical
* Excel Function:
* QUARTILE(value1[,value2[, ...]],entry)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Percentiles::QUARTILE()
+ * Use the QUARTILE() method in the Statistical\Percentiles class instead
+ *
* @param mixed $args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function QUARTILE(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $entry = floor(array_pop($aArgs));
-
- if ((is_numeric($entry)) && (!is_string($entry))) {
- $entry /= 4;
- if (($entry < 0) || ($entry > 1)) {
- return Functions::NAN();
- }
-
- return self::PERCENTILE($aArgs, $entry);
- }
-
- return Functions::VALUE();
+ return Statistical\Percentiles::QUARTILE(...$args);
}
/**
@@ -3018,35 +1365,20 @@ class Statistical
*
* Returns the rank of a number in a list of numbers.
*
- * @param int $value the number whose rank you want to find
- * @param float[] $valueSet An array of, or a reference to, a list of numbers
- * @param int $order Order to sort the values in the value set
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Percentiles::RANK()
+ * Use the RANK() method in the Statistical\Percentiles class instead
+ *
+ * @param mixed $value the number whose rank you want to find
+ * @param mixed $valueSet An array of, or a reference to, a list of numbers
+ * @param mixed $order Order to sort the values in the value set
*
* @return float|string The result, or a string containing an error
*/
public static function RANK($value, $valueSet, $order = 0)
{
- $value = Functions::flattenSingleValue($value);
- $valueSet = Functions::flattenArray($valueSet);
- $order = ($order === null) ? 0 : (int) Functions::flattenSingleValue($order);
-
- foreach ($valueSet as $key => $valueEntry) {
- if (!is_numeric($valueEntry)) {
- unset($valueSet[$key]);
- }
- }
-
- if ($order == 0) {
- rsort($valueSet, SORT_NUMERIC);
- } else {
- sort($valueSet, SORT_NUMERIC);
- }
- $pos = array_search($value, $valueSet);
- if ($pos === false) {
- return Functions::NA();
- }
-
- return ++$pos;
+ return Statistical\Percentiles::RANK($value, $valueSet, $order);
}
/**
@@ -3054,6 +1386,11 @@ class Statistical
*
* Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::RSQ()
+ * Use the RSQ() method in the Statistical\Trends class instead
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
@@ -3061,21 +1398,7 @@ class Statistical
*/
public static function RSQ($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getGoodnessOfFit();
+ return Trends::RSQ($yValues, $xValues);
}
/**
@@ -3086,37 +1409,18 @@ class Statistical
* asymmetric tail extending toward more positive values. Negative skewness indicates a
* distribution with an asymmetric tail extending toward more negative values.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Deviations::skew()
+ * Use the skew() method in the Statistical\Deviations class instead
+ *
* @param array ...$args Data Series
*
* @return float|string The result, or a string containing an error
*/
public static function SKEW(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
- $mean = self::AVERAGE($aArgs);
- $stdDev = self::STDEV($aArgs);
-
- $count = $summer = 0;
- // Loop through arguments
- foreach ($aArgs as $k => $arg) {
- if (
- (is_bool($arg)) &&
- (!Functions::isMatrixValue($k))
- ) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $summer += (($arg - $mean) / $stdDev) ** 3;
- ++$count;
- }
- }
- }
-
- if ($count > 2) {
- return $summer * ($count / (($count - 1) * ($count - 2)));
- }
-
- return Functions::DIV0();
+ return Statistical\Deviations::skew(...$args);
}
/**
@@ -3124,6 +1428,11 @@ class Statistical
*
* Returns the slope of the linear regression line through data points in known_y's and known_x's.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::SLOPE()
+ * Use the SLOPE() method in the Statistical\Trends class instead
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
@@ -3131,21 +1440,7 @@ class Statistical
*/
public static function SLOPE($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getSlope();
+ return Trends::SLOPE($yValues, $xValues);
}
/**
@@ -3157,38 +1452,18 @@ class Statistical
* Excel Function:
* SMALL(value1[,value2[, ...]],entry)
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Size::small()
+ * Use the small() method in the Statistical\Size class instead
+ *
* @param mixed $args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function SMALL(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $entry = array_pop($aArgs);
-
- if ((is_numeric($entry)) && (!is_string($entry))) {
- $entry = (int) floor($entry);
-
- $mArgs = [];
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $mArgs[] = $arg;
- }
- }
- $count = self::COUNT($mArgs);
- --$entry;
- if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
- return Functions::NAN();
- }
- sort($mArgs);
-
- return $mArgs[$entry];
- }
-
- return Functions::VALUE();
+ return Statistical\Size::small(...$args);
}
/**
@@ -3196,6 +1471,11 @@ class Statistical
*
* Returns a normalized value from a distribution characterized by mean and standard_dev.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Standardize::execute()
+ * Use the execute() method in the Statistical\Standardize class instead
+ *
* @param float $value Value to normalize
* @param float $mean Mean Value
* @param float $stdDev Standard Deviation
@@ -3204,19 +1484,7 @@ class Statistical
*/
public static function STANDARDIZE($value, $mean, $stdDev)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if ($stdDev <= 0) {
- return Functions::NAN();
- }
-
- return ($value - $mean) / $stdDev;
- }
-
- return Functions::VALUE();
+ return Statistical\Standardize::execute($value, $mean, $stdDev);
}
/**
@@ -3228,45 +1496,18 @@ class Statistical
* Excel Function:
* STDEV(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\StandardDeviations::STDEV()
+ * Use the STDEV() method in the Statistical\StandardDeviations class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function STDEV(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- // Return value
- $returnValue = null;
-
- $aMean = self::AVERAGE($aArgs);
- if ($aMean !== null) {
- $aCount = -1;
- foreach ($aArgs as $k => $arg) {
- if (
- (is_bool($arg)) &&
- ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
- ) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = ($arg - $aMean) ** 2;
- } else {
- $returnValue += ($arg - $aMean) ** 2;
- }
- ++$aCount;
- }
- }
-
- // Return
- if (($aCount > 0) && ($returnValue >= 0)) {
- return sqrt($returnValue / $aCount);
- }
- }
-
- return Functions::DIV0();
+ return StandardDeviations::STDEV(...$args);
}
/**
@@ -3277,48 +1518,18 @@ class Statistical
* Excel Function:
* STDEVA(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\StandardDeviations::STDEVA()
+ * Use the STDEVA() method in the Statistical\StandardDeviations class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function STDEVA(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- $returnValue = null;
-
- $aMean = self::AVERAGEA($aArgs);
- if ($aMean !== null) {
- $aCount = -1;
- foreach ($aArgs as $k => $arg) {
- if (
- (is_bool($arg)) &&
- (!Functions::isMatrixValue($k))
- ) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- if ($returnValue === null) {
- $returnValue = ($arg - $aMean) ** 2;
- } else {
- $returnValue += ($arg - $aMean) ** 2;
- }
- ++$aCount;
- }
- }
- }
-
- if (($aCount > 0) && ($returnValue >= 0)) {
- return sqrt($returnValue / $aCount);
- }
- }
-
- return Functions::DIV0();
+ return StandardDeviations::STDEVA(...$args);
}
/**
@@ -3329,43 +1540,18 @@ class Statistical
* Excel Function:
* STDEVP(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\StandardDeviations::STDEVP()
+ * Use the STDEVP() method in the Statistical\StandardDeviations class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function STDEVP(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- $returnValue = null;
-
- $aMean = self::AVERAGE($aArgs);
- if ($aMean !== null) {
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if (
- (is_bool($arg)) &&
- ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
- ) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = ($arg - $aMean) ** 2;
- } else {
- $returnValue += ($arg - $aMean) ** 2;
- }
- ++$aCount;
- }
- }
-
- if (($aCount > 0) && ($returnValue >= 0)) {
- return sqrt($returnValue / $aCount);
- }
- }
-
- return Functions::DIV0();
+ return StandardDeviations::STDEVP(...$args);
}
/**
@@ -3376,53 +1562,28 @@ class Statistical
* Excel Function:
* STDEVPA(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\StandardDeviations::STDEVPA()
+ * Use the STDEVPA() method in the Statistical\StandardDeviations class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function STDEVPA(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- $returnValue = null;
-
- $aMean = self::AVERAGEA($aArgs);
- if ($aMean !== null) {
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if (
- (is_bool($arg)) &&
- (!Functions::isMatrixValue($k))
- ) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- if ($returnValue === null) {
- $returnValue = ($arg - $aMean) ** 2;
- } else {
- $returnValue += ($arg - $aMean) ** 2;
- }
- ++$aCount;
- }
- }
- }
-
- if (($aCount > 0) && ($returnValue >= 0)) {
- return sqrt($returnValue / $aCount);
- }
- }
-
- return Functions::DIV0();
+ return StandardDeviations::STDEVPA(...$args);
}
/**
* STEYX.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::STEYX()
+ * Use the STEYX() method in the Statistical\Trends class instead
+ *
* Returns the standard error of the predicted y-value for each x in the regression.
*
* @param mixed[] $yValues Data Series Y
@@ -3432,21 +1593,7 @@ class Statistical
*/
public static function STEYX($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getStdevOfResiduals();
+ return Trends::STEYX($yValues, $xValues);
}
/**
@@ -3454,6 +1601,11 @@ class Statistical
*
* Returns the probability of Student's T distribution.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\StudentT::distribution()
+ * Use the distribution() method in the Statistical\Distributions\StudentT class instead
+ *
* @param float $value Value for the function
* @param float $degrees degrees of freedom
* @param float $tails number of tails (1 or 2)
@@ -3462,61 +1614,18 @@ class Statistical
*/
public static function TDIST($value, $degrees, $tails)
{
- $value = Functions::flattenSingleValue($value);
- $degrees = floor(Functions::flattenSingleValue($degrees));
- $tails = floor(Functions::flattenSingleValue($tails));
-
- if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
- if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
- return Functions::NAN();
- }
- // tdist, which finds the probability that corresponds to a given value
- // of t with k degrees of freedom. This algorithm is translated from a
- // pascal function on p81 of "Statistical Computing in Pascal" by D
- // Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
- // London). The above Pascal algorithm is itself a translation of the
- // fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
- // Laboratory as reported in (among other places) "Applied Statistics
- // Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
- // Horwood Ltd.; W. Sussex, England).
- $tterm = $degrees;
- $ttheta = atan2($value, sqrt($tterm));
- $tc = cos($ttheta);
- $ts = sin($ttheta);
-
- if (($degrees % 2) == 1) {
- $ti = 3;
- $tterm = $tc;
- } else {
- $ti = 2;
- $tterm = 1;
- }
-
- $tsum = $tterm;
- while ($ti < $degrees) {
- $tterm *= $tc * $tc * ($ti - 1) / $ti;
- $tsum += $tterm;
- $ti += 2;
- }
- $tsum *= $ts;
- if (($degrees % 2) == 1) {
- $tsum = Functions::M_2DIVPI * ($tsum + $ttheta);
- }
- $tValue = 0.5 * (1 + $tsum);
- if ($tails == 1) {
- return 1 - abs($tValue);
- }
-
- return 1 - abs((1 - $tValue) - $tValue);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\StudentT::distribution($value, $degrees, $tails);
}
/**
* TINV.
*
- * Returns the one-tailed probability of the chi-squared distribution.
+ * Returns the one-tailed probability of the Student-T distribution.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\StudentT::inverse()
+ * Use the inverse() method in the Statistical\Distributions\StudentT class instead
*
* @param float $probability Probability for the function
* @param float $degrees degrees of freedom
@@ -3525,50 +1634,7 @@ class Statistical
*/
public static function TINV($probability, $degrees)
{
- $probability = Functions::flattenSingleValue($probability);
- $degrees = floor(Functions::flattenSingleValue($degrees));
-
- if ((is_numeric($probability)) && (is_numeric($degrees))) {
- $xLo = 100;
- $xHi = 0;
-
- $x = $xNew = 1;
- $dx = 1;
- $i = 0;
-
- while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
- // Apply Newton-Raphson step
- $result = self::TDIST($x, $degrees, 2);
- $error = $result - $probability;
- if ($error == 0.0) {
- $dx = 0;
- } elseif ($error < 0.0) {
- $xLo = $x;
- } else {
- $xHi = $x;
- }
- // Avoid division by zero
- if ($result != 0.0) {
- $dx = $error / $result;
- $xNew = $x - $dx;
- }
- // If the NR fails to converge (which for example may be the
- // case if the initial guess is too rough) we apply a bisection
- // step to determine a more narrow interval around the root.
- if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
- $xNew = ($xLo + $xHi) / 2;
- $dx = $xNew - $x;
- }
- $x = $xNew;
- }
- if ($i == self::MAX_ITERATIONS) {
- return Functions::NA();
- }
-
- return round($x, 12);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\StudentT::inverse($probability, $degrees);
}
/**
@@ -3576,31 +1642,21 @@ class Statistical
*
* Returns values along a linear Trend
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Trends::TREND()
+ * Use the TREND() method in the Statistical\Trends class instead
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param bool $const a logical value specifying whether to force the intersect to equal 0
*
- * @return array of float
+ * @return float[]
*/
public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
{
- $yValues = Functions::flattenArray($yValues);
- $xValues = Functions::flattenArray($xValues);
- $newValues = Functions::flattenArray($newValues);
- $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
- if (empty($newValues)) {
- $newValues = $bestFitLinear->getXValues();
- }
-
- $returnArray = [];
- foreach ($newValues as $xValue) {
- $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
- }
-
- return $returnArray;
+ return Trends::TREND($yValues, $xValues, $newValues, $const);
}
/**
@@ -3613,39 +1669,18 @@ class Statistical
* Excel Function:
* TRIMEAN(value1[,value2[, ...]], $discard)
*
+ * @Deprecated 1.18.0
+ *
+ *@see Statistical\Averages\Mean::trim()
+ * Use the trim() method in the Statistical\Averages\Mean class instead
+ *
* @param mixed $args Data values
*
* @return float|string
*/
public static function TRIMMEAN(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $percent = array_pop($aArgs);
-
- if ((is_numeric($percent)) && (!is_string($percent))) {
- if (($percent < 0) || ($percent > 1)) {
- return Functions::NAN();
- }
- $mArgs = [];
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $mArgs[] = $arg;
- }
- }
- $discard = floor(self::COUNT($mArgs) * $percent / 2);
- sort($mArgs);
- for ($i = 0; $i < $discard; ++$i) {
- array_pop($mArgs);
- array_shift($mArgs);
- }
-
- return self::AVERAGE($mArgs);
- }
-
- return Functions::VALUE();
+ return Statistical\Averages\Mean::trim(...$args);
}
/**
@@ -3656,38 +1691,18 @@ class Statistical
* Excel Function:
* VAR(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ *@see Statistical\Variances::VAR()
+ * Use the VAR() method in the Statistical\Variances class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARFunc(...$args)
{
- $returnValue = Functions::DIV0();
-
- $summerA = $summerB = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- $aCount = 0;
- foreach ($aArgs as $arg) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $summerA += ($arg * $arg);
- $summerB += $arg;
- ++$aCount;
- }
- }
-
- if ($aCount > 1) {
- $summerA *= $aCount;
- $summerB *= $summerB;
- $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
- }
-
- return $returnValue;
+ return Variances::VAR(...$args);
}
/**
@@ -3698,51 +1713,18 @@ class Statistical
* Excel Function:
* VARA(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Variances::VARA()
+ * Use the VARA() method in the Statistical\Variances class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARA(...$args)
{
- $returnValue = Functions::DIV0();
-
- $summerA = $summerB = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArrayIndexed($args);
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if (
- (is_string($arg)) &&
- (Functions::isValue($k))
- ) {
- return Functions::VALUE();
- } elseif (
- (is_string($arg)) &&
- (!Functions::isMatrixValue($k))
- ) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- $summerA += ($arg * $arg);
- $summerB += $arg;
- ++$aCount;
- }
- }
- }
-
- if ($aCount > 1) {
- $summerA *= $aCount;
- $summerB *= $summerB;
- $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
- }
-
- return $returnValue;
+ return Variances::VARA(...$args);
}
/**
@@ -3753,39 +1735,18 @@ class Statistical
* Excel Function:
* VARP(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Variances::VARP()
+ * Use the VARP() method in the Statistical\Variances class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARP(...$args)
{
- // Return value
- $returnValue = Functions::DIV0();
-
- $summerA = $summerB = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- $aCount = 0;
- foreach ($aArgs as $arg) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $summerA += ($arg * $arg);
- $summerB += $arg;
- ++$aCount;
- }
- }
-
- if ($aCount > 0) {
- $summerA *= $aCount;
- $summerB *= $summerB;
- $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
- }
-
- return $returnValue;
+ return Variances::VARP(...$args);
}
/**
@@ -3796,51 +1757,18 @@ class Statistical
* Excel Function:
* VARPA(value1[,value2[, ...]])
*
+ * @Deprecated 1.17.0
+ *
+ * @see Statistical\Variances::VARPA()
+ * Use the VARPA() method in the Statistical\Variances class instead
+ *
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARPA(...$args)
{
- $returnValue = Functions::DIV0();
-
- $summerA = $summerB = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArrayIndexed($args);
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if (
- (is_string($arg)) &&
- (Functions::isValue($k))
- ) {
- return Functions::VALUE();
- } elseif (
- (is_string($arg)) &&
- (!Functions::isMatrixValue($k))
- ) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- $summerA += ($arg * $arg);
- $summerB += $arg;
- ++$aCount;
- }
- }
- }
-
- if ($aCount > 0) {
- $summerA *= $aCount;
- $summerB *= $summerB;
- $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
- }
-
- return $returnValue;
+ return Variances::VARPA(...$args);
}
/**
@@ -3849,6 +1777,11 @@ class Statistical
* Returns the Weibull distribution. Use this distribution in reliability
* analysis, such as calculating a device's mean time to failure.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\Weibull::distribution()
+ * Use the distribution() method in the Statistical\Distributions\Weibull class instead
+ *
* @param float $value
* @param float $alpha Alpha Parameter
* @param float $beta Beta Parameter
@@ -3858,31 +1791,21 @@ class Statistical
*/
public static function WEIBULL($value, $alpha, $beta, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $alpha = Functions::flattenSingleValue($alpha);
- $beta = Functions::flattenSingleValue($beta);
-
- if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
- if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- return 1 - exp(0 - ($value / $beta) ** $alpha);
- }
-
- return ($alpha / $beta ** $alpha) * $value ** ($alpha - 1) * exp(0 - ($value / $beta) ** $alpha);
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Weibull::distribution($value, $alpha, $beta, $cumulative);
}
/**
* ZTEST.
*
- * Returns the Weibull distribution. Use this distribution in reliability
- * analysis, such as calculating a device's mean time to failure.
+ * Returns the one-tailed P-value of a z-test.
+ *
+ * For a given hypothesized population mean, x, Z.TEST returns the probability that the sample mean would be
+ * greater than the average of observations in the data set (array) — that is, the observed sample mean.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Statistical\Distributions\StandardNormal::zTest()
+ * Use the zTest() method in the Statistical\Distributions\StandardNormal class instead
*
* @param float $dataSet
* @param float $m0 Alpha Parameter
@@ -3892,15 +1815,6 @@ class Statistical
*/
public static function ZTEST($dataSet, $m0, $sigma = null)
{
- $dataSet = Functions::flattenArrayIndexed($dataSet);
- $m0 = Functions::flattenSingleValue($m0);
- $sigma = Functions::flattenSingleValue($sigma);
-
- if ($sigma === null) {
- $sigma = self::STDEV($dataSet);
- }
- $n = count($dataSet);
-
- return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0) / ($sigma / sqrt($n)));
+ return Statistical\Distributions\StandardNormal::zTest($dataSet, $m0, $sigma);
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
new file mode 100644
index 00000000000..75c012dc322
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
@@ -0,0 +1,50 @@
+ $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
+ return Functions::VALUE();
+ }
+ if (self::isAcceptedCountable($arg, $k)) {
+ $returnValue += abs($arg - $aMean);
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount === 0) {
+ return Functions::DIV0();
+ }
+
+ return $returnValue / $aCount;
+ }
+
+ /**
+ * AVERAGE.
+ *
+ * Returns the average (arithmetic mean) of the arguments
+ *
+ * Excel Function:
+ * AVERAGE(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function average(...$args)
+ {
+ $returnValue = $aCount = 0;
+
+ // Loop through arguments
+ foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
+ return Functions::VALUE();
+ }
+ if (self::isAcceptedCountable($arg, $k)) {
+ $returnValue += $arg;
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ }
+
+ return Functions::DIV0();
+ }
+
+ /**
+ * AVERAGEA.
+ *
+ * Returns the average of its arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * AVERAGEA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function averageA(...$args)
+ {
+ $returnValue = null;
+
+ $aCount = 0;
+ // Loop through arguments
+ foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
+ if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (int) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ $returnValue += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ }
+
+ return Functions::DIV0();
+ }
+
+ /**
+ * MEDIAN.
+ *
+ * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
+ *
+ * Excel Function:
+ * MEDIAN(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function median(...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ $returnValue = Functions::NAN();
+
+ $aArgs = self::filterArguments($aArgs);
+ $valueCount = count($aArgs);
+ if ($valueCount > 0) {
+ sort($aArgs, SORT_NUMERIC);
+ $valueCount = $valueCount / 2;
+ if ($valueCount == floor($valueCount)) {
+ $returnValue = ($aArgs[$valueCount--] + $aArgs[$valueCount]) / 2;
+ } else {
+ $valueCount = floor($valueCount);
+ $returnValue = $aArgs[$valueCount];
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * MODE.
+ *
+ * Returns the most frequently occurring, or repetitive, value in an array or range of data
+ *
+ * Excel Function:
+ * MODE(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function mode(...$args)
+ {
+ $returnValue = Functions::NA();
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ $aArgs = self::filterArguments($aArgs);
+
+ if (!empty($aArgs)) {
+ return self::modeCalc($aArgs);
+ }
+
+ return $returnValue;
+ }
+
+ protected static function filterArguments($args)
+ {
+ return array_filter(
+ $args,
+ function ($value) {
+ // Is it a numeric value?
+ return (is_numeric($value)) && (!is_string($value));
+ }
+ );
+ }
+
+ //
+ // Special variant of array_count_values that isn't limited to strings and integers,
+ // but can work with floating point numbers as values
+ //
+ private static function modeCalc($data)
+ {
+ $frequencyArray = [];
+ $index = 0;
+ $maxfreq = 0;
+ $maxfreqkey = '';
+ $maxfreqdatum = '';
+ foreach ($data as $datum) {
+ $found = false;
+ ++$index;
+ foreach ($frequencyArray as $key => $value) {
+ if ((string) $value['value'] == (string) $datum) {
+ ++$frequencyArray[$key]['frequency'];
+ $freq = $frequencyArray[$key]['frequency'];
+ if ($freq > $maxfreq) {
+ $maxfreq = $freq;
+ $maxfreqkey = $key;
+ $maxfreqdatum = $datum;
+ } elseif ($freq == $maxfreq) {
+ if ($frequencyArray[$key]['index'] < $frequencyArray[$maxfreqkey]['index']) {
+ $maxfreqkey = $key;
+ $maxfreqdatum = $datum;
+ }
+ }
+ $found = true;
+
+ break;
+ }
+ }
+
+ if ($found === false) {
+ $frequencyArray[] = [
+ 'value' => $datum,
+ 'frequency' => 1,
+ 'index' => $index,
+ ];
+ }
+ }
+
+ if ($maxfreq <= 1) {
+ return Functions::NA();
+ }
+
+ return $maxfreqdatum;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
new file mode 100644
index 00000000000..d4d0c7ee504
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
@@ -0,0 +1,131 @@
+ 0)) {
+ $aCount = Counts::COUNT($aArgs);
+ if (Minimum::min($aArgs) > 0) {
+ return $aMean ** (1 / $aCount);
+ }
+ }
+
+ return Functions::NAN();
+ }
+
+ /**
+ * HARMEAN.
+ *
+ * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
+ * arithmetic mean of reciprocals.
+ *
+ * Excel Function:
+ * HARMEAN(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string
+ */
+ public static function harmonic(...$args)
+ {
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ if (Minimum::min($aArgs) < 0) {
+ return Functions::NAN();
+ }
+
+ $returnValue = 0;
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ($arg <= 0) {
+ return Functions::NAN();
+ }
+ $returnValue += (1 / $arg);
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return 1 / ($returnValue / $aCount);
+ }
+
+ return Functions::NA();
+ }
+
+ /**
+ * TRIMMEAN.
+ *
+ * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
+ * taken by excluding a percentage of data points from the top and bottom tails
+ * of a data set.
+ *
+ * Excel Function:
+ * TRIMEAN(value1[,value2[, ...]], $discard)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string
+ */
+ public static function trim(...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ // Calculate
+ $percent = array_pop($aArgs);
+
+ if ((is_numeric($percent)) && (!is_string($percent))) {
+ if (($percent < 0) || ($percent > 1)) {
+ return Functions::NAN();
+ }
+
+ $mArgs = [];
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ $discard = floor(Counts::COUNT($mArgs) * $percent / 2);
+ sort($mArgs);
+
+ for ($i = 0; $i < $discard; ++$i) {
+ array_pop($mArgs);
+ array_shift($mArgs);
+ }
+
+ return Averages::average($mArgs);
+ }
+
+ return Functions::VALUE();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
new file mode 100644
index 00000000000..51e6b004304
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
@@ -0,0 +1,304 @@
+getMessage();
+ }
+
+ if (($alpha <= 0) || ($alpha >= 1) || ($stdDev <= 0) || ($size < 1)) {
+ return Functions::NAN();
+ }
+
+ return Distributions\StandardNormal::inverse(1 - $alpha / 2) * $stdDev / sqrt($size);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php
new file mode 100644
index 00000000000..13e7af79912
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php
@@ -0,0 +1,95 @@
+ $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if (self::isAcceptedCountable($arg, $k)) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * COUNTA.
+ *
+ * Counts the number of cells that are not empty within the list of arguments
+ *
+ * Excel Function:
+ * COUNTA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return int
+ */
+ public static function COUNTA(...$args)
+ {
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ foreach ($aArgs as $k => $arg) {
+ // Nulls are counted if literals, but not if cell values
+ if ($arg !== null || (!Functions::isCellValue($k))) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * COUNTBLANK.
+ *
+ * Counts the number of empty cells within the list of arguments
+ *
+ * Excel Function:
+ * COUNTBLANK(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return int
+ */
+ public static function COUNTBLANK(...$args)
+ {
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ foreach ($aArgs as $arg) {
+ // Is it a blank cell?
+ if (($arg === null) || ((is_string($arg)) && ($arg == ''))) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
new file mode 100644
index 00000000000..55da9f1ddf5
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
@@ -0,0 +1,141 @@
+ $arg) {
+ // Is it a numeric value?
+ if (
+ (is_bool($arg)) &&
+ ((!Functions::isCellValue($k)) ||
+ (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
+ ) {
+ $arg = (int) $arg;
+ }
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += ($arg - $aMean) ** 2;
+ ++$aCount;
+ }
+ }
+
+ return $aCount === 0 ? Functions::VALUE() : $returnValue;
+ }
+
+ /**
+ * KURT.
+ *
+ * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
+ * or flatness of a distribution compared with the normal distribution. Positive
+ * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
+ * relatively flat distribution.
+ *
+ * @param array ...$args Data Series
+ *
+ * @return float|string
+ */
+ public static function kurtosis(...$args)
+ {
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $mean = Averages::average($aArgs);
+ if (!is_numeric($mean)) {
+ return Functions::DIV0();
+ }
+ $stdDev = StandardDeviations::STDEV($aArgs);
+
+ if ($stdDev > 0) {
+ $count = $summer = 0;
+
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += (($arg - $mean) / $stdDev) ** 4;
+ ++$count;
+ }
+ }
+ }
+
+ if ($count > 3) {
+ return $summer * ($count * ($count + 1) /
+ (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 /
+ (($count - 2) * ($count - 3)));
+ }
+ }
+
+ return Functions::DIV0();
+ }
+
+ /**
+ * SKEW.
+ *
+ * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
+ * of a distribution around its mean. Positive skewness indicates a distribution with an
+ * asymmetric tail extending toward more positive values. Negative skewness indicates a
+ * distribution with an asymmetric tail extending toward more negative values.
+ *
+ * @param array ...$args Data Series
+ *
+ * @return float|int|string The result, or a string containing an error
+ */
+ public static function skew(...$args)
+ {
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $mean = Averages::average($aArgs);
+ if (!is_numeric($mean)) {
+ return Functions::DIV0();
+ }
+ $stdDev = StandardDeviations::STDEV($aArgs);
+ if ($stdDev === 0.0 || is_string($stdDev)) {
+ return Functions::DIV0();
+ }
+
+ $count = $summer = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
+ } elseif (!is_numeric($arg)) {
+ return Functions::VALUE();
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += (($arg - $mean) / $stdDev) ** 3;
+ ++$count;
+ }
+ }
+ }
+
+ if ($count > 2) {
+ return $summer * ($count / (($count - 1) * ($count - 2)));
+ }
+
+ return Functions::DIV0();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
new file mode 100644
index 00000000000..63e6eb4d659
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
@@ -0,0 +1,260 @@
+getMessage();
+ }
+
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
+ return Functions::NAN();
+ }
+
+ $value -= $rMin;
+ $value /= ($rMax - $rMin);
+
+ return self::incompleteBeta($value, $alpha, $beta);
+ }
+
+ /**
+ * BETAINV.
+ *
+ * Returns the inverse of the Beta distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * @param mixed $alpha Parameter to the distribution as a float
+ * @param mixed $beta Parameter to the distribution as a float
+ * @param mixed $rMin Minimum value as a float
+ * @param mixed $rMax Maximum value as a float
+ *
+ * @return float|string
+ */
+ public static function inverse($probability, $alpha, $beta, $rMin = 0.0, $rMax = 1.0)
+ {
+ $probability = Functions::flattenSingleValue($probability);
+ $alpha = Functions::flattenSingleValue($alpha);
+ $beta = Functions::flattenSingleValue($beta);
+ $rMin = ($rMin === null) ? 0.0 : Functions::flattenSingleValue($rMin);
+ $rMax = ($rMax === null) ? 1.0 : Functions::flattenSingleValue($rMax);
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ $beta = DistributionValidations::validateFloat($beta);
+ $rMax = DistributionValidations::validateFloat($rMax);
+ $rMin = DistributionValidations::validateFloat($rMin);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0.0)) {
+ return Functions::NAN();
+ }
+
+ return self::calculateInverse($probability, $alpha, $beta, $rMin, $rMax);
+ }
+
+ /**
+ * @return float|string
+ */
+ private static function calculateInverse(float $probability, float $alpha, float $beta, float $rMin, float $rMax)
+ {
+ $a = 0;
+ $b = 2;
+
+ $i = 0;
+ while ((($b - $a) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
+ $guess = ($a + $b) / 2;
+ $result = self::distribution($guess, $alpha, $beta);
+ if (($result === $probability) || ($result === 0.0)) {
+ $b = $a;
+ } elseif ($result > $probability) {
+ $b = $guess;
+ } else {
+ $a = $guess;
+ }
+ }
+
+ if ($i === self::MAX_ITERATIONS) {
+ return Functions::NA();
+ }
+
+ return round($rMin + $guess * ($rMax - $rMin), 12);
+ }
+
+ /**
+ * Incomplete beta function.
+ *
+ * @author Jaco van Kooten
+ * @author Paul Meagher
+ *
+ * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
+ *
+ * @param float $x require 0<=x<=1
+ * @param float $p require p>0
+ * @param float $q require q>0
+ *
+ * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
+ */
+ public static function incompleteBeta(float $x, float $p, float $q): float
+ {
+ if ($x <= 0.0) {
+ return 0.0;
+ } elseif ($x >= 1.0) {
+ return 1.0;
+ } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
+ return 0.0;
+ }
+
+ $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
+ if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
+ return $beta_gam * self::betaFraction($x, $p, $q) / $p;
+ }
+
+ return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
+ }
+
+ // Function cache for logBeta function
+ private static $logBetaCacheP = 0.0;
+
+ private static $logBetaCacheQ = 0.0;
+
+ private static $logBetaCacheResult = 0.0;
+
+ /**
+ * The natural logarithm of the beta function.
+ *
+ * @param float $p require p>0
+ * @param float $q require q>0
+ *
+ * @return float 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
+ *
+ * @author Jaco van Kooten
+ */
+ private static function logBeta(float $p, float $q): float
+ {
+ if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
+ self::$logBetaCacheP = $p;
+ self::$logBetaCacheQ = $q;
+ if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
+ self::$logBetaCacheResult = 0.0;
+ } else {
+ self::$logBetaCacheResult = Gamma::logGamma($p) + Gamma::logGamma($q) - Gamma::logGamma($p + $q);
+ }
+ }
+
+ return self::$logBetaCacheResult;
+ }
+
+ /**
+ * Evaluates of continued fraction part of incomplete beta function.
+ * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
+ *
+ * @author Jaco van Kooten
+ */
+ private static function betaFraction(float $x, float $p, float $q): float
+ {
+ $c = 1.0;
+ $sum_pq = $p + $q;
+ $p_plus = $p + 1.0;
+ $p_minus = $p - 1.0;
+ $h = 1.0 - $sum_pq * $x / $p_plus;
+ if (abs($h) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $frac = $h;
+ $m = 1;
+ $delta = 0.0;
+ while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) {
+ $m2 = 2 * $m;
+ // even index for d
+ $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < self::XMININ) {
+ $c = self::XMININ;
+ }
+ $frac *= $h * $c;
+ // odd index for d
+ $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < self::XMININ) {
+ $c = self::XMININ;
+ }
+ $delta = $h * $c;
+ $frac *= $delta;
+ ++$m;
+ }
+
+ return $frac;
+ }
+
+ private static function betaValue(float $a, float $b): float
+ {
+ return (Gamma::gammaValue($a) * Gamma::gammaValue($b)) /
+ Gamma::gammaValue($a + $b);
+ }
+
+ private static function regularizedIncompleteBeta(float $value, float $a, float $b): float
+ {
+ return self::incompleteBeta($value, $a, $b) / self::betaValue($a, $b);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
new file mode 100644
index 00000000000..9631236ab89
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
@@ -0,0 +1,202 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($value > $trials)) {
+ return Functions::NAN();
+ }
+
+ if ($cumulative) {
+ return self::calculateCumulativeBinomial($value, $trials, $probability);
+ }
+
+ return Combinations::withoutRepetition($trials, $value) * $probability ** $value
+ * (1 - $probability) ** ($trials - $value);
+ }
+
+ /**
+ * BINOM.DIST.RANGE.
+ *
+ * Returns returns the Binomial Distribution probability for the number of successes from a specified number
+ * of trials falling into a specified range.
+ *
+ * @param mixed $trials Integer number of trials
+ * @param mixed $probability Probability of success on each trial as a float
+ * @param mixed $successes The integer number of successes in trials
+ * @param mixed $limit Upper limit for successes in trials as null, or an integer
+ * If null, then this will indicate the same as the number of Successes
+ *
+ * @return float|string
+ */
+ public static function range($trials, $probability, $successes, $limit = null)
+ {
+ $trials = Functions::flattenSingleValue($trials);
+ $probability = Functions::flattenSingleValue($probability);
+ $successes = Functions::flattenSingleValue($successes);
+ $limit = ($limit === null) ? $successes : Functions::flattenSingleValue($limit);
+
+ try {
+ $trials = DistributionValidations::validateInt($trials);
+ $probability = DistributionValidations::validateProbability($probability);
+ $successes = DistributionValidations::validateInt($successes);
+ $limit = DistributionValidations::validateInt($limit);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($successes < 0) || ($successes > $trials)) {
+ return Functions::NAN();
+ }
+ if (($limit < 0) || ($limit > $trials) || $limit < $successes) {
+ return Functions::NAN();
+ }
+
+ $summer = 0;
+ for ($i = $successes; $i <= $limit; ++$i) {
+ $summer += Combinations::withoutRepetition($trials, $i) * $probability ** $i
+ * (1 - $probability) ** ($trials - $i);
+ }
+
+ return $summer;
+ }
+
+ /**
+ * NEGBINOMDIST.
+ *
+ * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
+ * there will be number_f failures before the number_s-th success, when the constant
+ * probability of a success is probability_s. This function is similar to the binomial
+ * distribution, except that the number of successes is fixed, and the number of trials is
+ * variable. Like the binomial, trials are assumed to be independent.
+ *
+ * @param mixed $failures Number of Failures as an integer
+ * @param mixed $successes Threshold number of Successes as an integer
+ * @param mixed $probability Probability of success on each trial as a float
+ *
+ * @return float|string The result, or a string containing an error
+ *
+ * TODO Add support for the cumulative flag not present for NEGBINOMDIST, but introduced for NEGBINOM.DIST
+ * The cumulative default should be false to reflect the behaviour of NEGBINOMDIST
+ */
+ public static function negative($failures, $successes, $probability)
+ {
+ $failures = Functions::flattenSingleValue($failures);
+ $successes = Functions::flattenSingleValue($successes);
+ $probability = Functions::flattenSingleValue($probability);
+
+ try {
+ $failures = DistributionValidations::validateInt($failures);
+ $successes = DistributionValidations::validateInt($successes);
+ $probability = DistributionValidations::validateProbability($probability);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($failures < 0) || ($successes < 1)) {
+ return Functions::NAN();
+ }
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ if (($failures + $successes - 1) <= 0) {
+ return Functions::NAN();
+ }
+ }
+
+ return (Combinations::withoutRepetition($failures + $successes - 1, $successes - 1))
+ * ($probability ** $successes) * ((1 - $probability) ** $failures);
+ }
+
+ /**
+ * CRITBINOM.
+ *
+ * Returns the smallest value for which the cumulative binomial distribution is greater
+ * than or equal to a criterion value
+ *
+ * @param mixed $trials number of Bernoulli trials as an integer
+ * @param mixed $probability probability of a success on each trial as a float
+ * @param mixed $alpha criterion value as a float
+ *
+ * @return int|string
+ */
+ public static function inverse($trials, $probability, $alpha)
+ {
+ $trials = Functions::flattenSingleValue($trials);
+ $probability = Functions::flattenSingleValue($probability);
+ $alpha = Functions::flattenSingleValue($alpha);
+
+ try {
+ $trials = DistributionValidations::validateInt($trials);
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($trials < 0) {
+ return Functions::NAN();
+ } elseif (($alpha < 0.0) || ($alpha > 1.0)) {
+ return Functions::NAN();
+ }
+
+ $successes = 0;
+ while ($successes <= $trials) {
+ $result = self::calculateCumulativeBinomial($successes, $trials, $probability);
+ if ($result >= $alpha) {
+ break;
+ }
+ ++$successes;
+ }
+
+ return $successes;
+ }
+
+ /**
+ * @return float|int
+ */
+ private static function calculateCumulativeBinomial(int $value, int $trials, float $probability)
+ {
+ $summer = 0;
+ for ($i = 0; $i <= $value; ++$i) {
+ $summer += Combinations::withoutRepetition($trials, $i) * $probability ** $i
+ * (1 - $probability) ** ($trials - $i);
+ }
+
+ return $summer;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
new file mode 100644
index 00000000000..5165d6397ed
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
@@ -0,0 +1,311 @@
+getMessage();
+ }
+
+ if ($degrees < 1) {
+ return Functions::NAN();
+ }
+ if ($value < 0) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return 1;
+ }
+
+ return Functions::NAN();
+ }
+
+ return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2) / Gamma::gammaValue($degrees / 2));
+ }
+
+ /**
+ * CHIDIST.
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $value Float value for which we want the probability
+ * @param mixed $degrees Integer degrees of freedom
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ *
+ * @return float|string
+ */
+ public static function distributionLeftTail($value, $degrees, $cumulative)
+ {
+ $value = Functions::flattenSingleValue($value);
+ $degrees = Functions::flattenSingleValue($degrees);
+ $cumulative = Functions::flattenSingleValue($cumulative);
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $degrees = DistributionValidations::validateInt($degrees);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return Functions::NAN();
+ }
+ if ($value < 0) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return 1;
+ }
+
+ return Functions::NAN();
+ }
+
+ if ($cumulative === true) {
+ return 1 - self::distributionRightTail($value, $degrees);
+ }
+
+ return (($value ** (($degrees / 2) - 1) * exp(-$value / 2))) /
+ ((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2));
+ }
+
+ /**
+ * CHIINV.
+ *
+ * Returns the inverse of the right-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * @param mixed $degrees Integer degrees of freedom
+ *
+ * @return float|string
+ */
+ public static function inverseRightTail($probability, $degrees)
+ {
+ $probability = Functions::flattenSingleValue($probability);
+ $degrees = Functions::flattenSingleValue($degrees);
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return Functions::NAN();
+ }
+
+ $callback = function ($value) use ($degrees) {
+ return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2)
+ / Gamma::gammaValue($degrees / 2));
+ };
+
+ $newtonRaphson = new NewtonRaphson($callback);
+
+ return $newtonRaphson->execute($probability);
+ }
+
+ /**
+ * CHIINV.
+ *
+ * Returns the inverse of the left-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * @param mixed $degrees Integer degrees of freedom
+ *
+ * @return float|string
+ */
+ public static function inverseLeftTail($probability, $degrees)
+ {
+ $probability = Functions::flattenSingleValue($probability);
+ $degrees = Functions::flattenSingleValue($degrees);
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return Functions::NAN();
+ }
+
+ return self::inverseLeftTailCalculation($probability, $degrees);
+ }
+
+ /**
+ * CHITEST.
+ *
+ * Uses the chi-square test to calculate the probability that the differences between two supplied data sets
+ * (of observed and expected frequencies), are likely to be simply due to sampling error,
+ * or if they are likely to be real.
+ *
+ * @param mixed $actual an array of observed frequencies
+ * @param mixed $expected an array of expected frequencies
+ *
+ * @return float|string
+ */
+ public static function test($actual, $expected)
+ {
+ $rows = count($actual);
+ $actual = Functions::flattenArray($actual);
+ $expected = Functions::flattenArray($expected);
+ $columns = count($actual) / $rows;
+
+ $countActuals = count($actual);
+ $countExpected = count($expected);
+ if ($countActuals !== $countExpected || $countActuals === 1) {
+ return Functions::NAN();
+ }
+
+ $result = 0.0;
+ for ($i = 0; $i < $countActuals; ++$i) {
+ if ($expected[$i] == 0.0) {
+ return Functions::DIV0();
+ } elseif ($expected[$i] < 0.0) {
+ return Functions::NAN();
+ }
+ $result += (($actual[$i] - $expected[$i]) ** 2) / $expected[$i];
+ }
+
+ $degrees = self::degrees($rows, $columns);
+
+ $result = self::distributionRightTail($result, $degrees);
+
+ return $result;
+ }
+
+ protected static function degrees(int $rows, int $columns): int
+ {
+ if ($rows === 1) {
+ return $columns - 1;
+ } elseif ($columns === 1) {
+ return $rows - 1;
+ }
+
+ return ($columns - 1) * ($rows - 1);
+ }
+
+ private static function inverseLeftTailCalculation(float $probability, int $degrees): float
+ {
+ // bracket the root
+ $min = 0;
+ $sd = sqrt(2.0 * $degrees);
+ $max = 2 * $sd;
+ $s = -1;
+
+ while ($s * self::pchisq($max, $degrees) > $probability * $s) {
+ $min = $max;
+ $max += 2 * $sd;
+ }
+
+ // Find root using bisection
+ $chi2 = 0.5 * ($min + $max);
+
+ while (($max - $min) > self::EPS * $chi2) {
+ if ($s * self::pchisq($chi2, $degrees) > $probability * $s) {
+ $min = $chi2;
+ } else {
+ $max = $chi2;
+ }
+ $chi2 = 0.5 * ($min + $max);
+ }
+
+ return $chi2;
+ }
+
+ private static function pchisq($chi2, $degrees)
+ {
+ return self::gammp($degrees, 0.5 * $chi2);
+ }
+
+ private static function gammp($n, $x)
+ {
+ if ($x < 0.5 * $n + 1) {
+ return self::gser($n, $x);
+ }
+
+ return 1 - self::gcf($n, $x);
+ }
+
+ // Return the incomplete gamma function P(n/2,x) evaluated by
+ // series representation. Algorithm from numerical recipe.
+ // Assume that n is a positive integer and x>0, won't check arguments.
+ // Relative error controlled by the eps parameter
+ private static function gser($n, $x)
+ {
+ $gln = Gamma::ln($n / 2);
+ $a = 0.5 * $n;
+ $ap = $a;
+ $sum = 1.0 / $a;
+ $del = $sum;
+ for ($i = 1; $i < 101; ++$i) {
+ ++$ap;
+ $del = $del * $x / $ap;
+ $sum += $del;
+ if ($del < $sum * self::EPS) {
+ break;
+ }
+ }
+
+ return $sum * exp(-$x + $a * log($x) - $gln);
+ }
+
+ // Return the incomplete gamma function Q(n/2,x) evaluated by
+ // its continued fraction representation. Algorithm from numerical recipe.
+ // Assume that n is a postive integer and x>0, won't check arguments.
+ // Relative error controlled by the eps parameter
+ private static function gcf($n, $x)
+ {
+ $gln = Gamma::ln($n / 2);
+ $a = 0.5 * $n;
+ $b = $x + 1 - $a;
+ $fpmin = 1.e-300;
+ $c = 1 / $fpmin;
+ $d = 1 / $b;
+ $h = $d;
+ for ($i = 1; $i < 101; ++$i) {
+ $an = -$i * ($i - $a);
+ $b += 2;
+ $d = $an * $d + $b;
+ if (abs($d) < $fpmin) {
+ $d = $fpmin;
+ }
+ $c = $b + $an / $c;
+ if (abs($c) < $fpmin) {
+ $c = $fpmin;
+ }
+ $d = 1 / $d;
+ $del = $d * $c;
+ $h = $h * $del;
+ if (abs($del - 1) < self::EPS) {
+ break;
+ }
+ }
+
+ return $h * exp(-$x + $a * log($x) - $gln);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
new file mode 100644
index 00000000000..57ef00af04a
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
@@ -0,0 +1,24 @@
+ 1.0) {
+ throw new Exception(Functions::NAN());
+ }
+
+ return $probability;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
new file mode 100644
index 00000000000..b3fd9460c1e
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
@@ -0,0 +1,47 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($lambda < 0)) {
+ return Functions::NAN();
+ }
+
+ if ($cumulative === true) {
+ return 1 - exp(0 - $value * $lambda);
+ }
+
+ return $lambda * exp(0 - $value * $lambda);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
new file mode 100644
index 00000000000..54b1950d6d9
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
@@ -0,0 +1,56 @@
+getMessage();
+ }
+
+ if ($value < 0 || $u < 1 || $v < 1) {
+ return Functions::NAN();
+ }
+
+ if ($cumulative) {
+ $adjustedValue = ($u * $value) / ($u * $value + $v);
+
+ return Beta::incompleteBeta($adjustedValue, $u / 2, $v / 2);
+ }
+
+ return (Gamma::gammaValue(($v + $u) / 2) /
+ (Gamma::gammaValue($u / 2) * Gamma::gammaValue($v / 2))) *
+ (($u / $v) ** ($u / 2)) *
+ (($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2)));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
new file mode 100644
index 00000000000..923bf02d909
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
@@ -0,0 +1,61 @@
+getMessage();
+ }
+
+ if (($value <= -1) || ($value >= 1)) {
+ return Functions::NAN();
+ }
+
+ return 0.5 * log((1 + $value) / (1 - $value));
+ }
+
+ /**
+ * FISHERINV.
+ *
+ * Returns the inverse of the Fisher transformation. Use this transformation when
+ * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
+ * FISHERINV(y) = x.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ *
+ * @return float|string
+ */
+ public static function inverse($probability)
+ {
+ $probability = Functions::flattenSingleValue($probability);
+
+ try {
+ DistributionValidations::validateFloat($probability);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (exp(2 * $probability) - 1) / (exp(2 * $probability) + 1);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
new file mode 100644
index 00000000000..2c6ed670a8e
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
@@ -0,0 +1,127 @@
+getMessage();
+ }
+
+ if ((((int) $value) == ((float) $value)) && $value <= 0.0) {
+ return Functions::NAN();
+ }
+
+ return self::gammaValue($value);
+ }
+
+ /**
+ * GAMMADIST.
+ *
+ * Returns the gamma distribution.
+ *
+ * @param mixed $value Float Value at which you want to evaluate the distribution
+ * @param mixed $a Parameter to the distribution as a float
+ * @param mixed $b Parameter to the distribution as a float
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ *
+ * @return float|string
+ */
+ public static function distribution($value, $a, $b, $cumulative)
+ {
+ $value = Functions::flattenSingleValue($value);
+ $a = Functions::flattenSingleValue($a);
+ $b = Functions::flattenSingleValue($b);
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $a = DistributionValidations::validateFloat($a);
+ $b = DistributionValidations::validateFloat($b);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($value < 0) || ($a <= 0) || ($b <= 0)) {
+ return Functions::NAN();
+ }
+
+ return self::calculateDistribution($value, $a, $b, $cumulative);
+ }
+
+ /**
+ * GAMMAINV.
+ *
+ * Returns the inverse of the Gamma distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * @param mixed $alpha Parameter to the distribution as a float
+ * @param mixed $beta Parameter to the distribution as a float
+ *
+ * @return float|string
+ */
+ public static function inverse($probability, $alpha, $beta)
+ {
+ $probability = Functions::flattenSingleValue($probability);
+ $alpha = Functions::flattenSingleValue($alpha);
+ $beta = Functions::flattenSingleValue($beta);
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ $beta = DistributionValidations::validateFloat($beta);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($alpha <= 0.0) || ($beta <= 0.0)) {
+ return Functions::NAN();
+ }
+
+ return self::calculateInverse($probability, $alpha, $beta);
+ }
+
+ /**
+ * GAMMALN.
+ *
+ * Returns the natural logarithm of the gamma function.
+ *
+ * @param mixed $value Float Value at which you want to evaluate the distribution
+ *
+ * @return float|string
+ */
+ public static function ln($value)
+ {
+ $value = Functions::flattenSingleValue($value);
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($value <= 0) {
+ return Functions::NAN();
+ }
+
+ return log(self::gammaValue($value));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
new file mode 100644
index 00000000000..89170f7cada
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
@@ -0,0 +1,381 @@
+ Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = self::calculateDistribution($x, $alpha, $beta, true);
+ $error = $result - $probability;
+
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+
+ $pdf = self::calculateDistribution($x, $alpha, $beta, false);
+ // Avoid division by zero
+ if ($pdf !== 0.0) {
+ $dx = $error / $pdf;
+ $xNew = $x - $dx;
+ }
+
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+
+ if ($i === self::MAX_ITERATIONS) {
+ return Functions::NA();
+ }
+
+ return $x;
+ }
+
+ //
+ // Implementation of the incomplete Gamma function
+ //
+ public static function incompleteGamma(float $a, float $x): float
+ {
+ static $max = 32;
+ $summer = 0;
+ for ($n = 0; $n <= $max; ++$n) {
+ $divisor = $a;
+ for ($i = 1; $i <= $n; ++$i) {
+ $divisor *= ($a + $i);
+ }
+ $summer += ($x ** $n / $divisor);
+ }
+
+ return $x ** $a * exp(0 - $x) * $summer;
+ }
+
+ //
+ // Implementation of the Gamma function
+ //
+ public static function gammaValue(float $value): float
+ {
+ if ($value == 0.0) {
+ return 0;
+ }
+
+ static $p0 = 1.000000000190015;
+ static $p = [
+ 1 => 76.18009172947146,
+ 2 => -86.50532032941677,
+ 3 => 24.01409824083091,
+ 4 => -1.231739572450155,
+ 5 => 1.208650973866179e-3,
+ 6 => -5.395239384953e-6,
+ ];
+
+ $y = $x = $value;
+ $tmp = $x + 5.5;
+ $tmp -= ($x + 0.5) * log($tmp);
+
+ $summer = $p0;
+ for ($j = 1; $j <= 6; ++$j) {
+ $summer += ($p[$j] / ++$y);
+ }
+
+ return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x));
+ }
+
+ /**
+ * logGamma function.
+ *
+ * @version 1.1
+ *
+ * @author Jaco van Kooten
+ *
+ * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
+ *
+ * The natural logarithm of the gamma function.
+ * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
+ * Applied Mathematics Division
+ * Argonne National Laboratory
+ * Argonne, IL 60439
+ *
+ * References:
+ *
+ * - W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
+ * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.
+ * - K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.
+ * - Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.
+ *
+ *
+ *
+ * From the original documentation:
+ *
+ *
+ * This routine calculates the LOG(GAMMA) function for a positive real argument X.
+ * Computation is based on an algorithm outlined in references 1 and 2.
+ * The program uses rational functions that theoretically approximate LOG(GAMMA)
+ * to at least 18 significant decimal digits. The approximation for X > 12 is from
+ * reference 3, while approximations for X < 12.0 are similar to those in reference
+ * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
+ * the compiler, the intrinsic functions, and proper selection of the
+ * machine-dependent constants.
+ *
+ *
+ * Error returns:
+ * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
+ * The computation is believed to be free of underflow and overflow.
+ *
+ *
+ * @return float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
+ */
+
+ // Log Gamma related constants
+ private const LG_D1 = -0.5772156649015328605195174;
+
+ private const LG_D2 = 0.4227843350984671393993777;
+
+ private const LG_D4 = 1.791759469228055000094023;
+
+ private const LG_P1 = [
+ 4.945235359296727046734888,
+ 201.8112620856775083915565,
+ 2290.838373831346393026739,
+ 11319.67205903380828685045,
+ 28557.24635671635335736389,
+ 38484.96228443793359990269,
+ 26377.48787624195437963534,
+ 7225.813979700288197698961,
+ ];
+
+ private const LG_P2 = [
+ 4.974607845568932035012064,
+ 542.4138599891070494101986,
+ 15506.93864978364947665077,
+ 184793.2904445632425417223,
+ 1088204.76946882876749847,
+ 3338152.967987029735917223,
+ 5106661.678927352456275255,
+ 3074109.054850539556250927,
+ ];
+
+ private const LG_P4 = [
+ 14745.02166059939948905062,
+ 2426813.369486704502836312,
+ 121475557.4045093227939592,
+ 2663432449.630976949898078,
+ 29403789566.34553899906876,
+ 170266573776.5398868392998,
+ 492612579337.743088758812,
+ 560625185622.3951465078242,
+ ];
+
+ private const LG_Q1 = [
+ 67.48212550303777196073036,
+ 1113.332393857199323513008,
+ 7738.757056935398733233834,
+ 27639.87074403340708898585,
+ 54993.10206226157329794414,
+ 61611.22180066002127833352,
+ 36351.27591501940507276287,
+ 8785.536302431013170870835,
+ ];
+
+ private const LG_Q2 = [
+ 183.0328399370592604055942,
+ 7765.049321445005871323047,
+ 133190.3827966074194402448,
+ 1136705.821321969608938755,
+ 5267964.117437946917577538,
+ 13467014.54311101692290052,
+ 17827365.30353274213975932,
+ 9533095.591844353613395747,
+ ];
+
+ private const LG_Q4 = [
+ 2690.530175870899333379843,
+ 639388.5654300092398984238,
+ 41355999.30241388052042842,
+ 1120872109.61614794137657,
+ 14886137286.78813811542398,
+ 101680358627.2438228077304,
+ 341747634550.7377132798597,
+ 446315818741.9713286462081,
+ ];
+
+ private const LG_C = [
+ -0.001910444077728,
+ 8.4171387781295e-4,
+ -5.952379913043012e-4,
+ 7.93650793500350248e-4,
+ -0.002777777777777681622553,
+ 0.08333333333333333331554247,
+ 0.0057083835261,
+ ];
+
+ // Rough estimate of the fourth root of logGamma_xBig
+ private const LG_FRTBIG = 2.25e76;
+
+ private const PNT68 = 0.6796875;
+
+ // Function cache for logGamma
+ private static $logGammaCacheResult = 0.0;
+
+ private static $logGammaCacheX = 0.0;
+
+ public static function logGamma(float $x): float
+ {
+ if ($x == self::$logGammaCacheX) {
+ return self::$logGammaCacheResult;
+ }
+
+ $y = $x;
+ if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
+ if ($y <= self::EPS) {
+ $res = -log($y);
+ } elseif ($y <= 1.5) {
+ $res = self::logGamma1($y);
+ } elseif ($y <= 4.0) {
+ $res = self::logGamma2($y);
+ } elseif ($y <= 12.0) {
+ $res = self::logGamma3($y);
+ } else {
+ $res = self::logGamma4($y);
+ }
+ } else {
+ // --------------------------
+ // Return for bad arguments
+ // --------------------------
+ $res = self::MAX_VALUE;
+ }
+
+ // ------------------------------
+ // Final adjustments and return
+ // ------------------------------
+ self::$logGammaCacheX = $x;
+ self::$logGammaCacheResult = $res;
+
+ return $res;
+ }
+
+ private static function logGamma1(float $y)
+ {
+ // ---------------------
+ // EPS .LT. X .LE. 1.5
+ // ---------------------
+ if ($y < self::PNT68) {
+ $corr = -log($y);
+ $xm1 = $y;
+ } else {
+ $corr = 0.0;
+ $xm1 = $y - 1.0;
+ }
+
+ $xden = 1.0;
+ $xnum = 0.0;
+ if ($y <= 0.5 || $y >= self::PNT68) {
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm1 + self::LG_P1[$i];
+ $xden = $xden * $xm1 + self::LG_Q1[$i];
+ }
+
+ return $corr + $xm1 * (self::LG_D1 + $xm1 * ($xnum / $xden));
+ }
+
+ $xm2 = $y - 1.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + self::LG_P2[$i];
+ $xden = $xden * $xm2 + self::LG_Q2[$i];
+ }
+
+ return $corr + $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
+ }
+
+ private static function logGamma2(float $y)
+ {
+ // ---------------------
+ // 1.5 .LT. X .LE. 4.0
+ // ---------------------
+ $xm2 = $y - 2.0;
+ $xden = 1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + self::LG_P2[$i];
+ $xden = $xden * $xm2 + self::LG_Q2[$i];
+ }
+
+ return $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
+ }
+
+ protected static function logGamma3(float $y)
+ {
+ // ----------------------
+ // 4.0 .LT. X .LE. 12.0
+ // ----------------------
+ $xm4 = $y - 4.0;
+ $xden = -1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm4 + self::LG_P4[$i];
+ $xden = $xden * $xm4 + self::LG_Q4[$i];
+ }
+
+ return self::LG_D4 + $xm4 * ($xnum / $xden);
+ }
+
+ protected static function logGamma4(float $y)
+ {
+ // ---------------------------------
+ // Evaluate for argument .GE. 12.0
+ // ---------------------------------
+ $res = 0.0;
+ if ($y <= self::LG_FRTBIG) {
+ $res = self::LG_C[6];
+ $ysq = $y * $y;
+ for ($i = 0; $i < 6; ++$i) {
+ $res = $res / $ysq + self::LG_C[$i];
+ }
+ $res /= $y;
+ $corr = log($y);
+ $res = $res + log(self::SQRT2PI) - 0.5 * $corr;
+ $res += $y * ($corr - 1.0);
+ }
+
+ return $res;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
new file mode 100644
index 00000000000..fe30c0879e4
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
@@ -0,0 +1,59 @@
+getMessage();
+ }
+
+ if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
+ return Functions::NAN();
+ }
+ if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
+ return Functions::NAN();
+ }
+ if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
+ return Functions::NAN();
+ }
+
+ $successesPopulationAndSample = (float) Combinations::withoutRepetition($populationSuccesses, $sampleSuccesses);
+ $numbersPopulationAndSample = (float) Combinations::withoutRepetition($populationNumber, $sampleNumber);
+ $adjustedPopulationAndSample = (float) Combinations::withoutRepetition(
+ $populationNumber - $populationSuccesses,
+ $sampleNumber - $sampleSuccesses
+ );
+
+ return $successesPopulationAndSample * $adjustedPopulationAndSample / $numbersPopulationAndSample;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
new file mode 100644
index 00000000000..e15237736ca
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
@@ -0,0 +1,119 @@
+getMessage();
+ }
+
+ if (($value <= 0) || ($stdDev <= 0)) {
+ return Functions::NAN();
+ }
+
+ return StandardNormal::cumulative((log($value) - $mean) / $stdDev);
+ }
+
+ /**
+ * LOGNORM.DIST.
+ *
+ * Returns the lognormal distribution of x, where ln(x) is normally distributed
+ * with parameters mean and standard_dev.
+ *
+ * @param mixed $value Float value for which we want the probability
+ * @param mixed $mean Mean value as a float
+ * @param mixed $stdDev Standard Deviation as a float
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function distribution($value, $mean, $stdDev, $cumulative = false)
+ {
+ $value = Functions::flattenSingleValue($value);
+ $mean = Functions::flattenSingleValue($mean);
+ $stdDev = Functions::flattenSingleValue($stdDev);
+ $cumulative = Functions::flattenSingleValue($cumulative);
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($value <= 0) || ($stdDev <= 0)) {
+ return Functions::NAN();
+ }
+
+ if ($cumulative === true) {
+ return StandardNormal::distribution((log($value) - $mean) / $stdDev, true);
+ }
+
+ return (1 / (sqrt(2 * M_PI) * $stdDev * $value)) *
+ exp(0 - ((log($value) - $mean) ** 2 / (2 * $stdDev ** 2)));
+ }
+
+ /**
+ * LOGINV.
+ *
+ * Returns the inverse of the lognormal cumulative distribution
+ *
+ * @param mixed $probability Float probability for which we want the value
+ * @param mixed $mean Mean Value as a float
+ * @param mixed $stdDev Standard Deviation as a float
+ *
+ * @return float|string The result, or a string containing an error
+ *
+ * @TODO Try implementing P J Acklam's refinement algorithm for greater
+ * accuracy if I can get my head round the mathematics
+ * (as described at) http://home.online.no/~pjacklam/notes/invnorm/
+ */
+ public static function inverse($probability, $mean, $stdDev)
+ {
+ $probability = Functions::flattenSingleValue($probability);
+ $mean = Functions::flattenSingleValue($mean);
+ $stdDev = Functions::flattenSingleValue($stdDev);
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($stdDev <= 0) {
+ return Functions::NAN();
+ }
+
+ return exp($mean + $stdDev * StandardNormal::inverse($probability));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
new file mode 100644
index 00000000000..26211672576
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
@@ -0,0 +1,62 @@
+callback = $callback;
+ }
+
+ public function execute(float $probability)
+ {
+ $xLo = 100;
+ $xHi = 0;
+
+ $dx = 1;
+ $x = $xNew = 1;
+ $i = 0;
+
+ while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = call_user_func($this->callback, $x);
+ $error = $result - $probability;
+
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+
+ // Avoid division by zero
+ if ($result != 0.0) {
+ $dx = $error / $result;
+ $xNew = $x - $dx;
+ }
+
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+
+ if ($i == self::MAX_ITERATIONS) {
+ return Functions::NA();
+ }
+
+ return $x;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
new file mode 100644
index 00000000000..4d158b8c8f7
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
@@ -0,0 +1,166 @@
+getMessage();
+ }
+
+ if ($stdDev < 0) {
+ return Functions::NAN();
+ }
+
+ if ($cumulative) {
+ return 0.5 * (1 + Engineering\Erf::erfValue(($value - $mean) / ($stdDev * sqrt(2))));
+ }
+
+ return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (($value - $mean) ** 2 / (2 * ($stdDev * $stdDev))));
+ }
+
+ /**
+ * NORMINV.
+ *
+ * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
+ *
+ * @param mixed $probability Float probability for which we want the value
+ * @param mixed $mean Mean Value as a float
+ * @param mixed $stdDev Standard Deviation as a float
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function inverse($probability, $mean, $stdDev)
+ {
+ $probability = Functions::flattenSingleValue($probability);
+ $mean = Functions::flattenSingleValue($mean);
+ $stdDev = Functions::flattenSingleValue($stdDev);
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($stdDev < 0) {
+ return Functions::NAN();
+ }
+
+ return (self::inverseNcdf($probability) * $stdDev) + $mean;
+ }
+
+ /*
+ * inverse_ncdf.php
+ * -------------------
+ * begin : Friday, January 16, 2004
+ * copyright : (C) 2004 Michael Nickerson
+ * email : nickersonm@yahoo.com
+ *
+ */
+ private static function inverseNcdf($p)
+ {
+ // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
+ // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
+ // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
+ // I have not checked the accuracy of this implementation. Be aware that PHP
+ // will truncate the coeficcients to 14 digits.
+
+ // You have permission to use and distribute this function freely for
+ // whatever purpose you want, but please show common courtesy and give credit
+ // where credit is due.
+
+ // Input paramater is $p - probability - where 0 < p < 1.
+
+ // Coefficients in rational approximations
+ static $a = [
+ 1 => -3.969683028665376e+01,
+ 2 => 2.209460984245205e+02,
+ 3 => -2.759285104469687e+02,
+ 4 => 1.383577518672690e+02,
+ 5 => -3.066479806614716e+01,
+ 6 => 2.506628277459239e+00,
+ ];
+
+ static $b = [
+ 1 => -5.447609879822406e+01,
+ 2 => 1.615858368580409e+02,
+ 3 => -1.556989798598866e+02,
+ 4 => 6.680131188771972e+01,
+ 5 => -1.328068155288572e+01,
+ ];
+
+ static $c = [
+ 1 => -7.784894002430293e-03,
+ 2 => -3.223964580411365e-01,
+ 3 => -2.400758277161838e+00,
+ 4 => -2.549732539343734e+00,
+ 5 => 4.374664141464968e+00,
+ 6 => 2.938163982698783e+00,
+ ];
+
+ static $d = [
+ 1 => 7.784695709041462e-03,
+ 2 => 3.224671290700398e-01,
+ 3 => 2.445134137142996e+00,
+ 4 => 3.754408661907416e+00,
+ ];
+
+ // Define lower and upper region break-points.
+ $p_low = 0.02425; //Use lower region approx. below this
+ $p_high = 1 - $p_low; //Use upper region approx. above this
+
+ if (0 < $p && $p < $p_low) {
+ // Rational approximation for lower region.
+ $q = sqrt(-2 * log($p));
+
+ return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
+ (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+ } elseif ($p_high < $p && $p < 1) {
+ // Rational approximation for upper region.
+ $q = sqrt(-2 * log(1 - $p));
+
+ return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
+ (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+ }
+
+ // Rational approximation for central region.
+ $q = $p - 0.5;
+ $r = $q * $q;
+
+ return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
+ ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
new file mode 100644
index 00000000000..e7252e02898
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
@@ -0,0 +1,53 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($mean < 0)) {
+ return Functions::NAN();
+ }
+
+ if ($cumulative) {
+ $summer = 0;
+ $floor = floor($value);
+ for ($i = 0; $i <= $floor; ++$i) {
+ $summer += $mean ** $i / MathTrig\Factorial::fact($i);
+ }
+
+ return exp(0 - $mean) * $summer;
+ }
+
+ return (exp(0 - $mean) * $mean ** $value) / MathTrig\Factorial::fact($value);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
new file mode 100644
index 00000000000..d10f02a5f74
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
@@ -0,0 +1,110 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
+ return Functions::NAN();
+ }
+
+ return self::calculateDistribution($value, $degrees, $tails);
+ }
+
+ /**
+ * TINV.
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability for the function
+ * @param mixed $degrees Integer value for degrees of freedom
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function inverse($probability, $degrees)
+ {
+ $probability = Functions::flattenSingleValue($probability);
+ $degrees = Functions::flattenSingleValue($degrees);
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees <= 0) {
+ return Functions::NAN();
+ }
+
+ $callback = function ($value) use ($degrees) {
+ return self::distribution($value, $degrees, 2);
+ };
+
+ $newtonRaphson = new NewtonRaphson($callback);
+
+ return $newtonRaphson->execute($probability);
+ }
+
+ /**
+ * @return float
+ */
+ private static function calculateDistribution(float $value, int $degrees, int $tails)
+ {
+ // tdist, which finds the probability that corresponds to a given value
+ // of t with k degrees of freedom. This algorithm is translated from a
+ // pascal function on p81 of "Statistical Computing in Pascal" by D
+ // Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
+ // London). The above Pascal algorithm is itself a translation of the
+ // fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
+ // Laboratory as reported in (among other places) "Applied Statistics
+ // Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
+ // Horwood Ltd.; W. Sussex, England).
+ $tterm = $degrees;
+ $ttheta = atan2($value, sqrt($tterm));
+ $tc = cos($ttheta);
+ $ts = sin($ttheta);
+
+ if (($degrees % 2) === 1) {
+ $ti = 3;
+ $tterm = $tc;
+ } else {
+ $ti = 2;
+ $tterm = 1;
+ }
+
+ $tsum = $tterm;
+ while ($ti < $degrees) {
+ $tterm *= $tc * $tc * ($ti - 1) / $ti;
+ $tsum += $tterm;
+ $ti += 2;
+ }
+
+ $tsum *= $ts;
+ if (($degrees % 2) == 1) {
+ $tsum = Functions::M_2DIVPI * ($tsum + $ttheta);
+ }
+
+ $tValue = 0.5 * (1 + $tsum);
+ if ($tails == 1) {
+ return 1 - abs($tValue);
+ }
+
+ return 1 - abs((1 - $tValue) - $tValue);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
new file mode 100644
index 00000000000..ecec8a85640
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
@@ -0,0 +1,49 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
+ return Functions::NAN();
+ }
+
+ if ($cumulative) {
+ return 1 - exp(0 - ($value / $beta) ** $alpha);
+ }
+
+ return ($alpha / $beta ** $alpha) * $value ** ($alpha - 1) * exp(0 - ($value / $beta) ** $alpha);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
new file mode 100644
index 00000000000..bd17b062923
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
@@ -0,0 +1,17 @@
+ $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ if ($returnValue === null) {
+ return 0;
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * MAXA.
+ *
+ * Returns the greatest value in a list of arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * MAXA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float
+ */
+ public static function maxA(...$args)
+ {
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ if (($returnValue === null) || ($arg > $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ if ($returnValue === null) {
+ return 0;
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php
new file mode 100644
index 00000000000..596aad78fdf
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php
@@ -0,0 +1,78 @@
+getMessage();
+ }
+
+ if (($entry < 0) || ($entry > 1)) {
+ return Functions::NAN();
+ }
+
+ $mArgs = self::percentileFilterValues($aArgs);
+ $mValueCount = count($mArgs);
+ if ($mValueCount > 0) {
+ sort($mArgs);
+ $count = Counts::COUNT($mArgs);
+ $index = $entry * ($count - 1);
+ $iBase = floor($index);
+ if ($index == $iBase) {
+ return $mArgs[$index];
+ }
+ $iNext = $iBase + 1;
+ $iProportion = $index - $iBase;
+
+ return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
+ }
+
+ return Functions::NAN();
+ }
+
+ /**
+ * PERCENTRANK.
+ *
+ * Returns the rank of a value in a data set as a percentage of the data set.
+ * Note that the returned rank is simply rounded to the appropriate significant digits,
+ * rather than floored (as MS Excel), so value 3 for a value set of 1, 2, 3, 4 will return
+ * 0.667 rather than 0.666
+ *
+ * @param mixed $valueSet An array of (float) values, or a reference to, a list of numbers
+ * @param mixed $value The number whose rank you want to find
+ * @param mixed $significance The (integer) number of significant digits for the returned percentage value
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function PERCENTRANK($valueSet, $value, $significance = 3)
+ {
+ $valueSet = Functions::flattenArray($valueSet);
+ $value = Functions::flattenSingleValue($value);
+ $significance = ($significance === null) ? 3 : Functions::flattenSingleValue($significance);
+
+ try {
+ $value = StatisticalValidations::validateFloat($value);
+ $significance = StatisticalValidations::validateInt($significance);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $valueSet = self::rankFilterValues($valueSet);
+ $valueCount = count($valueSet);
+ if ($valueCount == 0) {
+ return Functions::NA();
+ }
+ sort($valueSet, SORT_NUMERIC);
+
+ $valueAdjustor = $valueCount - 1;
+ if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
+ return Functions::NA();
+ }
+
+ $pos = array_search($value, $valueSet);
+ if ($pos === false) {
+ $pos = 0;
+ $testValue = $valueSet[0];
+ while ($testValue < $value) {
+ $testValue = $valueSet[++$pos];
+ }
+ --$pos;
+ $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
+ }
+
+ return round($pos / $valueAdjustor, $significance);
+ }
+
+ /**
+ * QUARTILE.
+ *
+ * Returns the quartile of a data set.
+ *
+ * Excel Function:
+ * QUARTILE(value1[,value2[, ...]],entry)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function QUARTILE(...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+ $entry = array_pop($aArgs);
+
+ try {
+ $entry = StatisticalValidations::validateFloat($entry);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $entry = floor($entry);
+ $entry /= 4;
+ if (($entry < 0) || ($entry > 1)) {
+ return Functions::NAN();
+ }
+
+ return self::PERCENTILE($aArgs, $entry);
+ }
+
+ /**
+ * RANK.
+ *
+ * Returns the rank of a number in a list of numbers.
+ *
+ * @param mixed $value The number whose rank you want to find
+ * @param mixed $valueSet An array of float values, or a reference to, a list of numbers
+ * @param mixed $order Order to sort the values in the value set
+ *
+ * @return float|string The result, or a string containing an error (0 = Descending, 1 = Ascending)
+ */
+ public static function RANK($value, $valueSet, $order = self::RANK_SORT_DESCENDING)
+ {
+ $value = Functions::flattenSingleValue($value);
+ $valueSet = Functions::flattenArray($valueSet);
+ $order = ($order === null) ? self::RANK_SORT_DESCENDING : Functions::flattenSingleValue($order);
+
+ try {
+ $value = StatisticalValidations::validateFloat($value);
+ $order = StatisticalValidations::validateInt($order);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $valueSet = self::rankFilterValues($valueSet);
+ if ($order === self::RANK_SORT_DESCENDING) {
+ rsort($valueSet, SORT_NUMERIC);
+ } else {
+ sort($valueSet, SORT_NUMERIC);
+ }
+
+ $pos = array_search($value, $valueSet);
+ if ($pos === false) {
+ return Functions::NA();
+ }
+
+ return ++$pos;
+ }
+
+ protected static function percentileFilterValues(array $dataSet)
+ {
+ return array_filter(
+ $dataSet,
+ function ($value): bool {
+ return is_numeric($value) && !is_string($value);
+ }
+ );
+ }
+
+ protected static function rankFilterValues(array $dataSet)
+ {
+ return array_filter(
+ $dataSet,
+ function ($value): bool {
+ return is_numeric($value);
+ }
+ );
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
new file mode 100644
index 00000000000..272a8a53dbf
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
@@ -0,0 +1,77 @@
+getMessage();
+ }
+
+ if ($numObjs < $numInSet) {
+ return Functions::NAN();
+ }
+ $result = round(MathTrig\Factorial::fact($numObjs) / MathTrig\Factorial::fact($numObjs - $numInSet));
+
+ return IntOrFloat::evaluate($result);
+ }
+
+ /**
+ * PERMUTATIONA.
+ *
+ * Returns the number of permutations for a given number of objects (with repetitions)
+ * that can be selected from the total objects.
+ *
+ * @param mixed $numObjs Integer number of different objects
+ * @param mixed $numInSet Integer number of objects in each permutation
+ *
+ * @return float|int|string Number of permutations, or a string containing an error
+ */
+ public static function PERMUTATIONA($numObjs, $numInSet)
+ {
+ $numObjs = Functions::flattenSingleValue($numObjs);
+ $numInSet = Functions::flattenSingleValue($numInSet);
+
+ try {
+ $numObjs = StatisticalValidations::validateInt($numObjs);
+ $numInSet = StatisticalValidations::validateInt($numInSet);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($numObjs < 0 || $numInSet < 0) {
+ return Functions::NAN();
+ }
+
+ $result = $numObjs ** $numInSet;
+
+ return IntOrFloat::evaluate($result);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
new file mode 100644
index 00000000000..de4b6d6c3d8
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
@@ -0,0 +1,96 @@
+= $count) || ($count == 0)) {
+ return Functions::NAN();
+ }
+ rsort($mArgs);
+
+ return $mArgs[$entry];
+ }
+
+ return Functions::VALUE();
+ }
+
+ /**
+ * SMALL.
+ *
+ * Returns the nth smallest value in a data set. You can use this function to
+ * select a value based on its relative standing.
+ *
+ * Excel Function:
+ * SMALL(value1[,value2[, ...]],entry)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function small(...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ $entry = array_pop($aArgs);
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $entry = (int) floor($entry);
+
+ $mArgs = self::filter($aArgs);
+ $count = Counts::COUNT($mArgs);
+ --$entry;
+ if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
+ return Functions::NAN();
+ }
+ sort($mArgs);
+
+ return $mArgs[$entry];
+ }
+
+ return Functions::VALUE();
+ }
+
+ /**
+ * @param mixed[] $args Data values
+ */
+ protected static function filter(array $args): array
+ {
+ $mArgs = [];
+
+ foreach ($args as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ return $mArgs;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
new file mode 100644
index 00000000000..af2712053f3
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
@@ -0,0 +1,95 @@
+getMessage();
+ }
+
+ if ($stdDev <= 0) {
+ return Functions::NAN();
+ }
+
+ return ($value - $mean) / $stdDev;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
new file mode 100644
index 00000000000..5b315da45c9
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
@@ -0,0 +1,45 @@
+ $value) {
+ if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
+ unset($array1[$key], $array2[$key]);
+ }
+ }
+ }
+
+ private static function checkTrendArrays(&$array1, &$array2): void
+ {
+ if (!is_array($array1)) {
+ $array1 = [$array1];
+ }
+ if (!is_array($array2)) {
+ $array2 = [$array2];
+ }
+
+ $array1 = Functions::flattenArray($array1);
+ $array2 = Functions::flattenArray($array2);
+
+ self::filterTrendValues($array1, $array2);
+ self::filterTrendValues($array2, $array1);
+
+ // Reset the array indexes
+ $array1 = array_merge($array1);
+ $array2 = array_merge($array2);
+ }
+
+ protected static function validateTrendArrays(array $yValues, array $xValues): void
+ {
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount === 0) || ($yValueCount !== $xValueCount)) {
+ throw new Exception(Functions::NA());
+ } elseif ($yValueCount === 1) {
+ throw new Exception(Functions::DIV0());
+ }
+ }
+
+ /**
+ * CORREL.
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param mixed $yValues array of mixed Data Series Y
+ * @param null|mixed $xValues array of mixed Data Series X
+ *
+ * @return float|string
+ */
+ public static function CORREL($yValues, $xValues = null)
+ {
+ if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
+ return Functions::VALUE();
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getCorrelation();
+ }
+
+ /**
+ * COVAR.
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param mixed $yValues array of mixed Data Series Y
+ * @param mixed $xValues array of mixed Data Series X
+ *
+ * @return float|string
+ */
+ public static function COVAR($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getCovariance();
+ }
+
+ /**
+ * FORECAST.
+ *
+ * Calculates, or predicts, a future value by using existing values.
+ * The predicted value is a y-value for a given x-value.
+ *
+ * @param mixed $xValue Float value of X for which we want to find Y
+ * @param mixed $yValues array of mixed Data Series Y
+ * @param mixed $xValues of mixed Data Series X
+ *
+ * @return bool|float|string
+ */
+ public static function FORECAST($xValue, $yValues, $xValues)
+ {
+ $xValue = Functions::flattenSingleValue($xValue);
+
+ try {
+ $xValue = StatisticalValidations::validateFloat($xValue);
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getValueOfYForX($xValue);
+ }
+
+ /**
+ * GROWTH.
+ *
+ * Returns values along a predicted exponential Trend
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ * @param mixed[] $newValues Values of X for which we want to find Y
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ *
+ * @return float[]
+ */
+ public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
+ {
+ $yValues = Functions::flattenArray($yValues);
+ $xValues = Functions::flattenArray($xValues);
+ $newValues = Functions::flattenArray($newValues);
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+
+ $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
+ if (empty($newValues)) {
+ $newValues = $bestFitExponential->getXValues();
+ }
+
+ $returnArray = [];
+ foreach ($newValues as $xValue) {
+ $returnArray[0][] = [$bestFitExponential->getValueOfYForX($xValue)];
+ }
+
+ return $returnArray;
+ }
+
+ /**
+ * INTERCEPT.
+ *
+ * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string
+ */
+ public static function INTERCEPT($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getIntersect();
+ }
+
+ /**
+ * LINEST.
+ *
+ * Calculates the statistics for a line by using the "least squares" method to calculate a straight line
+ * that best fits your data, and then returns an array that describes the line.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param null|mixed[] $xValues Data Series X
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
+ *
+ * @return array|int|string The result, or a string containing an error
+ */
+ public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
+ {
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+ $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
+ if ($xValues === null) {
+ $xValues = $yValues;
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
+
+ if ($stats === true) {
+ return [
+ [
+ $bestFitLinear->getSlope(),
+ $bestFitLinear->getIntersect(),
+ ],
+ [
+ $bestFitLinear->getSlopeSE(),
+ ($const === false) ? Functions::NA() : $bestFitLinear->getIntersectSE(),
+ ],
+ [
+ $bestFitLinear->getGoodnessOfFit(),
+ $bestFitLinear->getStdevOfResiduals(),
+ ],
+ [
+ $bestFitLinear->getF(),
+ $bestFitLinear->getDFResiduals(),
+ ],
+ [
+ $bestFitLinear->getSSRegression(),
+ $bestFitLinear->getSSResiduals(),
+ ],
+ ];
+ }
+
+ return [
+ $bestFitLinear->getSlope(),
+ $bestFitLinear->getIntersect(),
+ ];
+ }
+
+ /**
+ * LOGEST.
+ *
+ * Calculates an exponential curve that best fits the X and Y data series,
+ * and then returns an array that describes the line.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param null|mixed[] $xValues Data Series X
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
+ *
+ * @return array|int|string The result, or a string containing an error
+ */
+ public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
+ {
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+ $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
+ if ($xValues === null) {
+ $xValues = $yValues;
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ foreach ($yValues as $value) {
+ if ($value < 0.0) {
+ return Functions::NAN();
+ }
+ }
+
+ $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
+
+ if ($stats === true) {
+ return [
+ [
+ $bestFitExponential->getSlope(),
+ $bestFitExponential->getIntersect(),
+ ],
+ [
+ $bestFitExponential->getSlopeSE(),
+ ($const === false) ? Functions::NA() : $bestFitExponential->getIntersectSE(),
+ ],
+ [
+ $bestFitExponential->getGoodnessOfFit(),
+ $bestFitExponential->getStdevOfResiduals(),
+ ],
+ [
+ $bestFitExponential->getF(),
+ $bestFitExponential->getDFResiduals(),
+ ],
+ [
+ $bestFitExponential->getSSRegression(),
+ $bestFitExponential->getSSResiduals(),
+ ],
+ ];
+ }
+
+ return [
+ $bestFitExponential->getSlope(),
+ $bestFitExponential->getIntersect(),
+ ];
+ }
+
+ /**
+ * RSQ.
+ *
+ * Returns the square of the Pearson product moment correlation coefficient through data points
+ * in known_y's and known_x's.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function RSQ($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getGoodnessOfFit();
+ }
+
+ /**
+ * SLOPE.
+ *
+ * Returns the slope of the linear regression line through data points in known_y's and known_x's.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function SLOPE($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getSlope();
+ }
+
+ /**
+ * STEYX.
+ *
+ * Returns the standard error of the predicted y-value for each x in the regression.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string
+ */
+ public static function STEYX($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getStdevOfResiduals();
+ }
+
+ /**
+ * TREND.
+ *
+ * Returns values along a linear Trend
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ * @param mixed[] $newValues Values of X for which we want to find Y
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ *
+ * @return float[]
+ */
+ public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
+ {
+ $yValues = Functions::flattenArray($yValues);
+ $xValues = Functions::flattenArray($xValues);
+ $newValues = Functions::flattenArray($newValues);
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
+ if (empty($newValues)) {
+ $newValues = $bestFitLinear->getXValues();
+ }
+
+ $returnArray = [];
+ foreach ($newValues as $xValue) {
+ $returnArray[0][] = [$bestFitLinear->getValueOfYForX($xValue)];
+ }
+
+ return $returnArray;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
new file mode 100644
index 00000000000..e53346719c6
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
@@ -0,0 +1,28 @@
+ 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARA.
+ *
+ * Estimates variance based on a sample, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARA(...$args)
+ {
+ $returnValue = Functions::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) && (Functions::isValue($k))) {
+ return Functions::VALUE();
+ } elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ if ($aCount > 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARP.
+ *
+ * Calculates variance based on the entire population
+ *
+ * Excel Function:
+ * VARP(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARP(...$args)
+ {
+ // Return value
+ $returnValue = Functions::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ $arg = self::datatypeAdjustmentBooleans($arg);
+
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * $aCount);
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARPA.
+ *
+ * Calculates variance based on the entire population, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARPA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARPA(...$args)
+ {
+ $returnValue = Functions::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) && (Functions::isValue($k))) {
+ return Functions::VALUE();
+ } elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * $aCount);
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
index f89744029e7..0bde3b7fe48 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
@@ -3,141 +3,88 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
use DateTimeInterface;
-use PhpOffice\PhpSpreadsheet\Shared\Date;
-use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
-use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+/**
+ * @deprecated 1.18.0
+ */
class TextData
{
- private static $invalidChars;
-
- private static function unicodeToOrd($character)
- {
- return unpack('V', iconv('UTF-8', 'UCS-4LE', $character))[1];
- }
-
/**
* CHARACTER.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the character() method in the TextData\CharacterConvert class instead
+ *
* @param string $character Value
*
* @return string
*/
public static function CHARACTER($character)
{
- $character = Functions::flattenSingleValue($character);
-
- if (!is_numeric($character)) {
- return Functions::VALUE();
- }
- $character = (int) $character;
- if ($character < 1 || $character > 255) {
- return Functions::VALUE();
- }
-
- return iconv('UCS-4LE', 'UTF-8', pack('V', $character));
+ return TextData\CharacterConvert::character($character);
}
/**
* TRIMNONPRINTABLE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the nonPrintable() method in the TextData\Trim class instead
+ *
* @param mixed $stringValue Value to check
*
* @return string
*/
public static function TRIMNONPRINTABLE($stringValue = '')
{
- $stringValue = Functions::flattenSingleValue($stringValue);
-
- if (is_bool($stringValue)) {
- return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (self::$invalidChars === null) {
- self::$invalidChars = range(chr(0), chr(31));
- }
-
- if (is_string($stringValue) || is_numeric($stringValue)) {
- return str_replace(self::$invalidChars, '', trim($stringValue, "\x00..\x1F"));
- }
-
- return null;
+ return TextData\Trim::nonPrintable($stringValue);
}
/**
* TRIMSPACES.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the spaces() method in the TextData\Trim class instead
+ *
* @param mixed $stringValue Value to check
*
* @return string
*/
public static function TRIMSPACES($stringValue = '')
{
- $stringValue = Functions::flattenSingleValue($stringValue);
- if (is_bool($stringValue)) {
- return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (is_string($stringValue) || is_numeric($stringValue)) {
- return trim(preg_replace('/ +/', ' ', trim($stringValue, ' ')), ' ');
- }
-
- return null;
- }
-
- private static function convertBooleanValue($value)
- {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- return (int) $value;
- }
-
- return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
+ return TextData\Trim::spaces($stringValue);
}
/**
* ASCIICODE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the code() method in the TextData\CharacterConvert class instead
+ *
* @param string $characters Value
*
* @return int|string A string if arguments are invalid
*/
public static function ASCIICODE($characters)
{
- if (($characters === null) || ($characters === '')) {
- return Functions::VALUE();
- }
- $characters = Functions::flattenSingleValue($characters);
- if (is_bool($characters)) {
- $characters = self::convertBooleanValue($characters);
- }
-
- $character = $characters;
- if (mb_strlen($characters, 'UTF-8') > 1) {
- $character = mb_substr($characters, 0, 1, 'UTF-8');
- }
-
- return self::unicodeToOrd($character);
+ return TextData\CharacterConvert::code($characters);
}
/**
* CONCATENATE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the CONCATENATE() method in the TextData\Concatenate class instead
+ *
* @return string
*/
public static function CONCATENATE(...$args)
{
- $returnValue = '';
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- if (is_bool($arg)) {
- $arg = self::convertBooleanValue($arg);
- }
- $returnValue .= $arg;
- }
-
- return $returnValue;
+ return TextData\Concatenate::CONCATENATE(...$args);
}
/**
@@ -146,6 +93,10 @@ class TextData
* This function converts a number to text using currency format, with the decimals rounded to the specified place.
* The format used is $#,##0.00_);($#,##0.00)..
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the DOLLAR() method in the TextData\Format class instead
+ *
* @param float $value The value to format
* @param int $decimals The number of digits to display to the right of the decimal point.
* If decimals is negative, number is rounded to the left of the decimal point.
@@ -155,33 +106,16 @@ class TextData
*/
public static function DOLLAR($value = 0, $decimals = 2)
{
- $value = Functions::flattenSingleValue($value);
- $decimals = $decimals === null ? 0 : Functions::flattenSingleValue($decimals);
-
- // Validate parameters
- if (!is_numeric($value) || !is_numeric($decimals)) {
- return Functions::VALUE();
- }
- $decimals = floor($decimals);
-
- $mask = '$#,##0';
- if ($decimals > 0) {
- $mask .= '.' . str_repeat('0', $decimals);
- } else {
- $round = 10 ** abs($decimals);
- if ($value < 0) {
- $round = 0 - $round;
- }
- $value = MathTrig::MROUND($value, $round);
- }
- $mask = "$mask;($mask)";
-
- return NumberFormat::toFormattedString($value, $mask);
+ return TextData\Format::DOLLAR($value, $decimals);
}
/**
* SEARCHSENSITIVE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the sensitive() method in the TextData\Search class instead
+ *
* @param string $needle The string to look for
* @param string $haystack The string in which to look
* @param int $offset Offset within $haystack
@@ -190,33 +124,16 @@ class TextData
*/
public static function SEARCHSENSITIVE($needle, $haystack, $offset = 1)
{
- $needle = Functions::flattenSingleValue($needle);
- $haystack = Functions::flattenSingleValue($haystack);
- $offset = Functions::flattenSingleValue($offset);
-
- if (!is_bool($needle)) {
- if (is_bool($haystack)) {
- $haystack = ($haystack) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
- if (StringHelper::countCharacters($needle) === 0) {
- return $offset;
- }
-
- $pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8');
- if ($pos !== false) {
- return ++$pos;
- }
- }
- }
-
- return Functions::VALUE();
+ return TextData\Search::sensitive($needle, $haystack, $offset);
}
/**
* SEARCHINSENSITIVE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the insensitive() method in the TextData\Search class instead
+ *
* @param string $needle The string to look for
* @param string $haystack The string in which to look
* @param int $offset Offset within $haystack
@@ -225,33 +142,16 @@ class TextData
*/
public static function SEARCHINSENSITIVE($needle, $haystack, $offset = 1)
{
- $needle = Functions::flattenSingleValue($needle);
- $haystack = Functions::flattenSingleValue($haystack);
- $offset = Functions::flattenSingleValue($offset);
-
- if (!is_bool($needle)) {
- if (is_bool($haystack)) {
- $haystack = ($haystack) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
- if (StringHelper::countCharacters($needle) === 0) {
- return $offset;
- }
-
- $pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8');
- if ($pos !== false) {
- return ++$pos;
- }
- }
- }
-
- return Functions::VALUE();
+ return TextData\Search::insensitive($needle, $haystack, $offset);
}
/**
* FIXEDFORMAT.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the FIXEDFORMAT() method in the TextData\Format class instead
+ *
* @param mixed $value Value to check
* @param int $decimals
* @param bool $no_commas
@@ -260,35 +160,16 @@ class TextData
*/
public static function FIXEDFORMAT($value, $decimals = 2, $no_commas = false)
{
- $value = Functions::flattenSingleValue($value);
- $decimals = Functions::flattenSingleValue($decimals);
- $no_commas = Functions::flattenSingleValue($no_commas);
-
- // Validate parameters
- if (!is_numeric($value) || !is_numeric($decimals)) {
- return Functions::VALUE();
- }
- $decimals = (int) floor($decimals);
-
- $valueResult = round($value, $decimals);
- if ($decimals < 0) {
- $decimals = 0;
- }
- if (!$no_commas) {
- $valueResult = number_format(
- $valueResult,
- $decimals,
- StringHelper::getDecimalSeparator(),
- StringHelper::getThousandsSeparator()
- );
- }
-
- return (string) $valueResult;
+ return TextData\Format::FIXEDFORMAT($value, $decimals, $no_commas);
}
/**
* LEFT.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the left() method in the TextData\Extract class instead
+ *
* @param string $value Value
* @param int $chars Number of characters
*
@@ -296,23 +177,16 @@ class TextData
*/
public static function LEFT($value = '', $chars = 1)
{
- $value = Functions::flattenSingleValue($value);
- $chars = Functions::flattenSingleValue($chars);
-
- if ($chars < 0) {
- return Functions::VALUE();
- }
-
- if (is_bool($value)) {
- $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return mb_substr($value, 0, $chars, 'UTF-8');
+ return TextData\Extract::left($value, $chars);
}
/**
* MID.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the mid() method in the TextData\Extract class instead
+ *
* @param string $value Value
* @param int $start Start character
* @param int $chars Number of characters
@@ -321,28 +195,16 @@ class TextData
*/
public static function MID($value = '', $start = 1, $chars = null)
{
- $value = Functions::flattenSingleValue($value);
- $start = Functions::flattenSingleValue($start);
- $chars = Functions::flattenSingleValue($chars);
-
- if (($start < 1) || ($chars < 0)) {
- return Functions::VALUE();
- }
-
- if (is_bool($value)) {
- $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (empty($chars)) {
- return '';
- }
-
- return mb_substr($value, --$start, $chars, 'UTF-8');
+ return TextData\Extract::mid($value, $start, $chars);
}
/**
* RIGHT.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the right() method in the TextData\Extract class instead
+ *
* @param string $value Value
* @param int $chars Number of characters
*
@@ -350,36 +212,23 @@ class TextData
*/
public static function RIGHT($value = '', $chars = 1)
{
- $value = Functions::flattenSingleValue($value);
- $chars = Functions::flattenSingleValue($chars);
-
- if ($chars < 0) {
- return Functions::VALUE();
- }
-
- if (is_bool($value)) {
- $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
+ return TextData\Extract::right($value, $chars);
}
/**
* STRINGLENGTH.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the length() method in the TextData\Text class instead
+ *
* @param string $value Value
*
* @return int
*/
public static function STRINGLENGTH($value = '')
{
- $value = Functions::flattenSingleValue($value);
-
- if (is_bool($value)) {
- $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return mb_strlen($value, 'UTF-8');
+ return TextData\Text::length($value);
}
/**
@@ -387,19 +236,17 @@ class TextData
*
* Converts a string value to upper case.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the lower() method in the TextData\CaseConvert class instead
+ *
* @param string $mixedCaseString
*
* @return string
*/
public static function LOWERCASE($mixedCaseString)
{
- $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
-
- if (is_bool($mixedCaseString)) {
- $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return StringHelper::strToLower($mixedCaseString);
+ return TextData\CaseConvert::lower($mixedCaseString);
}
/**
@@ -407,19 +254,17 @@ class TextData
*
* Converts a string value to upper case.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the upper() method in the TextData\CaseConvert class instead
+ *
* @param string $mixedCaseString
*
* @return string
*/
public static function UPPERCASE($mixedCaseString)
{
- $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
-
- if (is_bool($mixedCaseString)) {
- $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return StringHelper::strToUpper($mixedCaseString);
+ return TextData\CaseConvert::upper($mixedCaseString);
}
/**
@@ -427,24 +272,26 @@ class TextData
*
* Converts a string value to upper case.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the proper() method in the TextData\CaseConvert class instead
+ *
* @param string $mixedCaseString
*
* @return string
*/
public static function PROPERCASE($mixedCaseString)
{
- $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
-
- if (is_bool($mixedCaseString)) {
- $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return StringHelper::strToTitle($mixedCaseString);
+ return TextData\CaseConvert::proper($mixedCaseString);
}
/**
* REPLACE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the replace() method in the TextData\Replace class instead
+ *
* @param string $oldText String to modify
* @param int $start Start character
* @param int $chars Number of characters
@@ -454,20 +301,16 @@ class TextData
*/
public static function REPLACE($oldText, $start, $chars, $newText)
{
- $oldText = Functions::flattenSingleValue($oldText);
- $start = Functions::flattenSingleValue($start);
- $chars = Functions::flattenSingleValue($chars);
- $newText = Functions::flattenSingleValue($newText);
-
- $left = self::LEFT($oldText, $start - 1);
- $right = self::RIGHT($oldText, self::STRINGLENGTH($oldText) - ($start + $chars) + 1);
-
- return $left . $newText . $right;
+ return TextData\Replace::replace($oldText, $start, $chars, $newText);
}
/**
* SUBSTITUTE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the substitute() method in the TextData\Replace class instead
+ *
* @param string $text Value
* @param string $fromText From Value
* @param string $toText To Value
@@ -477,52 +320,32 @@ class TextData
*/
public static function SUBSTITUTE($text = '', $fromText = '', $toText = '', $instance = 0)
{
- $text = Functions::flattenSingleValue($text);
- $fromText = Functions::flattenSingleValue($fromText);
- $toText = Functions::flattenSingleValue($toText);
- $instance = floor(Functions::flattenSingleValue($instance));
-
- if ($instance == 0) {
- return str_replace($fromText, $toText, $text);
- }
-
- $pos = -1;
- while ($instance > 0) {
- $pos = mb_strpos($text, $fromText, $pos + 1, 'UTF-8');
- if ($pos === false) {
- break;
- }
- --$instance;
- }
-
- if ($pos !== false) {
- return self::REPLACE($text, ++$pos, mb_strlen($fromText, 'UTF-8'), $toText);
- }
-
- return $text;
+ return TextData\Replace::substitute($text, $fromText, $toText, $instance);
}
/**
* RETURNSTRING.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the test() method in the TextData\Text class instead
+ *
* @param mixed $testValue Value to check
*
* @return null|string
*/
public static function RETURNSTRING($testValue = '')
{
- $testValue = Functions::flattenSingleValue($testValue);
-
- if (is_string($testValue)) {
- return $testValue;
- }
-
- return null;
+ return TextData\Text::test($testValue);
}
/**
* TEXTFORMAT.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the TEXTFORMAT() method in the TextData\Format class instead
+ *
* @param mixed $value Value to check
* @param string $format Format mask to use
*
@@ -530,65 +353,32 @@ class TextData
*/
public static function TEXTFORMAT($value, $format)
{
- $value = Functions::flattenSingleValue($value);
- $format = Functions::flattenSingleValue($format);
-
- if ((is_string($value)) && (!is_numeric($value)) && Date::isDateTimeFormatCode($format)) {
- $value = DateTime::DATEVALUE($value);
- }
-
- return (string) NumberFormat::toFormattedString($value, $format);
+ return TextData\Format::TEXTFORMAT($value, $format);
}
/**
* VALUE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the VALUE() method in the TextData\Format class instead
+ *
* @param mixed $value Value to check
*
* @return DateTimeInterface|float|int|string A string if arguments are invalid
*/
public static function VALUE($value = '')
{
- $value = Functions::flattenSingleValue($value);
-
- if (!is_numeric($value)) {
- $numberValue = str_replace(
- StringHelper::getThousandsSeparator(),
- '',
- trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
- );
- if (is_numeric($numberValue)) {
- return (float) $numberValue;
- }
-
- $dateSetting = Functions::getReturnDateType();
- Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
-
- if (strpos($value, ':') !== false) {
- $timeValue = DateTime::TIMEVALUE($value);
- if ($timeValue !== Functions::VALUE()) {
- Functions::setReturnDateType($dateSetting);
-
- return $timeValue;
- }
- }
- $dateValue = DateTime::DATEVALUE($value);
- if ($dateValue !== Functions::VALUE()) {
- Functions::setReturnDateType($dateSetting);
-
- return $dateValue;
- }
- Functions::setReturnDateType($dateSetting);
-
- return Functions::VALUE();
- }
-
- return (float) $value;
+ return TextData\Format::VALUE($value);
}
/**
* NUMBERVALUE.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the NUMBERVALUE() method in the TextData\Format class instead
+ *
* @param mixed $value Value to check
* @param string $decimalSeparator decimal separator, defaults to locale defined value
* @param string $groupSeparator group/thosands separator, defaults to locale defined value
@@ -597,39 +387,7 @@ class TextData
*/
public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
{
- $value = Functions::flattenSingleValue($value);
- $decimalSeparator = Functions::flattenSingleValue($decimalSeparator);
- $groupSeparator = Functions::flattenSingleValue($groupSeparator);
-
- if (!is_numeric($value)) {
- $decimalSeparator = empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : $decimalSeparator;
- $groupSeparator = empty($groupSeparator) ? StringHelper::getThousandsSeparator() : $groupSeparator;
-
- $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator) . '/', $value, $matches, PREG_OFFSET_CAPTURE);
- if ($decimalPositions > 1) {
- return Functions::VALUE();
- }
- $decimalOffset = array_pop($matches[0])[1];
- if (strpos($value, $groupSeparator, $decimalOffset) !== false) {
- return Functions::VALUE();
- }
-
- $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
-
- // Handle the special case of trailing % signs
- $percentageString = rtrim($value, '%');
- if (!is_numeric($percentageString)) {
- return Functions::VALUE();
- }
-
- $percentageAdjustment = strlen($value) - strlen($percentageString);
- if ($percentageAdjustment) {
- $value = (float) $percentageString;
- $value /= 10 ** ($percentageAdjustment * 2);
- }
- }
-
- return (float) $value;
+ return TextData\Format::NUMBERVALUE($value, $decimalSeparator, $groupSeparator);
}
/**
@@ -637,22 +395,27 @@ class TextData
* EXACT is case-sensitive but ignores formatting differences.
* Use EXACT to test text being entered into a document.
*
- * @param $value1
- * @param $value2
+ * @Deprecated 1.18.0
+ *
+ * @see Use the exact() method in the TextData\Text class instead
+ *
+ * @param mixed $value1
+ * @param mixed $value2
*
* @return bool
*/
public static function EXACT($value1, $value2)
{
- $value1 = Functions::flattenSingleValue($value1);
- $value2 = Functions::flattenSingleValue($value2);
-
- return (string) $value2 === (string) $value1;
+ return TextData\Text::exact($value1, $value2);
}
/**
* TEXTJOIN.
*
+ * @Deprecated 1.18.0
+ *
+ * @see Use the TEXTJOIN() method in the TextData\Concatenate class instead
+ *
* @param mixed $delimiter
* @param mixed $ignoreEmpty
* @param mixed $args
@@ -661,16 +424,25 @@ class TextData
*/
public static function TEXTJOIN($delimiter, $ignoreEmpty, ...$args)
{
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $key => &$arg) {
- if ($ignoreEmpty && trim($arg) == '') {
- unset($aArgs[$key]);
- } elseif (is_bool($arg)) {
- $arg = self::convertBooleanValue($arg);
- }
- }
+ return TextData\Concatenate::TEXTJOIN($delimiter, $ignoreEmpty, ...$args);
+ }
- return implode($delimiter, $aArgs);
+ /**
+ * REPT.
+ *
+ * Returns the result of builtin function repeat after validating args.
+ *
+ * @Deprecated 1.18.0
+ *
+ * @see Use the builtinREPT() method in the TextData\Concatenate class instead
+ *
+ * @param string $str Should be numeric
+ * @param mixed $number Should be int
+ *
+ * @return string
+ */
+ public static function builtinREPT($str, $number)
+ {
+ return TextData\Concatenate::builtinREPT($str, $number);
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
new file mode 100644
index 00000000000..664cc2d883f
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
@@ -0,0 +1,54 @@
+ 255) {
+ return Functions::VALUE();
+ }
+ $result = iconv('UCS-4LE', 'UTF-8', pack('V', $character));
+
+ return ($result === false) ? '' : $result;
+ }
+
+ /**
+ * CODE.
+ *
+ * @param mixed $characters String character to convert to its ASCII value
+ *
+ * @return int|string A string if arguments are invalid
+ */
+ public static function code($characters)
+ {
+ $characters = Helpers::extractString($characters);
+ if ($characters === '') {
+ return Functions::VALUE();
+ }
+
+ $character = $characters;
+ if (mb_strlen($characters, 'UTF-8') > 1) {
+ $character = mb_substr($characters, 0, 1, 'UTF-8');
+ }
+
+ return self::unicodeToOrd($character);
+ }
+
+ private static function unicodeToOrd(string $character): int
+ {
+ $retVal = 0;
+ $iconv = iconv('UTF-8', 'UCS-4LE', $character);
+ if ($iconv !== false) {
+ $result = unpack('V', $iconv);
+ if (is_array($result) && isset($result[1])) {
+ $retVal = $result[1];
+ }
+ }
+
+ return $retVal;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php
new file mode 100644
index 00000000000..d53fc822898
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php
@@ -0,0 +1,71 @@
+ &$arg) {
+ if ($ignoreEmpty === true && is_string($arg) && trim($arg) === '') {
+ unset($aArgs[$key]);
+ } elseif (is_bool($arg)) {
+ $arg = Helpers::convertBooleanValue($arg);
+ }
+ }
+
+ return implode($delimiter, $aArgs);
+ }
+
+ /**
+ * REPT.
+ *
+ * Returns the result of builtin function round after validating args.
+ *
+ * @param mixed $stringValue The value to repeat
+ * @param mixed $repeatCount The number of times the string value should be repeated
+ */
+ public static function builtinREPT($stringValue, $repeatCount): string
+ {
+ $repeatCount = Functions::flattenSingleValue($repeatCount);
+ $stringValue = Helpers::extractString($stringValue);
+
+ if (!is_numeric($repeatCount) || $repeatCount < 0) {
+ return Functions::VALUE();
+ }
+
+ return str_repeat($stringValue, (int) $repeatCount);
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php
new file mode 100644
index 00000000000..7f18e0c6e7c
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ return mb_substr($value ?? '', 0, $chars, 'UTF-8');
+ }
+
+ /**
+ * MID.
+ *
+ * @param mixed $value String value from which to extract characters
+ * @param mixed $start Integer offset of the first character that we want to extract
+ * @param mixed $chars The number of characters to extract (as an integer)
+ */
+ public static function mid($value, $start, $chars): string
+ {
+ try {
+ $value = Helpers::extractString($value);
+ $start = Helpers::extractInt($start, 1);
+ $chars = Helpers::extractInt($chars, 0);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ return mb_substr($value ?? '', --$start, $chars, 'UTF-8');
+ }
+
+ /**
+ * RIGHT.
+ *
+ * @param mixed $value String value from which to extract characters
+ * @param mixed $chars The number of characters to extract (as an integer)
+ */
+ public static function right($value, $chars = 1): string
+ {
+ try {
+ $value = Helpers::extractString($value);
+ $chars = Helpers::extractInt($chars, 0, 1);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ return mb_substr($value ?? '', mb_strlen($value ?? '', 'UTF-8') - $chars, $chars, 'UTF-8');
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php
new file mode 100644
index 00000000000..3286de0cb03
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php
@@ -0,0 +1,236 @@
+getMessage();
+ }
+
+ $mask = '$#,##0';
+ if ($decimals > 0) {
+ $mask .= '.' . str_repeat('0', $decimals);
+ } else {
+ $round = 10 ** abs($decimals);
+ if ($value < 0) {
+ $round = 0 - $round;
+ }
+ $value = MathTrig\Round::multiple($value, $round);
+ }
+ $mask = "{$mask};-{$mask}";
+
+ return NumberFormat::toFormattedString($value, $mask);
+ }
+
+ /**
+ * FIXED.
+ *
+ * @param mixed $value The value to format
+ * @param mixed $decimals Integer value for the number of decimal places that should be formatted
+ * @param mixed $noCommas Boolean value indicating whether the value should have thousands separators or not
+ */
+ public static function FIXEDFORMAT($value, $decimals = 2, $noCommas = false): string
+ {
+ try {
+ $value = Helpers::extractFloat($value);
+ $decimals = Helpers::extractInt($decimals, -100, 0, true);
+ $noCommas = Functions::flattenSingleValue($noCommas);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ $valueResult = round($value, $decimals);
+ if ($decimals < 0) {
+ $decimals = 0;
+ }
+ if ($noCommas === false) {
+ $valueResult = number_format(
+ $valueResult,
+ $decimals,
+ StringHelper::getDecimalSeparator(),
+ StringHelper::getThousandsSeparator()
+ );
+ }
+
+ return (string) $valueResult;
+ }
+
+ /**
+ * TEXT.
+ *
+ * @param mixed $value The value to format
+ * @param mixed $format A string with the Format mask that should be used
+ */
+ public static function TEXTFORMAT($value, $format): string
+ {
+ $value = Helpers::extractString($value);
+ $format = Helpers::extractString($format);
+
+ if (!is_numeric($value) && Date::isDateTimeFormatCode($format)) {
+ $value = DateTimeExcel\DateValue::fromString($value);
+ }
+
+ return (string) NumberFormat::toFormattedString($value, $format);
+ }
+
+ /**
+ * @param mixed $value Value to check
+ *
+ * @return mixed
+ */
+ private static function convertValue($value)
+ {
+ $value = ($value === null) ? 0 : Functions::flattenSingleValue($value);
+ if (is_bool($value)) {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ $value = (int) $value;
+ } else {
+ throw new CalcExp(Functions::VALUE());
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * VALUE.
+ *
+ * @param mixed $value Value to check
+ *
+ * @return DateTimeInterface|float|int|string A string if arguments are invalid
+ */
+ public static function VALUE($value = '')
+ {
+ try {
+ $value = self::convertValue($value);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+ if (!is_numeric($value)) {
+ $numberValue = str_replace(
+ StringHelper::getThousandsSeparator(),
+ '',
+ trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
+ );
+ if (is_numeric($numberValue)) {
+ return (float) $numberValue;
+ }
+
+ $dateSetting = Functions::getReturnDateType();
+ Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
+
+ if (strpos($value, ':') !== false) {
+ $timeValue = DateTimeExcel\TimeValue::fromString($value);
+ if ($timeValue !== Functions::VALUE()) {
+ Functions::setReturnDateType($dateSetting);
+
+ return $timeValue;
+ }
+ }
+ $dateValue = DateTimeExcel\DateValue::fromString($value);
+ if ($dateValue !== Functions::VALUE()) {
+ Functions::setReturnDateType($dateSetting);
+
+ return $dateValue;
+ }
+ Functions::setReturnDateType($dateSetting);
+
+ return Functions::VALUE();
+ }
+
+ return (float) $value;
+ }
+
+ /**
+ * @param mixed $decimalSeparator
+ */
+ private static function getDecimalSeparator($decimalSeparator): string
+ {
+ $decimalSeparator = Functions::flattenSingleValue($decimalSeparator);
+
+ return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator;
+ }
+
+ /**
+ * @param mixed $groupSeparator
+ */
+ private static function getGroupSeparator($groupSeparator): string
+ {
+ $groupSeparator = Functions::flattenSingleValue($groupSeparator);
+
+ return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator;
+ }
+
+ /**
+ * NUMBERVALUE.
+ *
+ * @param mixed $value The value to format
+ * @param mixed $decimalSeparator A string with the decimal separator to use, defaults to locale defined value
+ * @param mixed $groupSeparator A string with the group/thousands separator to use, defaults to locale defined value
+ *
+ * @return float|string
+ */
+ public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
+ {
+ try {
+ $value = self::convertValue($value);
+ $decimalSeparator = self::getDecimalSeparator($decimalSeparator);
+ $groupSeparator = self::getGroupSeparator($groupSeparator);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ if (!is_numeric($value)) {
+ $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator) . '/', $value, $matches, PREG_OFFSET_CAPTURE);
+ if ($decimalPositions > 1) {
+ return Functions::VALUE();
+ }
+ $decimalOffset = array_pop($matches[0])[1];
+ if (strpos($value, $groupSeparator, $decimalOffset) !== false) {
+ return Functions::VALUE();
+ }
+
+ $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
+
+ // Handle the special case of trailing % signs
+ $percentageString = rtrim($value, '%');
+ if (!is_numeric($percentageString)) {
+ return Functions::VALUE();
+ }
+
+ $percentageAdjustment = strlen($value) - strlen($percentageString);
+ if ($percentageAdjustment) {
+ $value = (float) $percentageString;
+ $value /= 10 ** ($percentageAdjustment * 2);
+ }
+ }
+
+ return is_array($value) ? Functions::VALUE() : (float) $value;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php
new file mode 100644
index 00000000000..423b6d3f90b
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php
@@ -0,0 +1,90 @@
+getMessage();
+ }
+
+ return $left . $newText . $right;
+ }
+
+ /**
+ * SUBSTITUTE.
+ *
+ * @param mixed $text The text string value to modify
+ * @param mixed $fromText The string value that we want to replace in $text
+ * @param mixed $toText The string value that we want to replace with in $text
+ * @param mixed $instance Integer instance Number for the occurrence of frmText to change
+ */
+ public static function substitute($text = '', $fromText = '', $toText = '', $instance = null): string
+ {
+ try {
+ $text = Helpers::extractString($text);
+ $fromText = Helpers::extractString($fromText);
+ $toText = Helpers::extractString($toText);
+ $instance = Functions::flattenSingleValue($instance);
+ if ($instance === null) {
+ return str_replace($fromText, $toText, $text);
+ }
+ if (is_bool($instance)) {
+ if ($instance === false || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
+ return Functions::Value();
+ }
+ $instance = 1;
+ }
+ $instance = Helpers::extractInt($instance, 1, 0, true);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ $pos = -1;
+ while ($instance > 0) {
+ $pos = mb_strpos($text, $fromText, $pos + 1, 'UTF-8');
+ if ($pos === false) {
+ break;
+ }
+ --$instance;
+ }
+
+ if ($pos !== false) {
+ return self::REPLACE($text, ++$pos, mb_strlen($fromText, 'UTF-8'), $toText);
+ }
+
+ return $text;
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
new file mode 100644
index 00000000000..c9eed2e5a5a
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
@@ -0,0 +1,76 @@
+getMessage();
+ }
+
+ if (StringHelper::countCharacters($haystack) >= $offset) {
+ if (StringHelper::countCharacters($needle) === 0) {
+ return $offset;
+ }
+
+ $pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8');
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+
+ return Functions::VALUE();
+ }
+
+ /**
+ * SEARCH (case insensitive search).
+ *
+ * @param mixed $needle The string to look for
+ * @param mixed $haystack The string in which to look
+ * @param mixed $offset Integer offset within $haystack to start searching from
+ *
+ * @return int|string
+ */
+ public static function insensitive($needle, $haystack, $offset = 1)
+ {
+ try {
+ $needle = Helpers::extractString($needle);
+ $haystack = Helpers::extractString($haystack);
+ $offset = Helpers::extractInt($offset, 1, 0, true);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ if (StringHelper::countCharacters($haystack) >= $offset) {
+ if (StringHelper::countCharacters($needle) === 0) {
+ return $offset;
+ }
+
+ $pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8');
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+
+ return Functions::VALUE();
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
new file mode 100644
index 00000000000..6f8253eabe8
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
@@ -0,0 +1,54 @@
+ 2048) {
- return Functions::VALUE(); // Invalid URL length
- }
-
- if (!preg_match('/^http[s]?:\/\//', $url)) {
- return Functions::VALUE(); // Invalid protocol
- }
-
- // Get results from the the webservice
- $client = Settings::getHttpClient();
- $requestFactory = Settings::getRequestFactory();
- $request = $requestFactory->createRequest('GET', $url);
-
- try {
- $response = $client->sendRequest($request);
- } catch (ClientExceptionInterface $e) {
- return Functions::VALUE(); // cURL error
- }
-
- if ($response->getStatusCode() != 200) {
- return Functions::VALUE(); // cURL error
- }
-
- $output = $response->getBody()->getContents();
- if (strlen($output) > 32767) {
- return Functions::VALUE(); // Output not a string or too long
- }
-
- return $output;
+ return Web\Service::webService($url);
}
}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php
new file mode 100644
index 00000000000..05e04bf912c
--- /dev/null
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php
@@ -0,0 +1,75 @@
+ 2048) {
+ return Functions::VALUE(); // Invalid URL length
+ }
+
+ if (!preg_match('/^http[s]?:\/\//', $url)) {
+ return Functions::VALUE(); // Invalid protocol
+ }
+
+ // Get results from the the webservice
+ $client = Settings::getHttpClient();
+ $requestFactory = Settings::getRequestFactory();
+ $request = $requestFactory->createRequest('GET', $url);
+
+ try {
+ $response = $client->sendRequest($request);
+ } catch (ClientExceptionInterface $e) {
+ return Functions::VALUE(); // cURL error
+ }
+
+ if ($response->getStatusCode() != 200) {
+ return Functions::VALUE(); // cURL error
+ }
+
+ $output = $response->getBody()->getContents();
+ if (strlen($output) > 32767) {
+ return Functions::VALUE(); // Output not a string or too long
+ }
+
+ return $output;
+ }
+
+ /**
+ * URLENCODE.
+ *
+ * Returns data from a web service on the Internet or Intranet.
+ *
+ * Excel Function:
+ * urlEncode(text)
+ *
+ * @param mixed $text
+ *
+ * @return string the url encoded output
+ */
+ public static function urlEncode($text)
+ {
+ if (!is_string($text)) {
+ return Functions::VALUE();
+ }
+
+ return str_replace('+', '%20', urlencode($text));
+ }
+}
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt
index e71d18f4541..270715cd094 100644
--- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt
+++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt
@@ -40,6 +40,8 @@ BITOR
BITRSHIFT
BITXOR
CEILING
+CEILING.MATH
+CEILING.PRECISE
CELL
CHAR
CHIDIST
@@ -51,6 +53,7 @@ CODE
COLUMN
COLUMNS
COMBIN
+COMBINA
COMPLEX
CONCAT
CONCATENATE
@@ -247,6 +250,7 @@ MODE
MONTH
MROUND
MULTINOMIAL
+MUNIT
N
NA
NEGBINOMDIST
@@ -276,6 +280,7 @@ PEARSON
PERCENTILE
PERCENTRANK
PERMUT
+PERMUTATIONA
PHONETIC
PI
PMT
diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..518176ab9fb862b37b8d1e7340dfa8341b5abfd3
GIT binary patch
literal 109827
zcmeEtgDJ4kf5YpWs-5}Ck0#ZYYba!``BA|eDhte%DbbP<@dCz&z
z`Qk4)AJ^r8%y92od+oLF0aZl=L;?^p2o(eZQG>MTv_lTyKp=KB5C|WH3U46g?DWpk
z>79wDw~M8_5r>zf!;5@Gc&1zsJn;Je|M5RK1B0ncP6b@gB2IMng`-wmQGA1la1)%_~m^O;9oyo#pAqGqH5M6t?DaiW+3z&mX$Fc8OILhBZqotl+$KJ1p+cqJ5J7DFhyvz{OrodLO9Ze
zYWVq{Q{I6XBTK@mp(eTr{~e`f91=*&@>i!-{u7Ai4QBZ3;S1GYT)UzbVYH##Y>fj3
zZyqqsewb7bNB_?0Ty(*<2Jzj#Hg!IXom)D1p6|J}r=|6}&COsW7FyBPRIyqAZK4gw
zK0#CA^LulS(*e`ujrRiei9$UuUQY}?1fEIwrHe-#Ks9jR3gO8urUY4V20lBYAs&%>
zl<-qZDcTR<7c;)7(=x+c!W=1#JsNh3jO|*I
zQ|OAX3RGTky=y?WbU&$k!pUXGugLCwzW6l)LQtK_(=|rwp3$7@5XXy+9@;vDE^_q~
z?UJ7EYSq)3!p--Jt24T>t1&K)0wb6aE7HlQ-1hm0duJDq1$6tt$P+|^h@7kiX8_($
zPY58@|Hcuv(J#y>0FKxIC;$WC2opC;2X{^m*yI1_hX28Y@Lxi&gs3SFa1n&BAw5Zb
zyUXy1{!qCRgX)W4CbrJecBJrmIl-{$?y7id58WO0Hhpt*Q+UU!jm&}5n0qkQkow$#
z14&4^*h85*ry_EpiXgF%ba&W{+LL6fyt7kR)C?z+v+jB$X6#w6u#GDF_BlCWoXFc(e+cX~zSZ*UsA~%67(d3e)PFM#lCVV=Jf}`X
za?&7Dldws^u$31pK5Er@8>L@9B_2Z+K_I6aHH8ayvlN0VXCXMzV|ZDCUtwX
z)IdK7FN+vuk-h#dLsU`v-5W|!&~w7$8&)Zs#KI`4%_dt@DPh7P8YpC<2<=QhTYwKL
zk5bF?pm_C03PT&Rtu$K+Kdev=vC%DOEGZ+T_154jR^0HaCKsaLAY7!8-Y39p({~*y
zhBVVxTo6ffOYo+z5p+8FG(xiBb?~6ev-~NRSkDwWS_6*66SXFH&&nXtY)GwLeZfs?
zr&t@2MkJk&49_TxwA{+y((2q|=4Ms>lzX(8>$`q|`1vvQt^9
z^|2u|TY98=>A3=HtZ?1dYjYwo83Ub%(My@sBh<>~52In)3
zl27fM^HRpf;$(C4%Az2@X+gF+t^46(ntMLhUyUDHwBn#;1pUsRjCL?m8!TX?6MpF|
z)Ew0l&qny^ZrU;~wquB~p*i1(Gr$uQbB*d;NdH1$fSmw+OvxxWiO0rd#WUA55ZKPU{H|rMC_rrN3M(L06zr?K%w|Ly?D?
zTf-haS%(){fw6H2Xkn0(soje}B92F3iv6GS4G(R0lL~ld5a>!W5Gowdk^hXi|Js-T
zG49|1DGa>!fA&Xb(&(E3u4gh=^7o-zBCdG3FEmTY!=~AjacVP%rDCbh(&Zkr)!?xb
z723;m$C~_hcE`T^oZAXrV#66&(~m=vU)CK>CBS<4Y
zu-?@yRGDw2ByH18?u_~=h8o-sSHR^;$=0RGK|)xD*>tLKqJ>4HGL?ebBjUw<{&q?I
ztd_mz)e|Ad#?#TiO0uwGqrfj}hx
zCVY1rOUrleoPR%Y!gnn4Lt(0s^CA~P#0le(m~=N|lrkz}qV@%X<0P-vk!ZE@
zujEp2=0b6A@xv8`Ft=I3XGpc(ALcr$o}FuG@RzM_woGe=w__C~W^;1b6?oa(GZr63
z1NCFAz9mL$GqZbrF@I-@XJp*z-!Sj`hfd%lGYV?T$DgugKF8iX3$+l1w7JRi?{3~%0Ke!Pd}VQ8iUW#`NGk(zIJ-*4YhL4@QEw4ulRr2WxRhE1Z*U2CTJ2KVS
z3`U#D!}kvsUyzK5U1)rtPYf<#T+^Qlh~g@Zd+u|7Vvw8kruQ{-Sx39fw72(QI%{Y7
zHJm6TG=H5^dx|=>g?H|l{dISU-8fF_nl!v+YKuu1xk2q6#d2KrH@d;R9(@_JQ+y+~
z$>*IV4Q_~|=8c2rTX-nnI&?{#1BVyhWDmyWAE0R0N^{R6(aM=*@$IPi{POi0MX^$5
z-9DtU6Wk_|h=}Az^pBpS(k-smQybj12zs7TdjDyqFPfyPO}(>ooJwHCP!s9IG9=JC
z<)=mSi~^sSY>-_E|HlY#tc*>8B#bbjSg};T&o;R4gA_1{*P`?+^Nkn>!~Rix%)C
zb@wO~J;|1Ms+EY_@ZXH^w6jl(taVukS(jR`N4P}X;(ebQz)C{9Kq@V>N2KorA$JN{
zR^O2uQnou|Q>)A%)1;H!{`3-(7|Q@RO+q7WhqWb*dYY3O`)Lv52E|R6`Npk}-@fik
zO1Ov=-@-(c#n@v9z2~+!bC@H&01rOTSIpL+*S_wyxnY0gHUjkkv9AW*JnlXHXpT9UbcT1Y%Rn2&rTrFP|
zc3cc{2V%QIhvyEy;DOV5c1rIok5Fw>`nxeIzpwK?vM5<-CZ!`iPI%4QKZA?Ho-8aK
zVhcj?cfK!n{A@9v6}lc2p2}ljIJF!Rltal%?<>$P%KV7
z%2l>_OnkDfZHwY-!EQgpOtt8$@->-rISu<5O6{1Xt9X$6TWLVlf8`|=Z_mHGt1@H3
zX-WQ>Od$OP%15v(KEe`+Ua#YA)%2N_Ao4c-pWd^#R*wN0^`>3Y{5WI;a?;E!lGW
z#f9alZU8a={EiVUSp6HaFM$a*k~r&Z_ct7;M+zpMFYG=Q-(AQIK2x`IkLYjy7Mh!=
zc=o%ug+8{3Zk`_(`@?I*En|fcgknkceQKtc)WeGFA#Lc+koj45L-k~?PDKgyy^@qh
zj{5jfBXyGLjgY2f#4iF6L}CY-pabf6i)aV;s6&>YCSFIqac$!BI_PLo
z>kJgZ6Sf8N7gO5EB>t9+mFKhslBsAvlJG`ZF4hH>Z^AJHbXW
z6e3G5;|d`urB-V`7d>gCo5E5>_h&|n#-T&g6V;rRIYj0dHoQATp8O=}MDs3s{@^&J
zkYGqTyl`5|X)NsJ@AMV%gvn=PhONL)ny#oP^n>7x!D4w4?rCq^;p$D<>HCn#NloAV
zs8S*{zUvq2d6!3pe8z^K!C17>yN|rF(;=b#ZM}&qC(|^G_#xt0I@u(c%1o);nMnZo6$_OF2Y0cJ;(1hUT^|Zh>
zV2%%n^mazU6>QD-aALA=i5f9!M#^6
z5Q0yZxN4fYO-WwCeb>X_l1xL`?nNN^(hT>}Z2>;wvEY^ws;K6iySW&iz!TTILV;7~UJ?xQ;Kgv=KVw5ImwTSLk1tn>Jh&0Ffn*@J
zh)&Q?qOR`sPVzZrS6Yb+dZr!GYoRH&YmYav*Ld!IoTC`m(Py}6nS#ISi~V%lHqOW1
zPmXLP9PDL1_wk=mQ7Z!o)CA8j^IxQ9}#e~l$7Zv#;ZFMR%)a~EMI!YsrS0=
zH4)_7yU8YsN;>O
zpQ9UISW?4LCflsTjQg1*v$gV^S^wT5apkwjNPVP=#>7HEmotDe1v4jAePlI{=PL3k27bk=Z&a(qVA6mfBJZ
zKbXg?3n$m3Ex|%45yn5k5cjrCn9`gWjHoTwkf3XnjTGuLn5gUjYwQ5aAv~3~s_5Lu
zLUAL`3ZC6O`K%E#WR;@po*mv?+xo0)3+Z)IEM{cTX(ymhwcajbCD5}ho@XKSE+%1t+qxJ%kExG&
zsji`T2-y0+NVuLolk1pa5Gca}>Hj6+c>cSDt4uL+{`@Zq_hJo&iv(WHCZ(u~-~DHq=At1U$N?nIcMWYRLjMu<5|CFJ2+WN61@doHrRHap$M&6pl_iKAXO<&3R{
zAMN(+#tDzQBC&DArjIo>m)3p)INYLo
z&9l-e8SPJFq7Ez^yX8UnuS_L14qmKp^(~3PBpey%r(%)4PDlY)x?qUNR(TV0vZo%Q
z>qJ8*+_niShHpAcTKdr$n}nGEbJ>9Pq+#CfD2Blf4@L}3sttBMjwmYc)s7{TUAye9
z311dg#afRLBCpG5Ues>Y+wCnq+YW1)gQ@i+(dTl|bp(e^_<~}>2J9R=!C6EJ*&z-@
z5y%|XftKVlH7CBn{`rinWQG{=S*$9;M>qvC@=_G?_Ud1T>@U
zNJ@b}FnOYXhxg>D*Aazh#_vqbA+FT&Ofuz!>P|#w&IxWy@7k_@N@s_cKc#(&_)AwU
zPjhjHr0D5`Z}DW9LDW3;^Q8TAz4x7F%ILF^Jd65LBN(Q=C0g72=m>!mp_EtN3@y);
zk8DZ8jC0Z7Yo-SK)acTlXdambwA<$vueC2I*r|km%0RY|JC`IEqV7i*z=O9gr&jI4
z{)NAEZiFX0B|(5nIvmQEfl{y`^L`!WS`YHuTh;JK=+Dz->fvgu->y^GC>D{ln|x>M
zRPv*tKfhrdeYhr`;W#$sdQ5BRJ&jzrBu9)8^jB_gJuyX9f)f!Q44Cua6n*BfE7)O(a+Z>G57VJCFQkt=jUR)5Of~pV
zphMC`tjATXmgVzc6vgY{qc_7C4KLsHh7!t~UJ(!XYzf}x-(&sfl2e&=6n7vCaWpB~
zKuO$8q
zuX-MHOagA^Zc73mE(Wfi?uVWpro^6Zjc!(Q0v^V59v>?n4>-5fpU&q_%1<6I&I2EA
zYXa{b&*K9hk4gf&UOs8?i#^<@JzcLsIeQ-ON7tY&zt%EN0Y@UvmaY#xe|QE?
zt_9ARJl#d^--$h4;jBH~?5O-2!jZ%Zq)&yqjaAZ2akM*OSHaosyJ2I2QjW~A9ciZ7
z^kZx>RE=iNQ{Ut6XM`I$QRS6ins|arnb^{XP*bu>))7@_8qa&jU34-tWZ+ug|c8+o`Aqhnb_(r8LdoomX@QUAdzN4Q@@?eY3gWEoRMKUKe0SfOD@
zi+xbW;_)|->fXu@gQR-3krT{P*NTk%NYhetB*FBfAtjf}-I@Q6*m(G!q
ztaV^Wh|+i$5L^&VrRbL*Zgk>q8eM1Q{&H>8J6Z0Y5PlNuKl!~~W8QpNL-Uirse%^)
zs8!aM1Ad;>#spDrdwsVZiddDi(#c&(&(kxsuIkE97Gcv^1`H}7tw%dSIh%#~b5Gp!
zU0;U}7#q#u(APRax0BUwq0`f%pf-eQk9aCke5(FLO1
zMU%%4)ER$!ii^j+6EW$0miyTE9}~iCRZh@yM>fNkfOfi?EXk?8=t#wC?cku$gS9
z1AdDpMj~*jmyOSr6^)&{(3GD6Zv#$G?kJ_<`Y_E<{uV%Qhsqr&XqxEv1Quw{a@+~4
zt-OW0RnPvUm+#pe#ZfW4O}#>~lTq68pA?e85-$^OYIF#K=*KPsHq5uMnaXCI_W!X_
z(e$n`wS6)BNQ1?|IyY4`vHjzORF$Z5ey%~_V6`-~2y4qJd1Nsqkg@!8rR?~O&@H0X
zKzC<|egHT5Tg@K^RC8{DEyFP}H^=88?6ro-B?9V38|OD3;AF8B1oiI528ofO0fWNj
zz!hA=Iomq+z^Ug6XC-H-noCz3dLheKioVar=4bnd#^rCA?qZn*rV`xnAHF94pgERT
zCYs!-r%Lk-ypn=i#$LzV-C5lHNwf7VFkXlur6~lPku1I~h2tnqlPR_CtzC8~ShqJ(
zUx6!13&%>j6dz`8Uh{fUemZoGnp{kLEc;CBCgA``VwwPl7x9W)!Ms7>j8WA7sOsXg
zllZU6dChR8PLQd#-XY6hYwV}-P22nS-UIRUpOEr!N-RB3+GZy-^3&YRQ>2U@ANw*;z{rP=Z(aNkAc=T!&r8?M-{GZ5^e^GwX
z%r9Q1Iyu=%Uw&Eii$WEz^=?AE45dZs`3IJJqyR((DFV-esPg+CZpe0fF}%bIgOk-)
zvUM$wF2+&&tS!aNB~4;%SJn#|Kv)ld!;;Ja!ip_YHrLMC`aTuherwfnb3nicnz=?2
zKxFR}UICEkc8<-ow{&!B&V@53zm^E2)gC>+B~BkGL3TawiQ#FoMa_9Hq-os8Qyjc{Y@%-1Wb!8=`Vlp`e0;g*d*K0q
z1r+Xop-k!kC=qep8dFhIyvOl4gfH!i%FIN&ulxRf)Ns|wgl6;74;XTg_Nu2PRgIW%
zXMDQyOYakaN|$~pVv1g@G-8d0ib-nVwZulKd~(Qw)4v66Lu~hA&sJ}1Zxu98!o>te
zY>s|s-Zcn|5Cdl$Y2zQbtqNVsl9l$f3Z8!v$Z+sIzgdXa3Txb%Z4n0*DYq0idqk5Z
zoZ)^)X6B?_quD}HIQ(Ger)C%L7*6{-;cRa!v3~K&tQDv2B&~Zf&hbX`ASAg*a4r4J
zKTiG&J$bY!_JCF
z78xw*l)iFMxt`ZmGky7c?~|ZO!{LMVph?NJhpz}~;XMv<$x~xr-qV!3VQc}D!K45f
zubtE@VFkGcCY|kgGk0+<??}ocNt9e>Kgt
zJF8OwjMwg+qf~|72U5Pd3Ipu5S=LHUUs5Xy(!Cjb3iWAFvK^d%5h2sw42rw-%<9QW
z5X87k35&_Z=+!fg>sT>$N}(L%9Q6eupjqO6@krt{wM)oKv~+jAqc6Ww77$Rd6hu7%
zdgagCZ95NyjU~HS)>F|klbOUs{RgjHfXwKdZbz6^)hlv5B{Yv7Ct-{UzbT@sb@V$P
z$j{m>t!2LZ%gf}EIe^VP34rYE&R}r@+G8sYXqJ<_HZ`7s!~)O!_NZZ=xe8%;z<)O5
zj#w=@*#aGtT@^J1RefZf0=Q+7t#JN{)He%f8eFglAec;4DU=_W@4r%M^qfj-ZeipB
ztg@R_~6O&-I!f~PY_-sG`$0x00w5crb5V<
zZ__Un#Hy}V*kL*PbNoj0%7D>q&WY6DpvomX^(4X9`KLBcxeNCDXtJP5-Cmz_EFep1
zz0b7ngzjPLDc%`Q!6LX86X~w%kW>h$LyM?NnFBEN+Y32qF9ZX4zc-kFTLd2!abBGJ
zu2r}dXhv9^9=|i71ZP}Ta^Ae=%d91|(o0l(rI>CBH{&HH&n%P?7fEX3C)E=j_YyYmx@)6*4^q
zth3$SwJK_nniq1p{kE0=4nx)61$EsEcKV#Apunx!Mr|jcGdJn^H0;kOxt&fLv)(#7
zHxqJ^HThI|Mw5MD@uv>}Mi!hCFRs>|{?E7ovyOmx}oW-&+t=AP^dF5Dd{@F{l4meiU#2_Jg8|L`y(S4bW)ud$M43
z#+?;FebqFy>6@H2WbA6)&;r-ZcF;uozwTSQBP37n2N0<>)kGYf5=X>a(4V=0WbK;9
z4pEb*?Zz(wQrrmuu$oux6xLlnLwE^vn5GH&8zDXpIY1&;G
z@1OT?vXRrN^oPFB$;U3+jZco+v)w-F5L5Z9Nh6Y&
z&2@+=P8wDP4nC$b96I0Jk5*Q5`gNPdYU+-dk6gvQB%~vct>MUow)}4HyiF=Qtsi2&
zt#$!eeDtM8_t*Gj&W}c)&ZbgfZ2ZY*GoZRv%zO%rKQ?-IAHzJRtXX)k#m{AV6*!R0
zdGQkXJd{j)#l00tN4<4^gs<`bX73Ns-4@it>-25+k>HN;2_+4Wx?L#
zb58d!NVEjZIfj@@;d5>mQtd1bqrJ_3@J$vY%CyCYk5r$~@yViBBMYsaM(w53>#a{l
zl0|$4irKENtUYb2Thlu8kPXvo1EQ+qosC0Nc^kOxUX;J<0i!3E$es(c~<0b+1iqbH;U+J%FgD@|UWLRMbYY
zyA{U%$xvB|0|VsuVLWOZ7%Q-LTe|vJyWuqbq&frtiB5NcOlOpS)v-bD%)W~t&u#qW
z|+uH?E>2SD9fRg?R;`U=yi1v?~13vV;eShJx
zSP1FZeiuOD$U5#_-J8#vep0)YxXCE}{yhkYkPj>a5<|eLvr>+L@{}+|NxsiZ)lh0HGfoIxjF>(^;im{rLQPEdxzy`#@8aXf(YKW-IFGHXd0FAFq`_tf{m=y>r4I!3K)9tEsn=0dFKuk@3IzKDZZ|qv6^1+|5+>sD5EI;
zr(RDR93Wb{#-(RO5!QYZ4xkYKoi;xktj33MfhlJ%^PxP15HvkW|2OdiuVXHSZ+?vf
z;J=1k0kio{RhL-D7x$(|+%ZjI*W&G}4vf5WOJmWXng`vs>@l3qhDY4=_oQP1%1h>!I@_I-9eOqCge#yU#90nLxFq#SX
zYtrjoe-M_rcmhqj$;I)=8*!xPUT#-{zc2N+;ZPR#p{M<1*w4~^^DS8auHzdLRqHoW1I3R$2_
zXXjGBCfmbi0#6Y$;mrxtrCWr&qEB}ZY5VwHD#N%b!Db_4x;^PP{Bide
zOJjVGHkwtIX#`>en9IPFPWkibVGxA(NcI^2e|I3DU*Uf{zc5eeF~3J9dY+V)=fD(ZkD=%;?DGIyM~ZB?I}2TeITN-{8a^`D3}9O&~?~1po!|yoT_Ki{31DSv5&>7gA%p77`S0F+W2!lSh@*GRv-HC
zBo40Vj$v`_Lg1P#C$>bOuksP0QA_FI1Xz+|cNEosrX0t870|aaO4ZWgCpv3PDI1|L
zV48%ewN0C$G&Clbya>$b!g9PonHA0)Y*X+}`chwd*7
zLa}=_!#&mHatpO;Wa!GzRv!VKjs!!a_QP93Aba_m@0k{I4;<4CR<}oV>1ke*5zVE4$SV+S(W1iD4>Na5hM?l*2saCaQt6Y7pC~0*(ifnV4rhX`T^|ov*E!XOqAV*rcC0hY*8!%LWD?%5;YZ!ozY>Q{V3$=iIs1#y72t7e>NIZ``v)mrFmsDInic^sGQSEVJz|3DKAa
zY0lgl*8vi}{6y>pOXdo2Th>*6k_N|;c`a2(oOgl9WIJPz9W@z=wnLa?9X%q|1Ke>|
zh}J03-vippa-u~V0&M)ebNd6?BQ|`C8r{$9^?L&{7%~wuVYuv(115;pn!mWF;7bpb
zqp%OG^&uHc{KeXQ8Q5bdsC2~0;tZdLs&e4
zW^9w_^NNr91r}S1G=T3Vw*7mWe_OYs5%5$GO9_8W61Y&T@}XAvOV89~Uh`W{2*Kpt2%0D=S2R<&Co0g=QJs->Kmi&{K3~_3K
z4NpKyT3*B+hzfwY=+Y64ascom7o(^mjr$$g04d1j12;x3<1oiFVUE}PzXJ&8d4&uw
zpPxK{l>@|=bm__=3-Gil8ISM9!9~V3kk(b|zUTPgnfo$>^?YE>v-|S`Nxc?dQb9Zc$b4kxQ-gZpU=<`-dff^-!fo4ZOJl
z`skzPsS&Ksx$gwbP&wFUJ^_#-3sf=y;FMRBDW#`>Hpz>4{H!+KOqZ{t0*q@d7C^t?ea9uSomtV$XNlhf
zC@T+^0WzzHO|_fnd+5ZIx(k%GbjHx+S`Qxi4W53wig
ztekZ2MP7tx`|Gf&ZG6wAPz%p7sa5$KfcDj6Fv~#q8opW+tSg`Yy|+Jp(3q$ihX9;7
z&af6Vx!+^-2Npn)ck%3&BFBudh-Vqh1Z|VPSPO#!-peLDtsAU)P*HJbadfFmMsHkc
z|2XG7AYkOVuGS*T1gN9#&w%%g);`Q2wHUDFvSAG;srIN%bh0s3JG{IP6yFHAs!SFw<)M1%
zz+>hJ2Ql_fB2){18vNJ!Vw^D<8L5r|`-FHVzPi>n6)^YlyL`AoGAlkJe*o}oZIT08
zoJw4J+FYGV0MK8>GdyJKMLMfhAmA_+Q$YAmzNeqFr>SJ;rSEEYl+oO-zg03bhBSiXA&tlTDWh21k(wLrr&kshqJe66L^scDXA
zo^ERo1^k`?{052&t~TzKR93DK6cg6@=67V4l~=V21I@+0;*>Li$3f8-o&5eC8)rc4
zUy62!jZPWXa=y|Z-L*;S?rj4~3at+!;Csu_3J2sr+CKjxida}AskDEM>JIkiK%ow&
z`7o~sP3dLnm^c*MNY;jCj%zMoS;sgWwk-K>4Ph}2&-i@4OG5lUa|YNw`@Y5Rx+J
zl1jtP*ya1)^lkP`VUzWt`BY{RR!?VJdOL3K@7p2%!?XIbpUW<;j#&_@UQQ$g^Mk-u
z#$L+FomwY`zfbJZsZI}Hno)ot`d!8^rlzBE7Yl?C{wbsGfHdo4K&+ASBrM|^Z($kt
zY|5rHEuML#*Eq=HFX$Dn(?Zs_&ZS0h1VPw0ir9fS3SAF@Yxs99<6#~kd3_I*)Qk)w
z_kfE^=^4t|SNOA*VGQQbC?WuS^k)G0DC)^o@hX<|Rr0fAp!7Nw@xB!bb6@`LX{GUk
zWb&=jfiAG-ns)y^)7(Rm^*{aW$ORP8M7cF^FQaV+@qY|2J;ef!oGcgCSHa@~20?^2
zPPc+()|cp9TfQS->$)*sJ{aWiD$<&t&9w401HITE3cT-v0DS9Cuk3BshjIYNs$H-R
z2*2s}c}%``U1SB{zPQZC)HCwNB2N3Z94Or<_onW**3k8G&G!h}puH=fF7e{Bze&*V
zY78&^8e0dj-#E?Hr&<7QRmWCp8dG@Vh?)b#ET_G`BaL7^j$}mm2>t{1L
z&5cotJEc>UAc78Iy-&q&5N9Z7NK01<0qGj0JAfFI$;P?e2}nOGst!2%y7nX)fTe%&
z>l`R*YsW{77>ZP2Hjar)AuodH>e}OFG=Il53+MNjnElJoV++7f>dmpCe;paA`+(02
zo7_^ZfSxya14C6y9e^s>T&biqtQ7iQi#H)OHTf5+6~|_{GhU2cK%JV}{?=)-+*cQH
z64m?Pfn%Msp4E0Po-2s4XCei_$R6ha48N`KKRmEM3`RSzKx`aq{7W^H<>b23d`@!5
zA6S-+=V45vNho?PYz6aBEh~5*rgt-do672MJ!cBztPhPYMAuk=5_za#L`%1a(%+S%
z+?8WDunG}Pw0x<`V{WVO4o_fOaieMoIcTh0?h9$o_`329D1Hsy$;&7~P~rSaUw(1%
z=Uq`91g7(UiVrNnu#T<$*8kT;vkN6)VsD0>EuRQ~$B|*a-fMX9w)e-Ea({@7=F;^I
z)xmTAbw1$Fc~+hGBDfs&TwY?-eT^NaFb}UOIMKKfd`7?5o|kf$$JkvQ`JSQDGsYJm7I%(@1GA>A4SG<;X$l*8(|8VQTCIjw?YA!Sp?5aJ}=|X(Y
zjW#dBIY`zLMrUeM<)R`9|K=VZjifWLUoLh?(_%l+mNrd};X$_2GexoI<-ur=C+mac
zW>wk}xaV!a&6wbxfVqdD3~G!?2ciUsDgFE&hY19}V%tcLaC4WSVU``wa`2Lux*o
z0+HZ?yMahJ!Z$x(BSD-Cr=Qw!BVv_3V*~aAhtba`3fDeBtKIfIts?kDjRq#5^tR_t
zxEOLD6?(WC_U0aHt@ZdGx;H=Ao>0H*5w)dNpfwfCeh~@oi?UbfO@sukkVsX!{+5T2
zm__&Gpj(%=!E-63GZh$8k1cyDiZ!PEbU|LXs>SgzhFSl$#F@nVK!X(8bBZufS1{>3
zSZH$fEqkl);tryyO=OyMgM!a{ADhhb%nw1(Jhtb3;o2Zy(V9HC=QxFWwl$6JLDD|_
zGwGx=4euUtWl6tWBAJ-D#gyZOiI8cp9gZ@5obCxvT99?|r@*2&2&is9IvF8Ba2VE!
z3ca{VK`R2jsap7_ieEqRPkqInA1_V|qAkJ}L>CMN_u^`hMu@bib2%cO6Lzti$C4|l
zb2VzWexXKUJflXEQxK{BY%@Rq&fxt`W<}E83OKm=5vtskpNRy
zFi#Ub!hkexd*<2oFoghG&t^#ql9%P92Qyeu9a2kWmoPwQW>a<_88cjVDFyOgV-d0L_Vt-7j&*;#rdYhQrR|E@K
z;4R$LLwU{vr&&|v`{I~Oj`LArsLRH^V5m+uQ=XHb+WUZ3-8
z#dCNe)(S>PKB@d;fj0X}37vs)Y|g0&=oNJ?L5EmjO?56(*W(y@SWET29lQ}&N00bg
z%*SRzp!6m)k^u?$yB49e5bHOYId*hNN%%XS>gdQZ?3t-7u>T~%F_YAOd0Y04;J)_P
zNKdkf+=AHCT+P$%&>H{K_0C$rq@wKj;8XGs6|tu+u}9B`(HgO*GtY;r>z3^fs_ww?
z{hqX~$YFK=s~xKDmP;z_;kiq~^@^iFwvh2D$D|=DkJE2=>y`?`0z4=(<2r-wsvQ+O
ztf6~1G~ZXiA|_!nCFFIrRR{(cDB62)1u?hTE+S@O;VOq_J_B|OQQRgcGx*2?tj}jF
zpm1~dfiHZ>Zwq@!Oe(R4un#MJ_i8~lZIncpJmFpH%(@CCpmWEhVS
zAB*01K3)kfgFeLVbZmAsW(K#ZVbm96&P8;XvS7h3T|8vA=PT@J2+is5W*IjA8mqjy
zlOTk+gg`>@lMyp>3)c|oZ$i>0TdCM?;9-H?FjNEon97Jt_au+fut~VG2=Jyqw-!&c
z+U2jRwqSB=k&*bU;F{`l*D)&WD-Y8B3HZ!&6gP1Gbia3E!F+b7`!I|OZv?l*sW1BV
zK7RRngJ{wzpWRkK0^EV7u!uxe^d~Q)5#WREf=1xj#?QHhM)`iSRoTQ`T93#_fc2y3
z+oQXJY+{GxvG{PcaSVP1!3Et#z|s-Sl4xgY@_VzYHJm!t9`dqgD5LhTAExS@rYYpp
z5Y7d5?h~TbYh8tH3@?Lp8~{$lfjYBtX_csRS?ELw~&z1QqQ~2(k`{$@%jF
zPHHzRhkzC?G!bu1C^cx<`Hz+g4AX;npA}nuNH|E*dVX8AUc17vQX~>#rOi?;d%zgM
z?^ipuQcgFj$LO=q&xysT2n|@51DEl&eMGUPhh5QRoTr^k|B4}>3}O3OpTa--L0o94AYA$!-FZ3W=X0@iN~tlC3}{JP<{~|GuNw^g
z*6Qgn^E^eHQ^J0_)tN8>&-{wvhH&X|;2V6Wd(GjzgVIO10G~<>3vUtd8beTzPXb42zy)R&(yEbPqyE5z+iLWp{k9B^L9A-AE4us|d
z!}t_3Q%gC$*&^^OUVp?s@HwF>_BXU3Re97cK?mF^j$PgaA|a`kOx9n@uUaxMY6nh<#c+2O-3JLq*!k%L%#-
zub-=tH{FT5$g}pPf)c;OBu^+8eEVtr|*)S;fv!0cF_udFy5N0?s(0BLl)E
zXj3KDz90TF9cbAt>+u->bXib1QFugPfk4FUN`CW9!)fuFRA5zEeSaLKUt6Tv$V1$lJPQ&XWkaZ+$TRfld3lL6qPHZ-<}
zki_PTtdvPk)7Y96g`OxXmWL2~!sh{d#9T+j*dkmvYLDfKLHK##UNi-GBEG#KDG5ZG
z)thT=_&DOEpekVA*gQ|-NZ{92QyX~CD0_|SLz
z+S167t_)oxuCUb`WK?F7%tn{@4S*KAo{po#oHwtx6H9Y>Sd=aGT=r|zrTbrOy?H#8
zZTL1m_Q7BfVU%6=H6%ibVo-KM3^NL2iLxcxnv^8V*rmw6k8Ih;I<}t3F8h+Qi-`2Q
zrsw&7-tYTS|kTRH0G(-%n*578VW;NdUDYFU8qB?L3_tx)r**y`1
z46m&v{vQ{ELRL9wBdB=d^W#7nQ-Eu!wUtlx&Uf2q`O{`SZwFZo>QR1xlHTBkT=^*W=zzH98EUHUo6E
zCUW2)@sYMvP{K8E#$#SBC4#ToR-foVU#AE{i}Z>t-vc7I0h_MPz}0i)VrUJ*e)S*Hwq)0!W?n}vze0C41xq6v?w{t?gc&Y^|J{+#(a
z;8%)G6J%@?Fx8(5oPmC^XYv(v@6}$3T
z>JguHo__&xz4+uXYt~@o9bmB<%yD5lkIcQ6mUzBJ%+dL35b)O5MZMq9k?@}ZM8grk)G-|!^1
z9>l`-GFcd_pA=Rz`%ynYOD*qFH6k+N3
z0r-oXf?yrvR^Z@mP8l_xVFNd!@Yp33~_*9&1-vXKsy4HslE
zlWE^cOnFZRvPZ(g^iioli1(C1)V+O1jK1v>!?i0&&o3eBiw3
zVPVCyptjo>*PLBPCQ=es^=F2#Jz**63jbl8w959$%8vW(%BAV;I_c9*wG|SfgZzRC
zy6?&FC^QQFDOlk{pDb6JC)V=~vQ=ED0g*_~fpXi2!?9#hb1bXBhv)unJ8GTpVE
z^t1CUgRgue>UxHyn=O4&+r58O+KH#bHX@}(mz%{8lx#``!o`Cy^!XO{6q(J6qZo$x
zuut#g`$2lwdl-PRY3Ip&w+Us;3)F9U^|JhdE<>0CxDA83DM~)J7#(-`HiCkw3_U19;<;NTFg^-ITeW1G>ixh%z-XG5vQL;
zv%l-UdS^_pIYdcAIU$%`)9e?G#M-a8-~#y@X+Ua`rm>mfrvP>x_LdA9D}ewsNL&~C
z=EZ*e>6)lp>V7F@=_^6Z&{E_=%R%p}{PSRXvkS)ai<3G(=kVwAZc~+R_wQ()q918v
ztM?TW5gFC}Uu3P#Xj*-rVyYVRtUg{jx^{T#6u`hXbzy+FVeT*L2iT&l_D7qD2*;FFz_mOG{!;SUysP
z6=C~5FF|$p_%A#baxB09JI==j%1P(mEvzhWg%Fr(#3PyLD5pMuz+LuLoVWLYodSgIDcNIk<;93!IbV;n1mwj=MHH?o>oUwL3(b
zh`%Gjb*nC4fN{bTOYOY_8Hw|6m{wE35Iy3S{89vr(-9r9>$p~LEv2tGr2Oz+$`$Il
zm#_#OWoLPAGL#mx=wIxqOG$Xe{Ii5x}5gbKwPh?%r10wP~;pGE+u+v9LpW1V_{Su<`655
z7=3(WE2uB7zPBx|_t~eS_x!OftHuTyo}&YE~CH#_r9Q42rD}*c>F?X
za3OYl>?Cwc{)UG#d!fH~7Ui5t>PTE@x$5~T8c<|h5mWY@D
z@58sg*v>v&Kr4e$v}DO)OKUF@;exgyj~sGCxabk+P>$#
zhDG!z_VzIvTVd;tm#OA5q?52=<>THd1+=KDCH-4t=%rI<{~f_o`T8F>59M=(W8PVU
z3`rBO6JZhGl@8Cp;G!iu_br|6G7#=d?o}0pMq#gWlN|_}CWL2~ge%{Xxnlqxt&j6<
zEvw>xM^Q>ry$e@%lfm#~M{JibJtGQA=52+P2lya%HQ^e|{r%3m9arPTq&z0lO`Ws6
zuF(>g`!Ra5&@`2#hic*AI@N2`s8}X(s=3Zx2+F6_+mQxbv1A37XIWvFk~rVLbd3+X
z*-&RN8%lOkU}vQSpK)LA2=M&pDNx|~|Ivgu9rDvRR|DNCnp4wzDPSn>hVN~{r2GU@#iNqFJU1%$~`sd
z{w||aX}sY5(j-R+LWQ9vx93YJg%x^@)nbsy@Ew}xxWi$
z(hJUMt!l~;0Vd;WMLFj*FeP=?wCSssRl|D`zYpLs^rw6VD;jM*(tG|&!+ST6G8Up>
zn!-g8?{KgY{we>n{8KmOqu^ccq_+&M(VdXm!Vv>&h+H94OGi?ro1H=}W%&gc%#atv
z;|FhWP6fuah3$A+>J^oM6*d*dwk4@M5r?oNmQBcRdq1&vaqlDCKHv~qPwf(JDFXq=
zaBH&GcuUEALle?0=oC}W7FM+Oeo>2NR(JI8NLGJG%IO>|eL$+Ch4ji=SHtKWV(~(s
z_{VC;K9{Z0H}O_3ZS0gU?Ql0=eCB%F`|-z7i8RH=V_l3B=YEsY`2GGNf$0Kl&)AP?
zw8*!|H_gXkYuPx_{GOQ6H9~o
zeLXCQmQMSIha8}K9=ycYM_!Bn1P>{4eXVH2+;WB+5{2#ia2ryp0FlW}C*{5{gh-Vz
zsaoS^fn_@=F6|V7ZS&ApOfS1jG)rBrF|_ow-LB8(ce^Tq+O-KMJ5Y9D3F}Yj_56oV
zLh{Zp;Pl>LEG;lr6`V6h)P)kIxHL$j_zxEBfD4HyNgh9I1y3l2fhdUZCcudQ+Cx-5
zTX={2q;qUAUiZ2iKktSRG)g=v^C5o&_!R|g5nzz73`FOir5|j-iw1rGWRXtW`9DJ;
zv6ro|?^QE+;TzprR%oZ&0MB&mm)XvNIGyl`9-z`<5chx4`m7(#oP-d=s7plmz^X==
z7runM2*83UL(-5#@%JP$iYQOK;(H6ihIcL>Fx&BjGHRmAAE$@uo$L8WkB@tsPe%V7
zeSG|4lW&T*6d)e9?7{t3dMa@$q1=+(0&_L@3=iD%TSkMz
z(kJE5sMw3-o!*ip_`J62h~U5SusF
zl;sD2366vCw(?#}fDD*hT5^SSt=3vP)YXW-K6>gXR+DPpC%{v}XOVc}g(
zK~!@S*-hlfswC$^pi$fki>ILATT4H|0e0M>`kH@$m1M@VExWTL`l9u>Gp;IqbP;dd
zAWi3E`$)Yu@rI-64GpH_37YE<@k}Ydj~oc{nO)b+D~4|iO2sfm2w6R@9?_~z;L>w*
zGg4`qMobzAKW{A2^j~&qvdJ94)W-m~eCl*!HcsHa9qN@T`B?lMK^@b!sTzCtPa^t7
zI^*Mu$v)PvP4M+nu1&*#ED#ct%?3U5~vQ#PJ6f#_D_q?hXqG&Ce$L%26D^1{b
z5L}RI{+;X)V(oS)fr@zGF&D2ftyJUg95FnytYkMKD^o<wtwmYAzNQMN#p!f*CO(;yP?J`z6yl9qOIMb^u+{p%YUHM3TfG(lU0g=K-q7O$aj01e
zs!~ednkBK)s^yg}zs%0Waja}MYh&&<@U8XqFzt-D|4X~?akgP)wIUcNu`6ELEB_od
zNX%YZZjhQ6L=V9HI|8h|CtwYNX+yI#w_~GI+#+soo0Y1jMBE0p-*%;}8^@bFEBP@y
zorbDO(8+UzEu_PIJdpXZk>FL|f#=-NQi6gT&cO^37RBfZqPHMjSci=G(Ur@9)L-ra
z@eX53$qJic`||twH=VfclzSnl3S?qXW?Omp@(&A?6*|X&9ju(*)rw}Ya#C!#oGO7!
zJY0z@#shnysMv~OHok}iF
ze+L6A*)_xv<{7X-MU9lvSa}7P@+HNU`9L|x(>EkQ)abjfN*!hA-S2|~94&3i{w`wtj0;*R_-jLf+?du!`G0QRDN
za!wrNBZ-ECZZTVXW5mRI^{J#6s4}B0M--BzoBSd>YrlC7;a+0J>uK0|HEYdA^7ImQ
z{@2Ne?(W^zE*tms4im`SLmq%4Q;Nub*Ek
z%6LRw_&B3yEGyu#F}ylQIz^2Z6>xURR)(v(N?P^%AG89O%mdy*9$3;v=GYSMVwAs)
zqWW_9L9Si%;^=wUmIN|nD|;I93QiN)ZB+}4t*<>ZRSdq8g3uyVSeZc
zz{^(oh+cR@x3dS#ZNMHoh#)$r|7eT0u-@I|9WNO!hEyY~M
z<)rZ&|Ji4$LN1#;BP9wDQ2b>8tD+^`(3!qhKr0wcSfzz?n!1yyDF8GX{iFjhm7Bss
zbSx}M+sS#}<0unffyzncPADl-%V9a~K4_9(
zFE)`+rS2sF%MyQli%U65#;04ZnA0wTkNm!wC7C%*Cn+0$0bHxg#>JT_nXLrdl=={4
z$@Vnods2E%F37+%P`H)l$cCbjXuA976;`0_u?u87qDl_%5Y8
zl9H8$qV_)$bX=Fd8}6Ac|J6M|q~6pEoXl9S@Eq@wWM7>O0Vo2Tsg
zmkVERZC7Wy-(i^MBh9|%i*Au5$VYL!v7CDUAoBY41CH^8P5ZAL<2Q?@bvms>V>LtU
z@70cFRv8GiKajLJPeo38?mwNCu-VlW;2PGX*H}@D+mf_v7EXm@9TxK#sCzwAko^Ky
zJT}VWhnI-oKm$HK1a6B9bsbcWVq)tI5
z?5oDznH^otR9E!F5n-zjolk8mk_;`F@BeW;EghxRG;NLu>wRtKU9lZ^H~8nuE1yih
z#>}0SsE?B5?6<_ZwVjbGqQkJ>Igm_o@*dEW^WWBfoA^+#3Xl;l!#mOx$6xK!{hMYh
zB~|{uAxbspD2sG^H}t#_nD{1pM9m2A!!sLChgH^piHuXD9x@-kr6fA{V@zlxhEpFN
zCjd#p%|nS!^sE9js>6)Akcv#P`cr!Y5g;?)I28{XK+<+h6U4AG*3vsJB!{u$x^(MS
zua&IKhaaQmY=PhEqWs>a^Ki6($rk}FT
z{GeeQpC>X^{5U6^ZZvP&rtZ&zrhTA~(d5a6ltQVCjv<$F`f4=4V7i9}xE;=LQ}vD~^jEfouGj-!K``e4Yixg#53{SN
zx^vT_C#g4U*M?Z0BAe3sbnf(PGO+pZIUI;fHtn709d#M7L>9fs<9#g)_d{#hVzhYU
z&G<_dZ1D%LC>>)BQ);OlW0_X*RQK@rbRTU|w+lL5TcoT|f92jYn{+f7=+Woz>gF~r
z2KW4u
zbWm2HFUZWoI<43s(_w5X=|>L=yjJiwIxAFPm4Ou2Xl#+<+&28^{x5SlYZlt?z;;yn(!P=bgFt`XLcFS19<$^7$1*