Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation;
   4  
   5  use Complex\Complex;
   6  use Complex\Exception as ComplexException;
   7  
   8  class Engineering
   9  {
  10      /**
  11       * EULER.
  12       */
  13      const EULER = 2.71828182845904523536;
  14  
  15      /**
  16       * Details of the Units of measure that can be used in CONVERTUOM().
  17       *
  18       * @var mixed[]
  19       */
  20      private static $conversionUnits = [
  21          'g' => ['Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => true],
  22          'sg' => ['Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => false],
  23          'lbm' => ['Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
  24          'u' => ['Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
  25          'ozm' => ['Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
  26          'm' => ['Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => true],
  27          'mi' => ['Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
  28          'Nmi' => ['Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
  29          'in' => ['Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => false],
  30          'ft' => ['Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => false],
  31          'yd' => ['Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => false],
  32          'ang' => ['Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
  33          'Pica' => ['Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
  34          'yr' => ['Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => false],
  35          'day' => ['Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => false],
  36          'hr' => ['Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => false],
  37          'mn' => ['Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => false],
  38          'sec' => ['Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => true],
  39          'Pa' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
  40          'p' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
  41          'atm' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
  42          'at' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
  43          'mmHg' => ['Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
  44          'N' => ['Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => true],
  45          'dyn' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
  46          'dy' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
  47          'lbf' => ['Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
  48          'J' => ['Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => true],
  49          'e' => ['Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => true],
  50          'c' => ['Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
  51          'cal' => ['Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
  52          'eV' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
  53          'ev' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
  54          'HPh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
  55          'hh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
  56          'Wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
  57          'wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
  58          'flb' => ['Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
  59          'BTU' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false],
  60          'btu' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false],
  61          'HP' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
  62          'h' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
  63          'W' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true],
  64          'w' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true],
  65          'T' => ['Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
  66          'ga' => ['Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
  67          'C' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false],
  68          'cel' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false],
  69          'F' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false],
  70          'fah' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false],
  71          'K' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
  72          'kel' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
  73          'tsp' => ['Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
  74          'tbs' => ['Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
  75          'oz' => ['Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
  76          'cup' => ['Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => false],
  77          'pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
  78          'us_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
  79          'uk_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
  80          'qt' => ['Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => false],
  81          'gal' => ['Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
  82          'l' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true],
  83          'lt' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true],
  84      ];
  85  
  86      /**
  87       * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
  88       *
  89       * @var mixed[]
  90       */
  91      private static $conversionMultipliers = [
  92          'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
  93          'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
  94          'E' => ['multiplier' => 1E18, 'name' => 'exa'],
  95          'P' => ['multiplier' => 1E15, 'name' => 'peta'],
  96          'T' => ['multiplier' => 1E12, 'name' => 'tera'],
  97          'G' => ['multiplier' => 1E9, 'name' => 'giga'],
  98          'M' => ['multiplier' => 1E6, 'name' => 'mega'],
  99          'k' => ['multiplier' => 1E3, 'name' => 'kilo'],
 100          'h' => ['multiplier' => 1E2, 'name' => 'hecto'],
 101          'e' => ['multiplier' => 1E1, 'name' => 'deka'],
 102          'd' => ['multiplier' => 1E-1, 'name' => 'deci'],
 103          'c' => ['multiplier' => 1E-2, 'name' => 'centi'],
 104          'm' => ['multiplier' => 1E-3, 'name' => 'milli'],
 105          'u' => ['multiplier' => 1E-6, 'name' => 'micro'],
 106          'n' => ['multiplier' => 1E-9, 'name' => 'nano'],
 107          'p' => ['multiplier' => 1E-12, 'name' => 'pico'],
 108          'f' => ['multiplier' => 1E-15, 'name' => 'femto'],
 109          'a' => ['multiplier' => 1E-18, 'name' => 'atto'],
 110          'z' => ['multiplier' => 1E-21, 'name' => 'zepto'],
 111          'y' => ['multiplier' => 1E-24, 'name' => 'yocto'],
 112      ];
 113  
 114      /**
 115       * Details of the Units of measure conversion factors, organised by group.
 116       *
 117       * @var mixed[]
 118       */
 119      private static $unitConversions = [
 120          'Mass' => [
 121              'g' => [
 122                  'g' => 1.0,
 123                  'sg' => 6.85220500053478E-05,
 124                  'lbm' => 2.20462291469134E-03,
 125                  'u' => 6.02217000000000E+23,
 126                  'ozm' => 3.52739718003627E-02,
 127              ],
 128              'sg' => [
 129                  'g' => 1.45938424189287E+04,
 130                  'sg' => 1.0,
 131                  'lbm' => 3.21739194101647E+01,
 132                  'u' => 8.78866000000000E+27,
 133                  'ozm' => 5.14782785944229E+02,
 134              ],
 135              'lbm' => [
 136                  'g' => 4.5359230974881148E+02,
 137                  'sg' => 3.10810749306493E-02,
 138                  'lbm' => 1.0,
 139                  'u' => 2.73161000000000E+26,
 140                  'ozm' => 1.60000023429410E+01,
 141              ],
 142              'u' => [
 143                  'g' => 1.66053100460465E-24,
 144                  'sg' => 1.13782988532950E-28,
 145                  'lbm' => 3.66084470330684E-27,
 146                  'u' => 1.0,
 147                  'ozm' => 5.85735238300524E-26,
 148              ],
 149              'ozm' => [
 150                  'g' => 2.83495152079732E+01,
 151                  'sg' => 1.94256689870811E-03,
 152                  'lbm' => 6.24999908478882E-02,
 153                  'u' => 1.70725600000000E+25,
 154                  'ozm' => 1.0,
 155              ],
 156          ],
 157          'Distance' => [
 158              'm' => [
 159                  'm' => 1.0,
 160                  'mi' => 6.21371192237334E-04,
 161                  'Nmi' => 5.39956803455724E-04,
 162                  'in' => 3.93700787401575E+01,
 163                  'ft' => 3.28083989501312E+00,
 164                  'yd' => 1.09361329797891E+00,
 165                  'ang' => 1.00000000000000E+10,
 166                  'Pica' => 2.83464566929116E+03,
 167              ],
 168              'mi' => [
 169                  'm' => 1.60934400000000E+03,
 170                  'mi' => 1.0,
 171                  'Nmi' => 8.68976241900648E-01,
 172                  'in' => 6.33600000000000E+04,
 173                  'ft' => 5.28000000000000E+03,
 174                  'yd' => 1.76000000000000E+03,
 175                  'ang' => 1.60934400000000E+13,
 176                  'Pica' => 4.56191999999971E+06,
 177              ],
 178              'Nmi' => [
 179                  'm' => 1.85200000000000E+03,
 180                  'mi' => 1.15077944802354E+00,
 181                  'Nmi' => 1.0,
 182                  'in' => 7.29133858267717E+04,
 183                  'ft' => 6.07611548556430E+03,
 184                  'yd' => 2.02537182785694E+03,
 185                  'ang' => 1.85200000000000E+13,
 186                  'Pica' => 5.24976377952723E+06,
 187              ],
 188              'in' => [
 189                  'm' => 2.54000000000000E-02,
 190                  'mi' => 1.57828282828283E-05,
 191                  'Nmi' => 1.37149028077754E-05,
 192                  'in' => 1.0,
 193                  'ft' => 8.33333333333333E-02,
 194                  'yd' => 2.77777777686643E-02,
 195                  'ang' => 2.54000000000000E+08,
 196                  'Pica' => 7.19999999999955E+01,
 197              ],
 198              'ft' => [
 199                  'm' => 3.04800000000000E-01,
 200                  'mi' => 1.89393939393939E-04,
 201                  'Nmi' => 1.64578833693305E-04,
 202                  'in' => 1.20000000000000E+01,
 203                  'ft' => 1.0,
 204                  'yd' => 3.33333333223972E-01,
 205                  'ang' => 3.04800000000000E+09,
 206                  'Pica' => 8.63999999999946E+02,
 207              ],
 208              'yd' => [
 209                  'm' => 9.14400000300000E-01,
 210                  'mi' => 5.68181818368230E-04,
 211                  'Nmi' => 4.93736501241901E-04,
 212                  'in' => 3.60000000118110E+01,
 213                  'ft' => 3.00000000000000E+00,
 214                  'yd' => 1.0,
 215                  'ang' => 9.14400000300000E+09,
 216                  'Pica' => 2.59200000085023E+03,
 217              ],
 218              'ang' => [
 219                  'm' => 1.00000000000000E-10,
 220                  'mi' => 6.21371192237334E-14,
 221                  'Nmi' => 5.39956803455724E-14,
 222                  'in' => 3.93700787401575E-09,
 223                  'ft' => 3.28083989501312E-10,
 224                  'yd' => 1.09361329797891E-10,
 225                  'ang' => 1.0,
 226                  'Pica' => 2.83464566929116E-07,
 227              ],
 228              'Pica' => [
 229                  'm' => 3.52777777777800E-04,
 230                  'mi' => 2.19205948372629E-07,
 231                  'Nmi' => 1.90484761219114E-07,
 232                  'in' => 1.38888888888898E-02,
 233                  'ft' => 1.15740740740748E-03,
 234                  'yd' => 3.85802469009251E-04,
 235                  'ang' => 3.52777777777800E+06,
 236                  'Pica' => 1.0,
 237              ],
 238          ],
 239          'Time' => [
 240              'yr' => [
 241                  'yr' => 1.0,
 242                  'day' => 365.25,
 243                  'hr' => 8766.0,
 244                  'mn' => 525960.0,
 245                  'sec' => 31557600.0,
 246              ],
 247              'day' => [
 248                  'yr' => 2.73785078713210E-03,
 249                  'day' => 1.0,
 250                  'hr' => 24.0,
 251                  'mn' => 1440.0,
 252                  'sec' => 86400.0,
 253              ],
 254              'hr' => [
 255                  'yr' => 1.14077116130504E-04,
 256                  'day' => 4.16666666666667E-02,
 257                  'hr' => 1.0,
 258                  'mn' => 60.0,
 259                  'sec' => 3600.0,
 260              ],
 261              'mn' => [
 262                  'yr' => 1.90128526884174E-06,
 263                  'day' => 6.94444444444444E-04,
 264                  'hr' => 1.66666666666667E-02,
 265                  'mn' => 1.0,
 266                  'sec' => 60.0,
 267              ],
 268              'sec' => [
 269                  'yr' => 3.16880878140289E-08,
 270                  'day' => 1.15740740740741E-05,
 271                  'hr' => 2.77777777777778E-04,
 272                  'mn' => 1.66666666666667E-02,
 273                  'sec' => 1.0,
 274              ],
 275          ],
 276          'Pressure' => [
 277              'Pa' => [
 278                  'Pa' => 1.0,
 279                  'p' => 1.0,
 280                  'atm' => 9.86923299998193E-06,
 281                  'at' => 9.86923299998193E-06,
 282                  'mmHg' => 7.50061707998627E-03,
 283              ],
 284              'p' => [
 285                  'Pa' => 1.0,
 286                  'p' => 1.0,
 287                  'atm' => 9.86923299998193E-06,
 288                  'at' => 9.86923299998193E-06,
 289                  'mmHg' => 7.50061707998627E-03,
 290              ],
 291              'atm' => [
 292                  'Pa' => 1.01324996583000E+05,
 293                  'p' => 1.01324996583000E+05,
 294                  'atm' => 1.0,
 295                  'at' => 1.0,
 296                  'mmHg' => 760.0,
 297              ],
 298              'at' => [
 299                  'Pa' => 1.01324996583000E+05,
 300                  'p' => 1.01324996583000E+05,
 301                  'atm' => 1.0,
 302                  'at' => 1.0,
 303                  'mmHg' => 760.0,
 304              ],
 305              'mmHg' => [
 306                  'Pa' => 1.33322363925000E+02,
 307                  'p' => 1.33322363925000E+02,
 308                  'atm' => 1.31578947368421E-03,
 309                  'at' => 1.31578947368421E-03,
 310                  'mmHg' => 1.0,
 311              ],
 312          ],
 313          'Force' => [
 314              'N' => [
 315                  'N' => 1.0,
 316                  'dyn' => 1.0E+5,
 317                  'dy' => 1.0E+5,
 318                  'lbf' => 2.24808923655339E-01,
 319              ],
 320              'dyn' => [
 321                  'N' => 1.0E-5,
 322                  'dyn' => 1.0,
 323                  'dy' => 1.0,
 324                  'lbf' => 2.24808923655339E-06,
 325              ],
 326              'dy' => [
 327                  'N' => 1.0E-5,
 328                  'dyn' => 1.0,
 329                  'dy' => 1.0,
 330                  'lbf' => 2.24808923655339E-06,
 331              ],
 332              'lbf' => [
 333                  'N' => 4.448222,
 334                  'dyn' => 4.448222E+5,
 335                  'dy' => 4.448222E+5,
 336                  'lbf' => 1.0,
 337              ],
 338          ],
 339          'Energy' => [
 340              'J' => [
 341                  'J' => 1.0,
 342                  'e' => 9.99999519343231E+06,
 343                  'c' => 2.39006249473467E-01,
 344                  'cal' => 2.38846190642017E-01,
 345                  'eV' => 6.24145700000000E+18,
 346                  'ev' => 6.24145700000000E+18,
 347                  'HPh' => 3.72506430801000E-07,
 348                  'hh' => 3.72506430801000E-07,
 349                  'Wh' => 2.77777916238711E-04,
 350                  'wh' => 2.77777916238711E-04,
 351                  'flb' => 2.37304222192651E+01,
 352                  'BTU' => 9.47815067349015E-04,
 353                  'btu' => 9.47815067349015E-04,
 354              ],
 355              'e' => [
 356                  'J' => 1.00000048065700E-07,
 357                  'e' => 1.0,
 358                  'c' => 2.39006364353494E-08,
 359                  'cal' => 2.38846305445111E-08,
 360                  'eV' => 6.24146000000000E+11,
 361                  'ev' => 6.24146000000000E+11,
 362                  'HPh' => 3.72506609848824E-14,
 363                  'hh' => 3.72506609848824E-14,
 364                  'Wh' => 2.77778049754611E-11,
 365                  'wh' => 2.77778049754611E-11,
 366                  'flb' => 2.37304336254586E-06,
 367                  'BTU' => 9.47815522922962E-11,
 368                  'btu' => 9.47815522922962E-11,
 369              ],
 370              'c' => [
 371                  'J' => 4.18399101363672E+00,
 372                  'e' => 4.18398900257312E+07,
 373                  'c' => 1.0,
 374                  'cal' => 9.99330315287563E-01,
 375                  'eV' => 2.61142000000000E+19,
 376                  'ev' => 2.61142000000000E+19,
 377                  'HPh' => 1.55856355899327E-06,
 378                  'hh' => 1.55856355899327E-06,
 379                  'Wh' => 1.16222030532950E-03,
 380                  'wh' => 1.16222030532950E-03,
 381                  'flb' => 9.92878733152102E+01,
 382                  'BTU' => 3.96564972437776E-03,
 383                  'btu' => 3.96564972437776E-03,
 384              ],
 385              'cal' => [
 386                  'J' => 4.18679484613929E+00,
 387                  'e' => 4.18679283372801E+07,
 388                  'c' => 1.00067013349059E+00,
 389                  'cal' => 1.0,
 390                  'eV' => 2.61317000000000E+19,
 391                  'ev' => 2.61317000000000E+19,
 392                  'HPh' => 1.55960800463137E-06,
 393                  'hh' => 1.55960800463137E-06,
 394                  'Wh' => 1.16299914807955E-03,
 395                  'wh' => 1.16299914807955E-03,
 396                  'flb' => 9.93544094443283E+01,
 397                  'BTU' => 3.96830723907002E-03,
 398                  'btu' => 3.96830723907002E-03,
 399              ],
 400              'eV' => [
 401                  'J' => 1.60219000146921E-19,
 402                  'e' => 1.60218923136574E-12,
 403                  'c' => 3.82933423195043E-20,
 404                  'cal' => 3.82676978535648E-20,
 405                  'eV' => 1.0,
 406                  'ev' => 1.0,
 407                  'HPh' => 5.96826078912344E-26,
 408                  'hh' => 5.96826078912344E-26,
 409                  'Wh' => 4.45053000026614E-23,
 410                  'wh' => 4.45053000026614E-23,
 411                  'flb' => 3.80206452103492E-18,
 412                  'BTU' => 1.51857982414846E-22,
 413                  'btu' => 1.51857982414846E-22,
 414              ],
 415              'ev' => [
 416                  'J' => 1.60219000146921E-19,
 417                  'e' => 1.60218923136574E-12,
 418                  'c' => 3.82933423195043E-20,
 419                  'cal' => 3.82676978535648E-20,
 420                  'eV' => 1.0,
 421                  'ev' => 1.0,
 422                  'HPh' => 5.96826078912344E-26,
 423                  'hh' => 5.96826078912344E-26,
 424                  'Wh' => 4.45053000026614E-23,
 425                  'wh' => 4.45053000026614E-23,
 426                  'flb' => 3.80206452103492E-18,
 427                  'BTU' => 1.51857982414846E-22,
 428                  'btu' => 1.51857982414846E-22,
 429              ],
 430              'HPh' => [
 431                  'J' => 2.68451741316170E+06,
 432                  'e' => 2.68451612283024E+13,
 433                  'c' => 6.41616438565991E+05,
 434                  'cal' => 6.41186757845835E+05,
 435                  'eV' => 1.67553000000000E+25,
 436                  'ev' => 1.67553000000000E+25,
 437                  'HPh' => 1.0,
 438                  'hh' => 1.0,
 439                  'Wh' => 7.45699653134593E+02,
 440                  'wh' => 7.45699653134593E+02,
 441                  'flb' => 6.37047316692964E+07,
 442                  'BTU' => 2.54442605275546E+03,
 443                  'btu' => 2.54442605275546E+03,
 444              ],
 445              'hh' => [
 446                  'J' => 2.68451741316170E+06,
 447                  'e' => 2.68451612283024E+13,
 448                  'c' => 6.41616438565991E+05,
 449                  'cal' => 6.41186757845835E+05,
 450                  'eV' => 1.67553000000000E+25,
 451                  'ev' => 1.67553000000000E+25,
 452                  'HPh' => 1.0,
 453                  'hh' => 1.0,
 454                  'Wh' => 7.45699653134593E+02,
 455                  'wh' => 7.45699653134593E+02,
 456                  'flb' => 6.37047316692964E+07,
 457                  'BTU' => 2.54442605275546E+03,
 458                  'btu' => 2.54442605275546E+03,
 459              ],
 460              'Wh' => [
 461                  'J' => 3.59999820554720E+03,
 462                  'e' => 3.59999647518369E+10,
 463                  'c' => 8.60422069219046E+02,
 464                  'cal' => 8.59845857713046E+02,
 465                  'eV' => 2.24692340000000E+22,
 466                  'ev' => 2.24692340000000E+22,
 467                  'HPh' => 1.34102248243839E-03,
 468                  'hh' => 1.34102248243839E-03,
 469                  'Wh' => 1.0,
 470                  'wh' => 1.0,
 471                  'flb' => 8.54294774062316E+04,
 472                  'BTU' => 3.41213254164705E+00,
 473                  'btu' => 3.41213254164705E+00,
 474              ],
 475              'wh' => [
 476                  'J' => 3.59999820554720E+03,
 477                  'e' => 3.59999647518369E+10,
 478                  'c' => 8.60422069219046E+02,
 479                  'cal' => 8.59845857713046E+02,
 480                  'eV' => 2.24692340000000E+22,
 481                  'ev' => 2.24692340000000E+22,
 482                  'HPh' => 1.34102248243839E-03,
 483                  'hh' => 1.34102248243839E-03,
 484                  'Wh' => 1.0,
 485                  'wh' => 1.0,
 486                  'flb' => 8.54294774062316E+04,
 487                  'BTU' => 3.41213254164705E+00,
 488                  'btu' => 3.41213254164705E+00,
 489              ],
 490              'flb' => [
 491                  'J' => 4.21400003236424E-02,
 492                  'e' => 4.21399800687660E+05,
 493                  'c' => 1.00717234301644E-02,
 494                  'cal' => 1.00649785509554E-02,
 495                  'eV' => 2.63015000000000E+17,
 496                  'ev' => 2.63015000000000E+17,
 497                  'HPh' => 1.56974211145130E-08,
 498                  'hh' => 1.56974211145130E-08,
 499                  'Wh' => 1.17055614802000E-05,
 500                  'wh' => 1.17055614802000E-05,
 501                  'flb' => 1.0,
 502                  'BTU' => 3.99409272448406E-05,
 503                  'btu' => 3.99409272448406E-05,
 504              ],
 505              'BTU' => [
 506                  'J' => 1.05505813786749E+03,
 507                  'e' => 1.05505763074665E+10,
 508                  'c' => 2.52165488508168E+02,
 509                  'cal' => 2.51996617135510E+02,
 510                  'eV' => 6.58510000000000E+21,
 511                  'ev' => 6.58510000000000E+21,
 512                  'HPh' => 3.93015941224568E-04,
 513                  'hh' => 3.93015941224568E-04,
 514                  'Wh' => 2.93071851047526E-01,
 515                  'wh' => 2.93071851047526E-01,
 516                  'flb' => 2.50369750774671E+04,
 517                  'BTU' => 1.0,
 518                  'btu' => 1.0,
 519              ],
 520              'btu' => [
 521                  'J' => 1.05505813786749E+03,
 522                  'e' => 1.05505763074665E+10,
 523                  'c' => 2.52165488508168E+02,
 524                  'cal' => 2.51996617135510E+02,
 525                  'eV' => 6.58510000000000E+21,
 526                  'ev' => 6.58510000000000E+21,
 527                  'HPh' => 3.93015941224568E-04,
 528                  'hh' => 3.93015941224568E-04,
 529                  'Wh' => 2.93071851047526E-01,
 530                  'wh' => 2.93071851047526E-01,
 531                  'flb' => 2.50369750774671E+04,
 532                  'BTU' => 1.0,
 533                  'btu' => 1.0,
 534              ],
 535          ],
 536          'Power' => [
 537              'HP' => [
 538                  'HP' => 1.0,
 539                  'h' => 1.0,
 540                  'W' => 7.45701000000000E+02,
 541                  'w' => 7.45701000000000E+02,
 542              ],
 543              'h' => [
 544                  'HP' => 1.0,
 545                  'h' => 1.0,
 546                  'W' => 7.45701000000000E+02,
 547                  'w' => 7.45701000000000E+02,
 548              ],
 549              'W' => [
 550                  'HP' => 1.34102006031908E-03,
 551                  'h' => 1.34102006031908E-03,
 552                  'W' => 1.0,
 553                  'w' => 1.0,
 554              ],
 555              'w' => [
 556                  'HP' => 1.34102006031908E-03,
 557                  'h' => 1.34102006031908E-03,
 558                  'W' => 1.0,
 559                  'w' => 1.0,
 560              ],
 561          ],
 562          'Magnetism' => [
 563              'T' => [
 564                  'T' => 1.0,
 565                  'ga' => 10000.0,
 566              ],
 567              'ga' => [
 568                  'T' => 0.0001,
 569                  'ga' => 1.0,
 570              ],
 571          ],
 572          'Liquid' => [
 573              'tsp' => [
 574                  'tsp' => 1.0,
 575                  'tbs' => 3.33333333333333E-01,
 576                  'oz' => 1.66666666666667E-01,
 577                  'cup' => 2.08333333333333E-02,
 578                  'pt' => 1.04166666666667E-02,
 579                  'us_pt' => 1.04166666666667E-02,
 580                  'uk_pt' => 8.67558516821960E-03,
 581                  'qt' => 5.20833333333333E-03,
 582                  'gal' => 1.30208333333333E-03,
 583                  'l' => 4.92999408400710E-03,
 584                  'lt' => 4.92999408400710E-03,
 585              ],
 586              'tbs' => [
 587                  'tsp' => 3.00000000000000E+00,
 588                  'tbs' => 1.0,
 589                  'oz' => 5.00000000000000E-01,
 590                  'cup' => 6.25000000000000E-02,
 591                  'pt' => 3.12500000000000E-02,
 592                  'us_pt' => 3.12500000000000E-02,
 593                  'uk_pt' => 2.60267555046588E-02,
 594                  'qt' => 1.56250000000000E-02,
 595                  'gal' => 3.90625000000000E-03,
 596                  'l' => 1.47899822520213E-02,
 597                  'lt' => 1.47899822520213E-02,
 598              ],
 599              'oz' => [
 600                  'tsp' => 6.00000000000000E+00,
 601                  'tbs' => 2.00000000000000E+00,
 602                  'oz' => 1.0,
 603                  'cup' => 1.25000000000000E-01,
 604                  'pt' => 6.25000000000000E-02,
 605                  'us_pt' => 6.25000000000000E-02,
 606                  'uk_pt' => 5.20535110093176E-02,
 607                  'qt' => 3.12500000000000E-02,
 608                  'gal' => 7.81250000000000E-03,
 609                  'l' => 2.95799645040426E-02,
 610                  'lt' => 2.95799645040426E-02,
 611              ],
 612              'cup' => [
 613                  'tsp' => 4.80000000000000E+01,
 614                  'tbs' => 1.60000000000000E+01,
 615                  'oz' => 8.00000000000000E+00,
 616                  'cup' => 1.0,
 617                  'pt' => 5.00000000000000E-01,
 618                  'us_pt' => 5.00000000000000E-01,
 619                  'uk_pt' => 4.16428088074541E-01,
 620                  'qt' => 2.50000000000000E-01,
 621                  'gal' => 6.25000000000000E-02,
 622                  'l' => 2.36639716032341E-01,
 623                  'lt' => 2.36639716032341E-01,
 624              ],
 625              'pt' => [
 626                  'tsp' => 9.60000000000000E+01,
 627                  'tbs' => 3.20000000000000E+01,
 628                  'oz' => 1.60000000000000E+01,
 629                  'cup' => 2.00000000000000E+00,
 630                  'pt' => 1.0,
 631                  'us_pt' => 1.0,
 632                  'uk_pt' => 8.32856176149081E-01,
 633                  'qt' => 5.00000000000000E-01,
 634                  'gal' => 1.25000000000000E-01,
 635                  'l' => 4.73279432064682E-01,
 636                  'lt' => 4.73279432064682E-01,
 637              ],
 638              'us_pt' => [
 639                  'tsp' => 9.60000000000000E+01,
 640                  'tbs' => 3.20000000000000E+01,
 641                  'oz' => 1.60000000000000E+01,
 642                  'cup' => 2.00000000000000E+00,
 643                  'pt' => 1.0,
 644                  'us_pt' => 1.0,
 645                  'uk_pt' => 8.32856176149081E-01,
 646                  'qt' => 5.00000000000000E-01,
 647                  'gal' => 1.25000000000000E-01,
 648                  'l' => 4.73279432064682E-01,
 649                  'lt' => 4.73279432064682E-01,
 650              ],
 651              'uk_pt' => [
 652                  'tsp' => 1.15266000000000E+02,
 653                  'tbs' => 3.84220000000000E+01,
 654                  'oz' => 1.92110000000000E+01,
 655                  'cup' => 2.40137500000000E+00,
 656                  'pt' => 1.20068750000000E+00,
 657                  'us_pt' => 1.20068750000000E+00,
 658                  'uk_pt' => 1.0,
 659                  'qt' => 6.00343750000000E-01,
 660                  'gal' => 1.50085937500000E-01,
 661                  'l' => 5.68260698087162E-01,
 662                  'lt' => 5.68260698087162E-01,
 663              ],
 664              'qt' => [
 665                  'tsp' => 1.92000000000000E+02,
 666                  'tbs' => 6.40000000000000E+01,
 667                  'oz' => 3.20000000000000E+01,
 668                  'cup' => 4.00000000000000E+00,
 669                  'pt' => 2.00000000000000E+00,
 670                  'us_pt' => 2.00000000000000E+00,
 671                  'uk_pt' => 1.66571235229816E+00,
 672                  'qt' => 1.0,
 673                  'gal' => 2.50000000000000E-01,
 674                  'l' => 9.46558864129363E-01,
 675                  'lt' => 9.46558864129363E-01,
 676              ],
 677              'gal' => [
 678                  'tsp' => 7.68000000000000E+02,
 679                  'tbs' => 2.56000000000000E+02,
 680                  'oz' => 1.28000000000000E+02,
 681                  'cup' => 1.60000000000000E+01,
 682                  'pt' => 8.00000000000000E+00,
 683                  'us_pt' => 8.00000000000000E+00,
 684                  'uk_pt' => 6.66284940919265E+00,
 685                  'qt' => 4.00000000000000E+00,
 686                  'gal' => 1.0,
 687                  'l' => 3.78623545651745E+00,
 688                  'lt' => 3.78623545651745E+00,
 689              ],
 690              'l' => [
 691                  'tsp' => 2.02840000000000E+02,
 692                  'tbs' => 6.76133333333333E+01,
 693                  'oz' => 3.38066666666667E+01,
 694                  'cup' => 4.22583333333333E+00,
 695                  'pt' => 2.11291666666667E+00,
 696                  'us_pt' => 2.11291666666667E+00,
 697                  'uk_pt' => 1.75975569552166E+00,
 698                  'qt' => 1.05645833333333E+00,
 699                  'gal' => 2.64114583333333E-01,
 700                  'l' => 1.0,
 701                  'lt' => 1.0,
 702              ],
 703              'lt' => [
 704                  'tsp' => 2.02840000000000E+02,
 705                  'tbs' => 6.76133333333333E+01,
 706                  'oz' => 3.38066666666667E+01,
 707                  'cup' => 4.22583333333333E+00,
 708                  'pt' => 2.11291666666667E+00,
 709                  'us_pt' => 2.11291666666667E+00,
 710                  'uk_pt' => 1.75975569552166E+00,
 711                  'qt' => 1.05645833333333E+00,
 712                  'gal' => 2.64114583333333E-01,
 713                  'l' => 1.0,
 714                  'lt' => 1.0,
 715              ],
 716          ],
 717      ];
 718  
 719      /**
 720       * parseComplex.
 721       *
 722       * Parses a complex number into its real and imaginary parts, and an I or J suffix
 723       *
 724       * @deprecated 2.0.0 No longer used by internal code. Please use the Complex\Complex class instead
 725       *
 726       * @param string $complexNumber The complex number
 727       *
 728       * @return mixed[] Indexed on "real", "imaginary" and "suffix"
 729       */
 730      public static function parseComplex($complexNumber)
 731      {
 732          $complex = new Complex($complexNumber);
 733  
 734          return [
 735              'real' => $complex->getReal(),
 736              'imaginary' => $complex->getImaginary(),
 737              'suffix' => $complex->getSuffix(),
 738          ];
 739      }
 740  
 741      /**
 742       * Formats a number base string value with leading zeroes.
 743       *
 744       * @param string $xVal The "number" to pad
 745       * @param int $places The length that we want to pad this value
 746       *
 747       * @return string The padded "number"
 748       */
 749      private static function nbrConversionFormat($xVal, $places)
 750      {
 751          if ($places !== null) {
 752              if (is_numeric($places)) {
 753                  $places = (int) $places;
 754              } else {
 755                  return Functions::VALUE();
 756              }
 757              if ($places < 0) {
 758                  return Functions::NAN();
 759              }
 760              if (strlen($xVal) <= $places) {
 761                  return substr(str_pad($xVal, $places, '0', STR_PAD_LEFT), -10);
 762              }
 763  
 764              return Functions::NAN();
 765          }
 766  
 767          return substr($xVal, -10);
 768      }
 769  
 770      /**
 771       * BESSELI.
 772       *
 773       *    Returns the modified Bessel function In(x), which is equivalent to the Bessel function evaluated
 774       *        for purely imaginary arguments
 775       *
 776       *    Excel Function:
 777       *        BESSELI(x,ord)
 778       *
 779       * @category Engineering Functions
 780       *
 781       * @param float $x The value at which to evaluate the function.
 782       *                                If x is nonnumeric, BESSELI returns the #VALUE! error value.
 783       * @param int $ord The order of the Bessel function.
 784       *                                If ord is not an integer, it is truncated.
 785       *                                If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
 786       *                                If $ord < 0, BESSELI returns the #NUM! error value.
 787       *
 788       * @return float
 789       */
 790      public static function BESSELI($x, $ord)
 791      {
 792          $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
 793          $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
 794  
 795          if ((is_numeric($x)) && (is_numeric($ord))) {
 796              $ord = floor($ord);
 797              if ($ord < 0) {
 798                  return Functions::NAN();
 799              }
 800  
 801              if (abs($x) <= 30) {
 802                  $fResult = $fTerm = pow($x / 2, $ord) / MathTrig::FACT($ord);
 803                  $ordK = 1;
 804                  $fSqrX = ($x * $x) / 4;
 805                  do {
 806                      $fTerm *= $fSqrX;
 807                      $fTerm /= ($ordK * ($ordK + $ord));
 808                      $fResult += $fTerm;
 809                  } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
 810              } else {
 811                  $f_2_PI = 2 * M_PI;
 812  
 813                  $fXAbs = abs($x);
 814                  $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
 815                  if (($ord & 1) && ($x < 0)) {
 816                      $fResult = -$fResult;
 817                  }
 818              }
 819  
 820              return (is_nan($fResult)) ? Functions::NAN() : $fResult;
 821          }
 822  
 823          return Functions::VALUE();
 824      }
 825  
 826      /**
 827       * BESSELJ.
 828       *
 829       *    Returns the Bessel function
 830       *
 831       *    Excel Function:
 832       *        BESSELJ(x,ord)
 833       *
 834       * @category Engineering Functions
 835       *
 836       * @param float $x The value at which to evaluate the function.
 837       *                                If x is nonnumeric, BESSELJ returns the #VALUE! error value.
 838       * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
 839       *                                If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
 840       *                                If $ord < 0, BESSELJ returns the #NUM! error value.
 841       *
 842       * @return float
 843       */
 844      public static function BESSELJ($x, $ord)
 845      {
 846          $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
 847          $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
 848  
 849          if ((is_numeric($x)) && (is_numeric($ord))) {
 850              $ord = floor($ord);
 851              if ($ord < 0) {
 852                  return Functions::NAN();
 853              }
 854  
 855              $fResult = 0;
 856              if (abs($x) <= 30) {
 857                  $fResult = $fTerm = pow($x / 2, $ord) / MathTrig::FACT($ord);
 858                  $ordK = 1;
 859                  $fSqrX = ($x * $x) / -4;
 860                  do {
 861                      $fTerm *= $fSqrX;
 862                      $fTerm /= ($ordK * ($ordK + $ord));
 863                      $fResult += $fTerm;
 864                  } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
 865              } else {
 866                  $f_PI_DIV_2 = M_PI / 2;
 867                  $f_PI_DIV_4 = M_PI / 4;
 868  
 869                  $fXAbs = abs($x);
 870                  $fResult = sqrt(Functions::M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4);
 871                  if (($ord & 1) && ($x < 0)) {
 872                      $fResult = -$fResult;
 873                  }
 874              }
 875  
 876              return (is_nan($fResult)) ? Functions::NAN() : $fResult;
 877          }
 878  
 879          return Functions::VALUE();
 880      }
 881  
 882      private static function besselK0($fNum)
 883      {
 884          if ($fNum <= 2) {
 885              $fNum2 = $fNum * 0.5;
 886              $y = ($fNum2 * $fNum2);
 887              $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
 888                  (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
 889                                      (0.10750e-3 + $y * 0.74e-5))))));
 890          } else {
 891              $y = 2 / $fNum;
 892              $fRet = exp(-$fNum) / sqrt($fNum) *
 893                  (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
 894                                  (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
 895          }
 896  
 897          return $fRet;
 898      }
 899  
 900      private static function besselK1($fNum)
 901      {
 902          if ($fNum <= 2) {
 903              $fNum2 = $fNum * 0.5;
 904              $y = ($fNum2 * $fNum2);
 905              $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
 906                  (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
 907                                      (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
 908          } else {
 909              $y = 2 / $fNum;
 910              $fRet = exp(-$fNum) / sqrt($fNum) *
 911                  (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
 912                                      (0.325614e-2 + $y * (-0.68245e-3)))))));
 913          }
 914  
 915          return $fRet;
 916      }
 917  
 918      /**
 919       * BESSELK.
 920       *
 921       *    Returns the modified Bessel function Kn(x), which is equivalent to the Bessel functions evaluated
 922       *        for purely imaginary arguments.
 923       *
 924       *    Excel Function:
 925       *        BESSELK(x,ord)
 926       *
 927       * @category Engineering Functions
 928       *
 929       * @param float $x The value at which to evaluate the function.
 930       *                                If x is nonnumeric, BESSELK returns the #VALUE! error value.
 931       * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
 932       *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
 933       *                                If $ord < 0, BESSELK returns the #NUM! error value.
 934       *
 935       * @return float
 936       */
 937      public static function BESSELK($x, $ord)
 938      {
 939          $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
 940          $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
 941  
 942          if ((is_numeric($x)) && (is_numeric($ord))) {
 943              if (($ord < 0) || ($x == 0.0)) {
 944                  return Functions::NAN();
 945              }
 946  
 947              switch (floor($ord)) {
 948                  case 0:
 949                      $fBk = self::besselK0($x);
 950  
 951                      break;
 952                  case 1:
 953                      $fBk = self::besselK1($x);
 954  
 955                      break;
 956                  default:
 957                      $fTox = 2 / $x;
 958                      $fBkm = self::besselK0($x);
 959                      $fBk = self::besselK1($x);
 960                      for ($n = 1; $n < $ord; ++$n) {
 961                          $fBkp = $fBkm + $n * $fTox * $fBk;
 962                          $fBkm = $fBk;
 963                          $fBk = $fBkp;
 964                      }
 965              }
 966  
 967              return (is_nan($fBk)) ? Functions::NAN() : $fBk;
 968          }
 969  
 970          return Functions::VALUE();
 971      }
 972  
 973      private static function besselY0($fNum)
 974      {
 975          if ($fNum < 8.0) {
 976              $y = ($fNum * $fNum);
 977              $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
 978              $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
 979              $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
 980          } else {
 981              $z = 8.0 / $fNum;
 982              $y = ($z * $z);
 983              $xx = $fNum - 0.785398164;
 984              $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
 985              $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
 986              $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
 987          }
 988  
 989          return $fRet;
 990      }
 991  
 992      private static function besselY1($fNum)
 993      {
 994          if ($fNum < 8.0) {
 995              $y = ($fNum * $fNum);
 996              $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
 997                                  (-0.4237922726e7 + $y * 0.8511937935e4)))));
 998              $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
 999                              (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
1000              $fRet = $f1 / $f2 + 0.636619772 * (self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
1001          } else {
1002              $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
1003          }
1004  
1005          return $fRet;
1006      }
1007  
1008      /**
1009       * BESSELY.
1010       *
1011       * Returns the Bessel function, which is also called the Weber function or the Neumann function.
1012       *
1013       *    Excel Function:
1014       *        BESSELY(x,ord)
1015       *
1016       * @category Engineering Functions
1017       *
1018       * @param float $x The value at which to evaluate the function.
1019       *                                If x is nonnumeric, BESSELK returns the #VALUE! error value.
1020       * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
1021       *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
1022       *                                If $ord < 0, BESSELK returns the #NUM! error value.
1023       *
1024       * @return float
1025       */
1026      public static function BESSELY($x, $ord)
1027      {
1028          $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
1029          $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
1030  
1031          if ((is_numeric($x)) && (is_numeric($ord))) {
1032              if (($ord < 0) || ($x == 0.0)) {
1033                  return Functions::NAN();
1034              }
1035  
1036              switch (floor($ord)) {
1037                  case 0:
1038                      $fBy = self::besselY0($x);
1039  
1040                      break;
1041                  case 1:
1042                      $fBy = self::besselY1($x);
1043  
1044                      break;
1045                  default:
1046                      $fTox = 2 / $x;
1047                      $fBym = self::besselY0($x);
1048                      $fBy = self::besselY1($x);
1049                      for ($n = 1; $n < $ord; ++$n) {
1050                          $fByp = $n * $fTox * $fBy - $fBym;
1051                          $fBym = $fBy;
1052                          $fBy = $fByp;
1053                      }
1054              }
1055  
1056              return (is_nan($fBy)) ? Functions::NAN() : $fBy;
1057          }
1058  
1059          return Functions::VALUE();
1060      }
1061  
1062      /**
1063       * BINTODEC.
1064       *
1065       * Return a binary value as decimal.
1066       *
1067       * Excel Function:
1068       *        BIN2DEC(x)
1069       *
1070       * @category Engineering Functions
1071       *
1072       * @param string $x The binary number (as a string) that you want to convert. The number
1073       *                                cannot contain more than 10 characters (10 bits). The most significant
1074       *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1075       *                                Negative numbers are represented using two's-complement notation.
1076       *                                If number is not a valid binary number, or if number contains more than
1077       *                                10 characters (10 bits), BIN2DEC returns the #NUM! error value.
1078       *
1079       * @return string
1080       */
1081      public static function BINTODEC($x)
1082      {
1083          $x = Functions::flattenSingleValue($x);
1084  
1085          if (is_bool($x)) {
1086              if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1087                  $x = (int) $x;
1088              } else {
1089                  return Functions::VALUE();
1090              }
1091          }
1092          if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1093              $x = floor($x);
1094          }
1095          $x = (string) $x;
1096          if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1097              return Functions::NAN();
1098          }
1099          if (strlen($x) > 10) {
1100              return Functions::NAN();
1101          } elseif (strlen($x) == 10) {
1102              //    Two's Complement
1103              $x = substr($x, -9);
1104  
1105              return '-' . (512 - bindec($x));
1106          }
1107  
1108          return bindec($x);
1109      }
1110  
1111      /**
1112       * BINTOHEX.
1113       *
1114       * Return a binary value as hex.
1115       *
1116       * Excel Function:
1117       *        BIN2HEX(x[,places])
1118       *
1119       * @category Engineering Functions
1120       *
1121       * @param string $x The binary number (as a string) that you want to convert. The number
1122       *                                cannot contain more than 10 characters (10 bits). The most significant
1123       *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1124       *                                Negative numbers are represented using two's-complement notation.
1125       *                                If number is not a valid binary number, or if number contains more than
1126       *                                10 characters (10 bits), BIN2HEX returns the #NUM! error value.
1127       * @param int $places The number of characters to use. If places is omitted, BIN2HEX uses the
1128       *                                minimum number of characters necessary. Places is useful for padding the
1129       *                                return value with leading 0s (zeros).
1130       *                                If places is not an integer, it is truncated.
1131       *                                If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
1132       *                                If places is negative, BIN2HEX returns the #NUM! error value.
1133       *
1134       * @return string
1135       */
1136      public static function BINTOHEX($x, $places = null)
1137      {
1138          $x = Functions::flattenSingleValue($x);
1139          $places = Functions::flattenSingleValue($places);
1140  
1141          // Argument X
1142          if (is_bool($x)) {
1143              if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1144                  $x = (int) $x;
1145              } else {
1146                  return Functions::VALUE();
1147              }
1148          }
1149          if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1150              $x = floor($x);
1151          }
1152          $x = (string) $x;
1153          if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1154              return Functions::NAN();
1155          }
1156          if (strlen($x) > 10) {
1157              return Functions::NAN();
1158          } elseif (strlen($x) == 10) {
1159              //    Two's Complement
1160              return str_repeat('F', 8) . substr(strtoupper(dechex(bindec(substr($x, -9)))), -2);
1161          }
1162          $hexVal = (string) strtoupper(dechex(bindec($x)));
1163  
1164          return self::nbrConversionFormat($hexVal, $places);
1165      }
1166  
1167      /**
1168       * BINTOOCT.
1169       *
1170       * Return a binary value as octal.
1171       *
1172       * Excel Function:
1173       *        BIN2OCT(x[,places])
1174       *
1175       * @category Engineering Functions
1176       *
1177       * @param string $x The binary number (as a string) that you want to convert. The number
1178       *                                cannot contain more than 10 characters (10 bits). The most significant
1179       *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1180       *                                Negative numbers are represented using two's-complement notation.
1181       *                                If number is not a valid binary number, or if number contains more than
1182       *                                10 characters (10 bits), BIN2OCT returns the #NUM! error value.
1183       * @param int $places The number of characters to use. If places is omitted, BIN2OCT uses the
1184       *                                minimum number of characters necessary. Places is useful for padding the
1185       *                                return value with leading 0s (zeros).
1186       *                                If places is not an integer, it is truncated.
1187       *                                If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
1188       *                                If places is negative, BIN2OCT returns the #NUM! error value.
1189       *
1190       * @return string
1191       */
1192      public static function BINTOOCT($x, $places = null)
1193      {
1194          $x = Functions::flattenSingleValue($x);
1195          $places = Functions::flattenSingleValue($places);
1196  
1197          if (is_bool($x)) {
1198              if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1199                  $x = (int) $x;
1200              } else {
1201                  return Functions::VALUE();
1202              }
1203          }
1204          if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1205              $x = floor($x);
1206          }
1207          $x = (string) $x;
1208          if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1209              return Functions::NAN();
1210          }
1211          if (strlen($x) > 10) {
1212              return Functions::NAN();
1213          } elseif (strlen($x) == 10) {
1214              //    Two's Complement
1215              return str_repeat('7', 7) . substr(strtoupper(decoct(bindec(substr($x, -9)))), -3);
1216          }
1217          $octVal = (string) decoct(bindec($x));
1218  
1219          return self::nbrConversionFormat($octVal, $places);
1220      }
1221  
1222      /**
1223       * DECTOBIN.
1224       *
1225       * Return a decimal value as binary.
1226       *
1227       * Excel Function:
1228       *        DEC2BIN(x[,places])
1229       *
1230       * @category Engineering Functions
1231       *
1232       * @param string $x The decimal integer you want to convert. If number is negative,
1233       *                                valid place values are ignored and DEC2BIN returns a 10-character
1234       *                                (10-bit) binary number in which the most significant bit is the sign
1235       *                                bit. The remaining 9 bits are magnitude bits. Negative numbers are
1236       *                                represented using two's-complement notation.
1237       *                                If number < -512 or if number > 511, DEC2BIN returns the #NUM! error
1238       *                                value.
1239       *                                If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
1240       *                                If DEC2BIN requires more than places characters, it returns the #NUM!
1241       *                                error value.
1242       * @param int $places The number of characters to use. If places is omitted, DEC2BIN uses
1243       *                                the minimum number of characters necessary. Places is useful for
1244       *                                padding the return value with leading 0s (zeros).
1245       *                                If places is not an integer, it is truncated.
1246       *                                If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
1247       *                                If places is zero or negative, DEC2BIN returns the #NUM! error value.
1248       *
1249       * @return string
1250       */
1251      public static function DECTOBIN($x, $places = null)
1252      {
1253          $x = Functions::flattenSingleValue($x);
1254          $places = Functions::flattenSingleValue($places);
1255  
1256          if (is_bool($x)) {
1257              if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1258                  $x = (int) $x;
1259              } else {
1260                  return Functions::VALUE();
1261              }
1262          }
1263          $x = (string) $x;
1264          if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1265              return Functions::VALUE();
1266          }
1267  
1268          $x = (string) floor($x);
1269          if ($x < -512 || $x > 511) {
1270              return Functions::NAN();
1271          }
1272  
1273          $r = decbin($x);
1274          // Two's Complement
1275          $r = substr($r, -10);
1276          if (strlen($r) >= 11) {
1277              return Functions::NAN();
1278          }
1279  
1280          return self::nbrConversionFormat($r, $places);
1281      }
1282  
1283      /**
1284       * DECTOHEX.
1285       *
1286       * Return a decimal value as hex.
1287       *
1288       * Excel Function:
1289       *        DEC2HEX(x[,places])
1290       *
1291       * @category Engineering Functions
1292       *
1293       * @param string $x The decimal integer you want to convert. If number is negative,
1294       *                                places is ignored and DEC2HEX returns a 10-character (40-bit)
1295       *                                hexadecimal number in which the most significant bit is the sign
1296       *                                bit. The remaining 39 bits are magnitude bits. Negative numbers
1297       *                                are represented using two's-complement notation.
1298       *                                If number < -549,755,813,888 or if number > 549,755,813,887,
1299       *                                DEC2HEX returns the #NUM! error value.
1300       *                                If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
1301       *                                If DEC2HEX requires more than places characters, it returns the
1302       *                                #NUM! error value.
1303       * @param int $places The number of characters to use. If places is omitted, DEC2HEX uses
1304       *                                the minimum number of characters necessary. Places is useful for
1305       *                                padding the return value with leading 0s (zeros).
1306       *                                If places is not an integer, it is truncated.
1307       *                                If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
1308       *                                If places is zero or negative, DEC2HEX returns the #NUM! error value.
1309       *
1310       * @return string
1311       */
1312      public static function DECTOHEX($x, $places = null)
1313      {
1314          $x = Functions::flattenSingleValue($x);
1315          $places = Functions::flattenSingleValue($places);
1316  
1317          if (is_bool($x)) {
1318              if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1319                  $x = (int) $x;
1320              } else {
1321                  return Functions::VALUE();
1322              }
1323          }
1324          $x = (string) $x;
1325          if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1326              return Functions::VALUE();
1327          }
1328          $x = (string) floor($x);
1329          $r = strtoupper(dechex($x));
1330          if (strlen($r) == 8) {
1331              //    Two's Complement
1332              $r = 'FF' . $r;
1333          }
1334  
1335          return self::nbrConversionFormat($r, $places);
1336      }
1337  
1338      /**
1339       * DECTOOCT.
1340       *
1341       * Return an decimal value as octal.
1342       *
1343       * Excel Function:
1344       *        DEC2OCT(x[,places])
1345       *
1346       * @category Engineering Functions
1347       *
1348       * @param string $x The decimal integer you want to convert. If number is negative,
1349       *                                places is ignored and DEC2OCT returns a 10-character (30-bit)
1350       *                                octal number in which the most significant bit is the sign bit.
1351       *                                The remaining 29 bits are magnitude bits. Negative numbers are
1352       *                                represented using two's-complement notation.
1353       *                                If number < -536,870,912 or if number > 536,870,911, DEC2OCT
1354       *                                returns the #NUM! error value.
1355       *                                If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
1356       *                                If DEC2OCT requires more than places characters, it returns the
1357       *                                #NUM! error value.
1358       * @param int $places The number of characters to use. If places is omitted, DEC2OCT uses
1359       *                                the minimum number of characters necessary. Places is useful for
1360       *                                padding the return value with leading 0s (zeros).
1361       *                                If places is not an integer, it is truncated.
1362       *                                If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
1363       *                                If places is zero or negative, DEC2OCT returns the #NUM! error value.
1364       *
1365       * @return string
1366       */
1367      public static function DECTOOCT($x, $places = null)
1368      {
1369          $xorig = $x;
1370          $x = Functions::flattenSingleValue($x);
1371          $places = Functions::flattenSingleValue($places);
1372  
1373          if (is_bool($x)) {
1374              if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1375                  $x = (int) $x;
1376              } else {
1377                  return Functions::VALUE();
1378              }
1379          }
1380          $x = (string) $x;
1381          if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1382              return Functions::VALUE();
1383          }
1384          $x = (string) floor($x);
1385          $r = decoct($x);
1386          if (strlen($r) == 11) {
1387              //    Two's Complement
1388              $r = substr($r, -10);
1389          }
1390  
1391          return self::nbrConversionFormat($r, $places);
1392      }
1393  
1394      /**
1395       * HEXTOBIN.
1396       *
1397       * Return a hex value as binary.
1398       *
1399       * Excel Function:
1400       *        HEX2BIN(x[,places])
1401       *
1402       * @category Engineering Functions
1403       *
1404       * @param string $x the hexadecimal number you want to convert.
1405       *                  Number cannot contain more than 10 characters.
1406       *                  The most significant bit of number is the sign bit (40th bit from the right).
1407       *                  The remaining 9 bits are magnitude bits.
1408       *                  Negative numbers are represented using two's-complement notation.
1409       *                  If number is negative, HEX2BIN ignores places and returns a 10-character binary number.
1410       *                  If number is negative, it cannot be less than FFFFFFFE00,
1411       *                      and if number is positive, it cannot be greater than 1FF.
1412       *                  If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
1413       *                  If HEX2BIN requires more than places characters, it returns the #NUM! error value.
1414       * @param int $places The number of characters to use. If places is omitted,
1415       *                                    HEX2BIN uses the minimum number of characters necessary. Places
1416       *                                    is useful for padding the return value with leading 0s (zeros).
1417       *                                    If places is not an integer, it is truncated.
1418       *                                    If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
1419       *                                    If places is negative, HEX2BIN returns the #NUM! error value.
1420       *
1421       * @return string
1422       */
1423      public static function HEXTOBIN($x, $places = null)
1424      {
1425          $x = Functions::flattenSingleValue($x);
1426          $places = Functions::flattenSingleValue($places);
1427  
1428          if (is_bool($x)) {
1429              return Functions::VALUE();
1430          }
1431          $x = (string) $x;
1432          if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1433              return Functions::NAN();
1434          }
1435  
1436          return self::DECTOBIN(self::HEXTODEC($x), $places);
1437      }
1438  
1439      /**
1440       * HEXTODEC.
1441       *
1442       * Return a hex value as decimal.
1443       *
1444       * Excel Function:
1445       *        HEX2DEC(x)
1446       *
1447       * @category Engineering Functions
1448       *
1449       * @param string $x The hexadecimal number you want to convert. This number cannot
1450       *                                contain more than 10 characters (40 bits). The most significant
1451       *                                bit of number is the sign bit. The remaining 39 bits are magnitude
1452       *                                bits. Negative numbers are represented using two's-complement
1453       *                                notation.
1454       *                                If number is not a valid hexadecimal number, HEX2DEC returns the
1455       *                                #NUM! error value.
1456       *
1457       * @return string
1458       */
1459      public static function HEXTODEC($x)
1460      {
1461          $x = Functions::flattenSingleValue($x);
1462  
1463          if (is_bool($x)) {
1464              return Functions::VALUE();
1465          }
1466          $x = (string) $x;
1467          if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1468              return Functions::NAN();
1469          }
1470  
1471          if (strlen($x) > 10) {
1472              return Functions::NAN();
1473          }
1474  
1475          $binX = '';
1476          foreach (str_split($x) as $char) {
1477              $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
1478          }
1479          if (strlen($binX) == 40 && $binX[0] == '1') {
1480              for ($i = 0; $i < 40; ++$i) {
1481                  $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
1482              }
1483  
1484              return (bindec($binX) + 1) * -1;
1485          }
1486  
1487          return bindec($binX);
1488      }
1489  
1490      /**
1491       * HEXTOOCT.
1492       *
1493       * Return a hex value as octal.
1494       *
1495       * Excel Function:
1496       *        HEX2OCT(x[,places])
1497       *
1498       * @category Engineering Functions
1499       *
1500       * @param string $x The hexadecimal number you want to convert. Number cannot
1501       *                                    contain more than 10 characters. The most significant bit of
1502       *                                    number is the sign bit. The remaining 39 bits are magnitude
1503       *                                    bits. Negative numbers are represented using two's-complement
1504       *                                    notation.
1505       *                                    If number is negative, HEX2OCT ignores places and returns a
1506       *                                    10-character octal number.
1507       *                                    If number is negative, it cannot be less than FFE0000000, and
1508       *                                    if number is positive, it cannot be greater than 1FFFFFFF.
1509       *                                    If number is not a valid hexadecimal number, HEX2OCT returns
1510       *                                    the #NUM! error value.
1511       *                                    If HEX2OCT requires more than places characters, it returns
1512       *                                    the #NUM! error value.
1513       * @param int $places The number of characters to use. If places is omitted, HEX2OCT
1514       *                                    uses the minimum number of characters necessary. Places is
1515       *                                    useful for padding the return value with leading 0s (zeros).
1516       *                                    If places is not an integer, it is truncated.
1517       *                                    If places is nonnumeric, HEX2OCT returns the #VALUE! error
1518       *                                    value.
1519       *                                    If places is negative, HEX2OCT returns the #NUM! error value.
1520       *
1521       * @return string
1522       */
1523      public static function HEXTOOCT($x, $places = null)
1524      {
1525          $x = Functions::flattenSingleValue($x);
1526          $places = Functions::flattenSingleValue($places);
1527  
1528          if (is_bool($x)) {
1529              return Functions::VALUE();
1530          }
1531          $x = (string) $x;
1532          if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1533              return Functions::NAN();
1534          }
1535  
1536          $decimal = self::HEXTODEC($x);
1537          if ($decimal < -536870912 || $decimal > 536870911) {
1538              return Functions::NAN();
1539          }
1540  
1541          return self::DECTOOCT($decimal, $places);
1542      }
1543  
1544      /**
1545       * OCTTOBIN.
1546       *
1547       * Return an octal value as binary.
1548       *
1549       * Excel Function:
1550       *        OCT2BIN(x[,places])
1551       *
1552       * @category Engineering Functions
1553       *
1554       * @param string $x The octal number you want to convert. Number may not
1555       *                                    contain more than 10 characters. The most significant
1556       *                                    bit of number is the sign bit. The remaining 29 bits
1557       *                                    are magnitude bits. Negative numbers are represented
1558       *                                    using two's-complement notation.
1559       *                                    If number is negative, OCT2BIN ignores places and returns
1560       *                                    a 10-character binary number.
1561       *                                    If number is negative, it cannot be less than 7777777000,
1562       *                                    and if number is positive, it cannot be greater than 777.
1563       *                                    If number is not a valid octal number, OCT2BIN returns
1564       *                                    the #NUM! error value.
1565       *                                    If OCT2BIN requires more than places characters, it
1566       *                                    returns the #NUM! error value.
1567       * @param int $places The number of characters to use. If places is omitted,
1568       *                                    OCT2BIN uses the minimum number of characters necessary.
1569       *                                    Places is useful for padding the return value with
1570       *                                    leading 0s (zeros).
1571       *                                    If places is not an integer, it is truncated.
1572       *                                    If places is nonnumeric, OCT2BIN returns the #VALUE!
1573       *                                    error value.
1574       *                                    If places is negative, OCT2BIN returns the #NUM! error
1575       *                                    value.
1576       *
1577       * @return string
1578       */
1579      public static function OCTTOBIN($x, $places = null)
1580      {
1581          $x = Functions::flattenSingleValue($x);
1582          $places = Functions::flattenSingleValue($places);
1583  
1584          if (is_bool($x)) {
1585              return Functions::VALUE();
1586          }
1587          $x = (string) $x;
1588          if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1589              return Functions::NAN();
1590          }
1591  
1592          return self::DECTOBIN(self::OCTTODEC($x), $places);
1593      }
1594  
1595      /**
1596       * OCTTODEC.
1597       *
1598       * Return an octal value as decimal.
1599       *
1600       * Excel Function:
1601       *        OCT2DEC(x)
1602       *
1603       * @category Engineering Functions
1604       *
1605       * @param string $x The octal number you want to convert. Number may not contain
1606       *                                more than 10 octal characters (30 bits). The most significant
1607       *                                bit of number is the sign bit. The remaining 29 bits are
1608       *                                magnitude bits. Negative numbers are represented using
1609       *                                two's-complement notation.
1610       *                                If number is not a valid octal number, OCT2DEC returns the
1611       *                                #NUM! error value.
1612       *
1613       * @return string
1614       */
1615      public static function OCTTODEC($x)
1616      {
1617          $x = Functions::flattenSingleValue($x);
1618  
1619          if (is_bool($x)) {
1620              return Functions::VALUE();
1621          }
1622          $x = (string) $x;
1623          if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1624              return Functions::NAN();
1625          }
1626          $binX = '';
1627          foreach (str_split($x) as $char) {
1628              $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
1629          }
1630          if (strlen($binX) == 30 && $binX[0] == '1') {
1631              for ($i = 0; $i < 30; ++$i) {
1632                  $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
1633              }
1634  
1635              return (bindec($binX) + 1) * -1;
1636          }
1637  
1638          return bindec($binX);
1639      }
1640  
1641      /**
1642       * OCTTOHEX.
1643       *
1644       * Return an octal value as hex.
1645       *
1646       * Excel Function:
1647       *        OCT2HEX(x[,places])
1648       *
1649       * @category Engineering Functions
1650       *
1651       * @param string $x The octal number you want to convert. Number may not contain
1652       *                                    more than 10 octal characters (30 bits). The most significant
1653       *                                    bit of number is the sign bit. The remaining 29 bits are
1654       *                                    magnitude bits. Negative numbers are represented using
1655       *                                    two's-complement notation.
1656       *                                    If number is negative, OCT2HEX ignores places and returns a
1657       *                                    10-character hexadecimal number.
1658       *                                    If number is not a valid octal number, OCT2HEX returns the
1659       *                                    #NUM! error value.
1660       *                                    If OCT2HEX requires more than places characters, it returns
1661       *                                    the #NUM! error value.
1662       * @param int $places The number of characters to use. If places is omitted, OCT2HEX
1663       *                                    uses the minimum number of characters necessary. Places is useful
1664       *                                    for padding the return value with leading 0s (zeros).
1665       *                                    If places is not an integer, it is truncated.
1666       *                                    If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
1667       *                                    If places is negative, OCT2HEX returns the #NUM! error value.
1668       *
1669       * @return string
1670       */
1671      public static function OCTTOHEX($x, $places = null)
1672      {
1673          $x = Functions::flattenSingleValue($x);
1674          $places = Functions::flattenSingleValue($places);
1675  
1676          if (is_bool($x)) {
1677              return Functions::VALUE();
1678          }
1679          $x = (string) $x;
1680          if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1681              return Functions::NAN();
1682          }
1683          $hexVal = strtoupper(dechex(self::OCTTODEC($x)));
1684  
1685          return self::nbrConversionFormat($hexVal, $places);
1686      }
1687  
1688      /**
1689       * COMPLEX.
1690       *
1691       * Converts real and imaginary coefficients into a complex number of the form x +/- yi or x +/- yj.
1692       *
1693       * Excel Function:
1694       *        COMPLEX(realNumber,imaginary[,suffix])
1695       *
1696       * @category Engineering Functions
1697       *
1698       * @param float $realNumber the real coefficient of the complex number
1699       * @param float $imaginary the imaginary coefficient of the complex number
1700       * @param string $suffix The suffix for the imaginary component of the complex number.
1701       *                                        If omitted, the suffix is assumed to be "i".
1702       *
1703       * @return string
1704       */
1705      public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
1706      {
1707          $realNumber = ($realNumber === null) ? 0.0 : Functions::flattenSingleValue($realNumber);
1708          $imaginary = ($imaginary === null) ? 0.0 : Functions::flattenSingleValue($imaginary);
1709          $suffix = ($suffix === null) ? 'i' : Functions::flattenSingleValue($suffix);
1710  
1711          if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
1712              (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))
1713          ) {
1714              $complex = new Complex($realNumber, $imaginary, $suffix);
1715  
1716              return (string) $complex;
1717          }
1718  
1719          return Functions::VALUE();
1720      }
1721  
1722      /**
1723       * IMAGINARY.
1724       *
1725       * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
1726       *
1727       * Excel Function:
1728       *        IMAGINARY(complexNumber)
1729       *
1730       * @category Engineering Functions
1731       *
1732       * @param string $complexNumber the complex number for which you want the imaginary
1733       *                                         coefficient
1734       *
1735       * @return float
1736       */
1737      public static function IMAGINARY($complexNumber)
1738      {
1739          $complexNumber = Functions::flattenSingleValue($complexNumber);
1740  
1741          return (new Complex($complexNumber))->getImaginary();
1742      }
1743  
1744      /**
1745       * IMREAL.
1746       *
1747       * Returns the real coefficient of a complex number in x + yi or x + yj text format.
1748       *
1749       * Excel Function:
1750       *        IMREAL(complexNumber)
1751       *
1752       * @category Engineering Functions
1753       *
1754       * @param string $complexNumber the complex number for which you want the real coefficient
1755       *
1756       * @return float
1757       */
1758      public static function IMREAL($complexNumber)
1759      {
1760          $complexNumber = Functions::flattenSingleValue($complexNumber);
1761  
1762          return (new Complex($complexNumber))->getReal();
1763      }
1764  
1765      /**
1766       * IMABS.
1767       *
1768       * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
1769       *
1770       * Excel Function:
1771       *        IMABS(complexNumber)
1772       *
1773       * @param string $complexNumber the complex number for which you want the absolute value
1774       *
1775       * @return float
1776       */
1777      public static function IMABS($complexNumber)
1778      {
1779          $complexNumber = Functions::flattenSingleValue($complexNumber);
1780  
1781          return (new Complex($complexNumber))->abs();
1782      }
1783  
1784      /**
1785       * IMARGUMENT.
1786       *
1787       * Returns the argument theta of a complex number, i.e. the angle in radians from the real
1788       * axis to the representation of the number in polar coordinates.
1789       *
1790       * Excel Function:
1791       *        IMARGUMENT(complexNumber)
1792       *
1793       * @param string $complexNumber the complex number for which you want the argument theta
1794       *
1795       * @return float|string
1796       */
1797      public static function IMARGUMENT($complexNumber)
1798      {
1799          $complexNumber = Functions::flattenSingleValue($complexNumber);
1800  
1801          $complex = new Complex($complexNumber);
1802          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
1803              return Functions::DIV0();
1804          }
1805  
1806          return $complex->argument();
1807      }
1808  
1809      /**
1810       * IMCONJUGATE.
1811       *
1812       * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
1813       *
1814       * Excel Function:
1815       *        IMCONJUGATE(complexNumber)
1816       *
1817       * @param string $complexNumber the complex number for which you want the conjugate
1818       *
1819       * @return string
1820       */
1821      public static function IMCONJUGATE($complexNumber)
1822      {
1823          $complexNumber = Functions::flattenSingleValue($complexNumber);
1824  
1825          return (string) (new Complex($complexNumber))->conjugate();
1826      }
1827  
1828      /**
1829       * IMCOS.
1830       *
1831       * Returns the cosine of a complex number in x + yi or x + yj text format.
1832       *
1833       * Excel Function:
1834       *        IMCOS(complexNumber)
1835       *
1836       * @param string $complexNumber the complex number for which you want the cosine
1837       *
1838       * @return float|string
1839       */
1840      public static function IMCOS($complexNumber)
1841      {
1842          $complexNumber = Functions::flattenSingleValue($complexNumber);
1843  
1844          return (string) (new Complex($complexNumber))->cos();
1845      }
1846  
1847      /**
1848       * IMCOSH.
1849       *
1850       * Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
1851       *
1852       * Excel Function:
1853       *        IMCOSH(complexNumber)
1854       *
1855       * @param string $complexNumber the complex number for which you want the hyperbolic cosine
1856       *
1857       * @return float|string
1858       */
1859      public static function IMCOSH($complexNumber)
1860      {
1861          $complexNumber = Functions::flattenSingleValue($complexNumber);
1862  
1863          return (string) (new Complex($complexNumber))->cosh();
1864      }
1865  
1866      /**
1867       * IMCOT.
1868       *
1869       * Returns the cotangent of a complex number in x + yi or x + yj text format.
1870       *
1871       * Excel Function:
1872       *        IMCOT(complexNumber)
1873       *
1874       * @param string $complexNumber the complex number for which you want the cotangent
1875       *
1876       * @return float|string
1877       */
1878      public static function IMCOT($complexNumber)
1879      {
1880          $complexNumber = Functions::flattenSingleValue($complexNumber);
1881  
1882          return (string) (new Complex($complexNumber))->cot();
1883      }
1884  
1885      /**
1886       * IMCSC.
1887       *
1888       * Returns the cosecant of a complex number in x + yi or x + yj text format.
1889       *
1890       * Excel Function:
1891       *        IMCSC(complexNumber)
1892       *
1893       * @param string $complexNumber the complex number for which you want the cosecant
1894       *
1895       * @return float|string
1896       */
1897      public static function IMCSC($complexNumber)
1898      {
1899          $complexNumber = Functions::flattenSingleValue($complexNumber);
1900  
1901          return (string) (new Complex($complexNumber))->csc();
1902      }
1903  
1904      /**
1905       * IMCSCH.
1906       *
1907       * Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
1908       *
1909       * Excel Function:
1910       *        IMCSCH(complexNumber)
1911       *
1912       * @param string $complexNumber the complex number for which you want the hyperbolic cosecant
1913       *
1914       * @return float|string
1915       */
1916      public static function IMCSCH($complexNumber)
1917      {
1918          $complexNumber = Functions::flattenSingleValue($complexNumber);
1919  
1920          return (string) (new Complex($complexNumber))->csch();
1921      }
1922  
1923      /**
1924       * IMSIN.
1925       *
1926       * Returns the sine of a complex number in x + yi or x + yj text format.
1927       *
1928       * Excel Function:
1929       *        IMSIN(complexNumber)
1930       *
1931       * @param string $complexNumber the complex number for which you want the sine
1932       *
1933       * @return float|string
1934       */
1935      public static function IMSIN($complexNumber)
1936      {
1937          $complexNumber = Functions::flattenSingleValue($complexNumber);
1938  
1939          return (string) (new Complex($complexNumber))->sin();
1940      }
1941  
1942      /**
1943       * IMSINH.
1944       *
1945       * Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
1946       *
1947       * Excel Function:
1948       *        IMSINH(complexNumber)
1949       *
1950       * @param string $complexNumber the complex number for which you want the hyperbolic sine
1951       *
1952       * @return float|string
1953       */
1954      public static function IMSINH($complexNumber)
1955      {
1956          $complexNumber = Functions::flattenSingleValue($complexNumber);
1957  
1958          return (string) (new Complex($complexNumber))->sinh();
1959      }
1960  
1961      /**
1962       * IMSEC.
1963       *
1964       * Returns the secant of a complex number in x + yi or x + yj text format.
1965       *
1966       * Excel Function:
1967       *        IMSEC(complexNumber)
1968       *
1969       * @param string $complexNumber the complex number for which you want the secant
1970       *
1971       * @return float|string
1972       */
1973      public static function IMSEC($complexNumber)
1974      {
1975          $complexNumber = Functions::flattenSingleValue($complexNumber);
1976  
1977          return (string) (new Complex($complexNumber))->sec();
1978      }
1979  
1980      /**
1981       * IMSECH.
1982       *
1983       * Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
1984       *
1985       * Excel Function:
1986       *        IMSECH(complexNumber)
1987       *
1988       * @param string $complexNumber the complex number for which you want the hyperbolic secant
1989       *
1990       * @return float|string
1991       */
1992      public static function IMSECH($complexNumber)
1993      {
1994          $complexNumber = Functions::flattenSingleValue($complexNumber);
1995  
1996          return (string) (new Complex($complexNumber))->sech();
1997      }
1998  
1999      /**
2000       * IMTAN.
2001       *
2002       * Returns the tangent of a complex number in x + yi or x + yj text format.
2003       *
2004       * Excel Function:
2005       *        IMTAN(complexNumber)
2006       *
2007       * @param string $complexNumber the complex number for which you want the tangent
2008       *
2009       * @return float|string
2010       */
2011      public static function IMTAN($complexNumber)
2012      {
2013          $complexNumber = Functions::flattenSingleValue($complexNumber);
2014  
2015          return (string) (new Complex($complexNumber))->tan();
2016      }
2017  
2018      /**
2019       * IMSQRT.
2020       *
2021       * Returns the square root of a complex number in x + yi or x + yj text format.
2022       *
2023       * Excel Function:
2024       *        IMSQRT(complexNumber)
2025       *
2026       * @param string $complexNumber the complex number for which you want the square root
2027       *
2028       * @return string
2029       */
2030      public static function IMSQRT($complexNumber)
2031      {
2032          $complexNumber = Functions::flattenSingleValue($complexNumber);
2033  
2034          $theta = self::IMARGUMENT($complexNumber);
2035          if ($theta === Functions::DIV0()) {
2036              return '0';
2037          }
2038  
2039          return (string) (new Complex($complexNumber))->sqrt();
2040      }
2041  
2042      /**
2043       * IMLN.
2044       *
2045       * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
2046       *
2047       * Excel Function:
2048       *        IMLN(complexNumber)
2049       *
2050       * @param string $complexNumber the complex number for which you want the natural logarithm
2051       *
2052       * @return string
2053       */
2054      public static function IMLN($complexNumber)
2055      {
2056          $complexNumber = Functions::flattenSingleValue($complexNumber);
2057  
2058          $complex = new Complex($complexNumber);
2059          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
2060              return Functions::NAN();
2061          }
2062  
2063          return (string) (new Complex($complexNumber))->ln();
2064      }
2065  
2066      /**
2067       * IMLOG10.
2068       *
2069       * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
2070       *
2071       * Excel Function:
2072       *        IMLOG10(complexNumber)
2073       *
2074       * @param string $complexNumber the complex number for which you want the common logarithm
2075       *
2076       * @return string
2077       */
2078      public static function IMLOG10($complexNumber)
2079      {
2080          $complexNumber = Functions::flattenSingleValue($complexNumber);
2081  
2082          $complex = new Complex($complexNumber);
2083          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
2084              return Functions::NAN();
2085          }
2086  
2087          return (string) (new Complex($complexNumber))->log10();
2088      }
2089  
2090      /**
2091       * IMLOG2.
2092       *
2093       * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
2094       *
2095       * Excel Function:
2096       *        IMLOG2(complexNumber)
2097       *
2098       * @param string $complexNumber the complex number for which you want the base-2 logarithm
2099       *
2100       * @return string
2101       */
2102      public static function IMLOG2($complexNumber)
2103      {
2104          $complexNumber = Functions::flattenSingleValue($complexNumber);
2105  
2106          $complex = new Complex($complexNumber);
2107          if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
2108              return Functions::NAN();
2109          }
2110  
2111          return (string) (new Complex($complexNumber))->log2();
2112      }
2113  
2114      /**
2115       * IMEXP.
2116       *
2117       * Returns the exponential of a complex number in x + yi or x + yj text format.
2118       *
2119       * Excel Function:
2120       *        IMEXP(complexNumber)
2121       *
2122       * @param string $complexNumber the complex number for which you want the exponential
2123       *
2124       * @return string
2125       */
2126      public static function IMEXP($complexNumber)
2127      {
2128          $complexNumber = Functions::flattenSingleValue($complexNumber);
2129  
2130          return (string) (new Complex($complexNumber))->exp();
2131      }
2132  
2133      /**
2134       * IMPOWER.
2135       *
2136       * Returns a complex number in x + yi or x + yj text format raised to a power.
2137       *
2138       * Excel Function:
2139       *        IMPOWER(complexNumber,realNumber)
2140       *
2141       * @param string $complexNumber the complex number you want to raise to a power
2142       * @param float $realNumber the power to which you want to raise the complex number
2143       *
2144       * @return string
2145       */
2146      public static function IMPOWER($complexNumber, $realNumber)
2147      {
2148          $complexNumber = Functions::flattenSingleValue($complexNumber);
2149          $realNumber = Functions::flattenSingleValue($realNumber);
2150  
2151          if (!is_numeric($realNumber)) {
2152              return Functions::VALUE();
2153          }
2154  
2155          return (string) (new Complex($complexNumber))->pow($realNumber);
2156      }
2157  
2158      /**
2159       * IMDIV.
2160       *
2161       * Returns the quotient of two complex numbers in x + yi or x + yj text format.
2162       *
2163       * Excel Function:
2164       *        IMDIV(complexDividend,complexDivisor)
2165       *
2166       * @param string $complexDividend the complex numerator or dividend
2167       * @param string $complexDivisor the complex denominator or divisor
2168       *
2169       * @return string
2170       */
2171      public static function IMDIV($complexDividend, $complexDivisor)
2172      {
2173          $complexDividend = Functions::flattenSingleValue($complexDividend);
2174          $complexDivisor = Functions::flattenSingleValue($complexDivisor);
2175  
2176          try {
2177              return (string) (new Complex($complexDividend))->divideby(new Complex($complexDivisor));
2178          } catch (ComplexException $e) {
2179              return Functions::NAN();
2180          }
2181      }
2182  
2183      /**
2184       * IMSUB.
2185       *
2186       * Returns the difference of two complex numbers in x + yi or x + yj text format.
2187       *
2188       * Excel Function:
2189       *        IMSUB(complexNumber1,complexNumber2)
2190       *
2191       * @param string $complexNumber1 the complex number from which to subtract complexNumber2
2192       * @param string $complexNumber2 the complex number to subtract from complexNumber1
2193       *
2194       * @return string
2195       */
2196      public static function IMSUB($complexNumber1, $complexNumber2)
2197      {
2198          $complexNumber1 = Functions::flattenSingleValue($complexNumber1);
2199          $complexNumber2 = Functions::flattenSingleValue($complexNumber2);
2200  
2201          try {
2202              return (string) (new Complex($complexNumber1))->subtract(new Complex($complexNumber2));
2203          } catch (ComplexException $e) {
2204              return Functions::NAN();
2205          }
2206      }
2207  
2208      /**
2209       * IMSUM.
2210       *
2211       * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
2212       *
2213       * Excel Function:
2214       *        IMSUM(complexNumber[,complexNumber[,...]])
2215       *
2216       * @param string ...$complexNumbers Series of complex numbers to add
2217       *
2218       * @return string
2219       */
2220      public static function IMSUM(...$complexNumbers)
2221      {
2222          // Return value
2223          $returnValue = new Complex(0.0);
2224          $aArgs = Functions::flattenArray($complexNumbers);
2225  
2226          try {
2227              // Loop through the arguments
2228              foreach ($aArgs as $complex) {
2229                  $returnValue = $returnValue->add(new Complex($complex));
2230              }
2231          } catch (ComplexException $e) {
2232              return Functions::NAN();
2233          }
2234  
2235          return (string) $returnValue;
2236      }
2237  
2238      /**
2239       * IMPRODUCT.
2240       *
2241       * Returns the product of two or more complex numbers in x + yi or x + yj text format.
2242       *
2243       * Excel Function:
2244       *        IMPRODUCT(complexNumber[,complexNumber[,...]])
2245       *
2246       * @param string ...$complexNumbers Series of complex numbers to multiply
2247       *
2248       * @return string
2249       */
2250      public static function IMPRODUCT(...$complexNumbers)
2251      {
2252          // Return value
2253          $returnValue = new Complex(1.0);
2254          $aArgs = Functions::flattenArray($complexNumbers);
2255  
2256          try {
2257              // Loop through the arguments
2258              foreach ($aArgs as $complex) {
2259                  $returnValue = $returnValue->multiply(new Complex($complex));
2260              }
2261          } catch (ComplexException $e) {
2262              return Functions::NAN();
2263          }
2264  
2265          return (string) $returnValue;
2266      }
2267  
2268      /**
2269       * DELTA.
2270       *
2271       * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
2272       *    Use this function to filter a set of values. For example, by summing several DELTA
2273       *    functions you calculate the count of equal pairs. This function is also known as the
2274       * Kronecker Delta function.
2275       *
2276       *    Excel Function:
2277       *        DELTA(a[,b])
2278       *
2279       * @param float $a the first number
2280       * @param float $b The second number. If omitted, b is assumed to be zero.
2281       *
2282       * @return int
2283       */
2284      public static function DELTA($a, $b = 0)
2285      {
2286          $a = Functions::flattenSingleValue($a);
2287          $b = Functions::flattenSingleValue($b);
2288  
2289          return (int) ($a == $b);
2290      }
2291  
2292      /**
2293       * GESTEP.
2294       *
2295       *    Excel Function:
2296       *        GESTEP(number[,step])
2297       *
2298       *    Returns 1 if number >= step; returns 0 (zero) otherwise
2299       *    Use this function to filter a set of values. For example, by summing several GESTEP
2300       * functions you calculate the count of values that exceed a threshold.
2301       *
2302       * @param float $number the value to test against step
2303       * @param float $step The threshold value.
2304       *                                    If you omit a value for step, GESTEP uses zero.
2305       *
2306       * @return int
2307       */
2308      public static function GESTEP($number, $step = 0)
2309      {
2310          $number = Functions::flattenSingleValue($number);
2311          $step = Functions::flattenSingleValue($step);
2312  
2313          return (int) ($number >= $step);
2314      }
2315  
2316      //
2317      //    Private method to calculate the erf value
2318      //
2319      private static $twoSqrtPi = 1.128379167095512574;
2320  
2321      public static function erfVal($x)
2322      {
2323          if (abs($x) > 2.2) {
2324              return 1 - self::erfcVal($x);
2325          }
2326          $sum = $term = $x;
2327          $xsqr = ($x * $x);
2328          $j = 1;
2329          do {
2330              $term *= $xsqr / $j;
2331              $sum -= $term / (2 * $j + 1);
2332              ++$j;
2333              $term *= $xsqr / $j;
2334              $sum += $term / (2 * $j + 1);
2335              ++$j;
2336              if ($sum == 0.0) {
2337                  break;
2338              }
2339          } while (abs($term / $sum) > Functions::PRECISION);
2340  
2341          return self::$twoSqrtPi * $sum;
2342      }
2343  
2344      /**
2345       * Validate arguments passed to the bitwise functions.
2346       *
2347       * @param mixed $value
2348       *
2349       * @throws Exception
2350       *
2351       * @return int
2352       */
2353      private static function validateBitwiseArgument($value)
2354      {
2355          $value = Functions::flattenSingleValue($value);
2356  
2357          if (is_int($value)) {
2358              return $value;
2359          } elseif (is_numeric($value)) {
2360              if ($value == (int) ($value)) {
2361                  $value = (int) ($value);
2362                  if (($value > pow(2, 48) - 1) || ($value < 0)) {
2363                      throw new Exception(Functions::NAN());
2364                  }
2365  
2366                  return $value;
2367              }
2368  
2369              throw new Exception(Functions::NAN());
2370          }
2371  
2372          throw new Exception(Functions::VALUE());
2373      }
2374  
2375      /**
2376       * BITAND.
2377       *
2378       * Returns the bitwise AND of two integer values.
2379       *
2380       * Excel Function:
2381       *        BITAND(number1, number2)
2382       *
2383       * @category Engineering Functions
2384       *
2385       * @param int $number1
2386       * @param int $number2
2387       *
2388       * @return int|string
2389       */
2390      public static function BITAND($number1, $number2)
2391      {
2392          try {
2393              $number1 = self::validateBitwiseArgument($number1);
2394              $number2 = self::validateBitwiseArgument($number2);
2395          } catch (Exception $e) {
2396              return $e->getMessage();
2397          }
2398  
2399          return $number1 & $number2;
2400      }
2401  
2402      /**
2403       * BITOR.
2404       *
2405       * Returns the bitwise OR of two integer values.
2406       *
2407       * Excel Function:
2408       *        BITOR(number1, number2)
2409       *
2410       * @category Engineering Functions
2411       *
2412       * @param int $number1
2413       * @param int $number2
2414       *
2415       * @return int|string
2416       */
2417      public static function BITOR($number1, $number2)
2418      {
2419          try {
2420              $number1 = self::validateBitwiseArgument($number1);
2421              $number2 = self::validateBitwiseArgument($number2);
2422          } catch (Exception $e) {
2423              return $e->getMessage();
2424          }
2425  
2426          return $number1 | $number2;
2427      }
2428  
2429      /**
2430       * BITXOR.
2431       *
2432       * Returns the bitwise XOR of two integer values.
2433       *
2434       * Excel Function:
2435       *        BITXOR(number1, number2)
2436       *
2437       * @category Engineering Functions
2438       *
2439       * @param int $number1
2440       * @param int $number2
2441       *
2442       * @return int|string
2443       */
2444      public static function BITXOR($number1, $number2)
2445      {
2446          try {
2447              $number1 = self::validateBitwiseArgument($number1);
2448              $number2 = self::validateBitwiseArgument($number2);
2449          } catch (Exception $e) {
2450              return $e->getMessage();
2451          }
2452  
2453          return $number1 ^ $number2;
2454      }
2455  
2456      /**
2457       * BITLSHIFT.
2458       *
2459       * Returns the number value shifted left by shift_amount bits.
2460       *
2461       * Excel Function:
2462       *        BITLSHIFT(number, shift_amount)
2463       *
2464       * @category Engineering Functions
2465       *
2466       * @param int $number
2467       * @param int $shiftAmount
2468       *
2469       * @return int|string
2470       */
2471      public static function BITLSHIFT($number, $shiftAmount)
2472      {
2473          try {
2474              $number = self::validateBitwiseArgument($number);
2475          } catch (Exception $e) {
2476              return $e->getMessage();
2477          }
2478  
2479          $shiftAmount = Functions::flattenSingleValue($shiftAmount);
2480  
2481          $result = $number << $shiftAmount;
2482          if ($result > pow(2, 48) - 1) {
2483              return Functions::NAN();
2484          }
2485  
2486          return $result;
2487      }
2488  
2489      /**
2490       * BITRSHIFT.
2491       *
2492       * Returns the number value shifted right by shift_amount bits.
2493       *
2494       * Excel Function:
2495       *        BITRSHIFT(number, shift_amount)
2496       *
2497       * @category Engineering Functions
2498       *
2499       * @param int $number
2500       * @param int $shiftAmount
2501       *
2502       * @return int|string
2503       */
2504      public static function BITRSHIFT($number, $shiftAmount)
2505      {
2506          try {
2507              $number = self::validateBitwiseArgument($number);
2508          } catch (Exception $e) {
2509              return $e->getMessage();
2510          }
2511  
2512          $shiftAmount = Functions::flattenSingleValue($shiftAmount);
2513  
2514          return $number >> $shiftAmount;
2515      }
2516  
2517      /**
2518       * ERF.
2519       *
2520       * Returns the error function integrated between the lower and upper bound arguments.
2521       *
2522       *    Note: In Excel 2007 or earlier, if you input a negative value for the upper or lower bound arguments,
2523       *            the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
2524       *            improved, so that it can now calculate the function for both positive and negative ranges.
2525       *            PhpSpreadsheet follows Excel 2010 behaviour, and accepts negative arguments.
2526       *
2527       *    Excel Function:
2528       *        ERF(lower[,upper])
2529       *
2530       * @param float $lower lower bound for integrating ERF
2531       * @param float $upper upper bound for integrating ERF.
2532       *                                If omitted, ERF integrates between zero and lower_limit
2533       *
2534       * @return float|string
2535       */
2536      public static function ERF($lower, $upper = null)
2537      {
2538          $lower = Functions::flattenSingleValue($lower);
2539          $upper = Functions::flattenSingleValue($upper);
2540  
2541          if (is_numeric($lower)) {
2542              if ($upper === null) {
2543                  return self::erfVal($lower);
2544              }
2545              if (is_numeric($upper)) {
2546                  return self::erfVal($upper) - self::erfVal($lower);
2547              }
2548          }
2549  
2550          return Functions::VALUE();
2551      }
2552  
2553      /**
2554       * ERFPRECISE.
2555       *
2556       * Returns the error function integrated between the lower and upper bound arguments.
2557       *
2558       *    Excel Function:
2559       *        ERF.PRECISE(limit)
2560       *
2561       * @param float $limit bound for integrating ERF
2562       *
2563       * @return float|string
2564       */
2565      public static function ERFPRECISE($limit)
2566      {
2567          $limit = Functions::flattenSingleValue($limit);
2568  
2569          return self::ERF($limit);
2570      }
2571  
2572      //
2573      //    Private method to calculate the erfc value
2574      //
2575      private static $oneSqrtPi = 0.564189583547756287;
2576  
2577      private static function erfcVal($x)
2578      {
2579          if (abs($x) < 2.2) {
2580              return 1 - self::erfVal($x);
2581          }
2582          if ($x < 0) {
2583              return 2 - self::ERFC(-$x);
2584          }
2585          $a = $n = 1;
2586          $b = $c = $x;
2587          $d = ($x * $x) + 0.5;
2588          $q1 = $q2 = $b / $d;
2589          $t = 0;
2590          do {
2591              $t = $a * $n + $b * $x;
2592              $a = $b;
2593              $b = $t;
2594              $t = $c * $n + $d * $x;
2595              $c = $d;
2596              $d = $t;
2597              $n += 0.5;
2598              $q1 = $q2;
2599              $q2 = $b / $d;
2600          } while ((abs($q1 - $q2) / $q2) > Functions::PRECISION);
2601  
2602          return self::$oneSqrtPi * exp(-$x * $x) * $q2;
2603      }
2604  
2605      /**
2606       * ERFC.
2607       *
2608       *    Returns the complementary ERF function integrated between x and infinity
2609       *
2610       *    Note: In Excel 2007 or earlier, if you input a negative value for the lower bound argument,
2611       *        the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
2612       *        improved, so that it can now calculate the function for both positive and negative x values.
2613       *            PhpSpreadsheet follows Excel 2010 behaviour, and accepts nagative arguments.
2614       *
2615       *    Excel Function:
2616       *        ERFC(x)
2617       *
2618       * @param float $x The lower bound for integrating ERFC
2619       *
2620       * @return float|string
2621       */
2622      public static function ERFC($x)
2623      {
2624          $x = Functions::flattenSingleValue($x);
2625  
2626          if (is_numeric($x)) {
2627              return self::erfcVal($x);
2628          }
2629  
2630          return Functions::VALUE();
2631      }
2632  
2633      /**
2634       *    getConversionGroups
2635       * Returns a list of the different conversion groups for UOM conversions.
2636       *
2637       * @return array
2638       */
2639      public static function getConversionGroups()
2640      {
2641          $conversionGroups = [];
2642          foreach (self::$conversionUnits as $conversionUnit) {
2643              $conversionGroups[] = $conversionUnit['Group'];
2644          }
2645  
2646          return array_merge(array_unique($conversionGroups));
2647      }
2648  
2649      /**
2650       *    getConversionGroupUnits
2651       * Returns an array of units of measure, for a specified conversion group, or for all groups.
2652       *
2653       * @param string $group The group whose units of measure you want to retrieve
2654       *
2655       * @return array
2656       */
2657      public static function getConversionGroupUnits($group = null)
2658      {
2659          $conversionGroups = [];
2660          foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
2661              if (($group === null) || ($conversionGroup['Group'] == $group)) {
2662                  $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
2663              }
2664          }
2665  
2666          return $conversionGroups;
2667      }
2668  
2669      /**
2670       * getConversionGroupUnitDetails.
2671       *
2672       * @param string $group The group whose units of measure you want to retrieve
2673       *
2674       * @return array
2675       */
2676      public static function getConversionGroupUnitDetails($group = null)
2677      {
2678          $conversionGroups = [];
2679          foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
2680              if (($group === null) || ($conversionGroup['Group'] == $group)) {
2681                  $conversionGroups[$conversionGroup['Group']][] = [
2682                      'unit' => $conversionUnit,
2683                      'description' => $conversionGroup['Unit Name'],
2684                  ];
2685              }
2686          }
2687  
2688          return $conversionGroups;
2689      }
2690  
2691      /**
2692       *    getConversionMultipliers
2693       * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
2694       *
2695       * @return array of mixed
2696       */
2697      public static function getConversionMultipliers()
2698      {
2699          return self::$conversionMultipliers;
2700      }
2701  
2702      /**
2703       * CONVERTUOM.
2704       *
2705       * Converts a number from one measurement system to another.
2706       *    For example, CONVERT can translate a table of distances in miles to a table of distances
2707       * in kilometers.
2708       *
2709       *    Excel Function:
2710       *        CONVERT(value,fromUOM,toUOM)
2711       *
2712       * @param float $value the value in fromUOM to convert
2713       * @param string $fromUOM the units for value
2714       * @param string $toUOM the units for the result
2715       *
2716       * @return float|string
2717       */
2718      public static function CONVERTUOM($value, $fromUOM, $toUOM)
2719      {
2720          $value = Functions::flattenSingleValue($value);
2721          $fromUOM = Functions::flattenSingleValue($fromUOM);
2722          $toUOM = Functions::flattenSingleValue($toUOM);
2723  
2724          if (!is_numeric($value)) {
2725              return Functions::VALUE();
2726          }
2727          $fromMultiplier = 1.0;
2728          if (isset(self::$conversionUnits[$fromUOM])) {
2729              $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
2730          } else {
2731              $fromMultiplier = substr($fromUOM, 0, 1);
2732              $fromUOM = substr($fromUOM, 1);
2733              if (isset(self::$conversionMultipliers[$fromMultiplier])) {
2734                  $fromMultiplier = self::$conversionMultipliers[$fromMultiplier]['multiplier'];
2735              } else {
2736                  return Functions::NA();
2737              }
2738              if ((isset(self::$conversionUnits[$fromUOM])) && (self::$conversionUnits[$fromUOM]['AllowPrefix'])) {
2739                  $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
2740              } else {
2741                  return Functions::NA();
2742              }
2743          }
2744          $value *= $fromMultiplier;
2745  
2746          $toMultiplier = 1.0;
2747          if (isset(self::$conversionUnits[$toUOM])) {
2748              $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
2749          } else {
2750              $toMultiplier = substr($toUOM, 0, 1);
2751              $toUOM = substr($toUOM, 1);
2752              if (isset(self::$conversionMultipliers[$toMultiplier])) {
2753                  $toMultiplier = self::$conversionMultipliers[$toMultiplier]['multiplier'];
2754              } else {
2755                  return Functions::NA();
2756              }
2757              if ((isset(self::$conversionUnits[$toUOM])) && (self::$conversionUnits[$toUOM]['AllowPrefix'])) {
2758                  $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
2759              } else {
2760                  return Functions::NA();
2761              }
2762          }
2763          if ($unitGroup1 != $unitGroup2) {
2764              return Functions::NA();
2765          }
2766  
2767          if (($fromUOM == $toUOM) && ($fromMultiplier == $toMultiplier)) {
2768              //    We've already factored $fromMultiplier into the value, so we need
2769              //        to reverse it again
2770              return $value / $fromMultiplier;
2771          } elseif ($unitGroup1 == 'Temperature') {
2772              if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
2773                  if (($toUOM == 'F') || ($toUOM == 'fah')) {
2774                      return $value;
2775                  }
2776                  $value = (($value - 32) / 1.8);
2777                  if (($toUOM == 'K') || ($toUOM == 'kel')) {
2778                      $value += 273.15;
2779                  }
2780  
2781                  return $value;
2782              } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) &&
2783                  (($toUOM == 'K') || ($toUOM == 'kel'))
2784              ) {
2785                  return $value;
2786              } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
2787                  (($toUOM == 'C') || ($toUOM == 'cel'))
2788              ) {
2789                  return $value;
2790              }
2791              if (($toUOM == 'F') || ($toUOM == 'fah')) {
2792                  if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
2793                      $value -= 273.15;
2794                  }
2795  
2796                  return ($value * 1.8) + 32;
2797              }
2798              if (($toUOM == 'C') || ($toUOM == 'cel')) {
2799                  return $value - 273.15;
2800              }
2801  
2802              return $value + 273.15;
2803          }
2804  
2805          return ($value * self::$unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
2806      }
2807  }