├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.md ├── _config.yml ├── composer.json ├── examples ├── matrix.php └── vector.php ├── phpunit.xml ├── src ├── Generator │ ├── Fibonacci.php │ └── Generator.php ├── Indexing │ ├── Indexer.php │ └── StringIndexer.php ├── Operator │ ├── Bitwise.php │ └── Operators.php ├── Printing │ └── Printer.php ├── Random │ └── Random.php ├── Shaping │ └── Reshaper.php ├── Slicing │ └── Slice.php ├── Statistics │ └── Statistics.php ├── np.php └── np_array.php └── tests ├── ComparationsTest.php ├── GeneralTest.php ├── GeneratorTest.php ├── IndexingTest.php ├── OperatorsTest.php ├── PrinterTest.php ├── RandomTest.php ├── SetItemsTest.php ├── ShapingTest.php ├── SliceTest.php └── StatisticsTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | .DS_Store 4 | .php_cs 5 | .php_cs.cache 6 | vendor 7 | composer.phar 8 | composer.lock 9 | *.sublime* 10 | clover.xml 11 | /tmp 12 | play.php -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | - CC_TEST_REPORTER_ID=936e0a1ab71092997c76c2dda7a2157e610ef1ba75d22869fce0556e9255f658 4 | 5 | language: php 6 | php : 7 | - 5.5 8 | - 5.6 9 | - 7.0 10 | - 7.1 11 | - 7.2 12 | - 7.3 13 | 14 | before_script: 15 | - composer self-update 16 | - composer install --prefer-source --no-interaction 17 | 18 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 19 | - chmod +x ./cc-test-reporter 20 | - ./cc-test-reporter before-build 21 | 22 | script: 23 | - vendor/bin/phpunit --coverage-clover=coverage.xml 24 | 25 | after_success: 26 | - bash <(curl -s https://codecov.io/bash) 27 | 28 | - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT 29 | 30 | notifications: 31 | email: false 32 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem "rspec" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dmitriy Apollonin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Numphp 2 | 3 | [![Build Status](https://travis-ci.org/apollonin/numphp.svg?branch=master)](https://travis-ci.org/apollonin/numphp) 4 | [![Latest Stable Version](https://poser.pugx.org/apollonin/numphp/v/stable)](https://packagist.org/packages/apollonin/numphp) 5 | [![Total Downloads](https://poser.pugx.org/apollonin/numphp/downloads)](https://packagist.org/packages/apollonin/numphp) 6 | [![License](https://poser.pugx.org/apollonin/numphp/license)](https://packagist.org/packages/apollonin/numphp) 7 | [![codecov](https://codecov.io/gh/apollonin/numphp/branch/master/graph/badge.svg)](https://codecov.io/gh/apollonin/numphp) 8 | [![Maintainability](https://api.codeclimate.com/v1/badges/9cda6d0e7e7967900ff2/maintainability)](https://codeclimate.com/github/apollonin/numphp/maintainability) 9 | [![Test Coverage](https://api.codeclimate.com/v1/badges/9cda6d0e7e7967900ff2/test_coverage)](https://codeclimate.com/github/apollonin/numphp/test_coverage) 10 | 11 | Numphp is a library for number manipulations. If you have an array of numbers, numphp gives you an ability to perform a wide range of useful operations. 12 | 13 | Contributions are highly appreciated. 14 | 15 | ## Installation 16 | 17 | ### With composer 18 | 19 | ``` 20 | composer require apollonin/numphp 21 | ``` 22 | 23 | 24 | ## Available features 25 | 26 | **Arrays** 27 | 28 | * get item by index 29 | * get items by array of indexes 30 | * get items by condition 31 | * eq, gt, gte, lt, lte, neq - equals, greater than, and so on 32 | * get items by complex conditions 33 | * b_and, b_or - bitwise AND and OR 34 | * set items values according to conditions, indexes or slices 35 | * apply math operations to whole array 36 | * mul, div, add, sub, pow, mod 37 | * get slice of array 38 | * get statistical values from array 39 | * count, max, mean, median, min, sum 40 | * describe - special method that displays all above values 41 | * Get dimensional data 42 | * shape 43 | * dimension 44 | * Concatenate arrays 45 | 46 | np_array also has classical array behaviour. So you are able to iterate through it as usual. 47 | 48 | **Matrix** 49 | 50 | Matrix is a special case of arrays. At the moment, we only support 2d matrices. Full support for n-dimensional matrices is on the way. 51 | 52 | You can perform all the same operations and comparisons as with arrays. Refer to Matrix section below in usage examples. 53 | 54 | **Dimensional Manipulation** 55 | 56 | You are able to change dimensions for existed array or matrix. Use `flatten` or `reshape` methods. 57 | 58 | **Random Module** 59 | 60 | Numphp also provides convenient ways to generate new np_arrays and populate them with random values. Available methods are 61 | 62 | * rand 63 | * randint 64 | 65 | If `size` parameter is given, returns np_array with appropriate elements. Otherwise, it returns single random value. 66 | 67 | **Generators** 68 | 69 | For quick stub array creation you may use these convenient predefined methods 70 | 71 | * ones - creates array full of 1 72 | * zeros - creates array full of 0 73 | * full- creates array full of provided fill_value 74 | * arange - creates evenly spaced values within a given interval. 75 | * fib - creates Fibonacci numbers 76 | * formula - returns sequence of numbers, based on provided formula 77 | 78 | 79 | ## Usage Examples 80 | 81 | ### Indexing 82 | 83 | **create new array** 84 | ```php 85 | $list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 86 | ``` 87 | 88 | **Get items by their indexes** 89 | 90 | ```php 91 | $result = $list[[2,3]]; 92 | 93 | // result 94 | [2, 3] 95 | ``` 96 | 97 | To get item as single value - pass index as single value as well 98 | 99 | ```php 100 | $result = $list[1]; 101 | 102 | // result 103 | 1 104 | ``` 105 | 106 | **Get items by condition** 107 | 108 | ```php 109 | $result = $list[$list->gt(5)]; 110 | 111 | // result 112 | [6, 7, 8, 9] 113 | ``` 114 | 115 | You may also access index by string representations of comparison. 116 | 117 | ```php 118 | // gives the same result as above 119 | $result = $list[$list['> 5']]; 120 | ``` 121 | 122 | *Important note about conditional indexing: conditional operator returns masking array:* 123 | 124 | 125 | 126 | ```php 127 | $mask = $list->gt(5); 128 | 129 | // mask 130 | [false, false, false, false, false, false, true, true, true, true] 131 | 132 | // and then 133 | $result = $list[$mask]; 134 | 135 | // result 136 | [6, 7, 8, 9] 137 | ``` 138 | 139 | You also can pass another array as an argument. In this case the comparison will be applied for each element respectively. 140 | 141 | ```php 142 | $result = $list[$list->gt([5, 6, 7, 8, 9, 3, 4, 5, 6, 7])]; 143 | 144 | // result 145 | [6, 7, 8, 9] 146 | ``` 147 | 148 | 149 | **Get items by conditions** 150 | 151 | *b_and* - "bitwise" and 152 | 153 | ```php 154 | $resuilt = $list[Bitwise::b_and($list->gte(5), $list->lt(8))]; 155 | 156 | // result 157 | [5, 6, 7] 158 | ``` 159 | 160 | **Array-like behaviour** 161 | 162 | You may also iterate your np_array object as usual 163 | 164 | ```php 165 | foreach ($list as $item) { 166 | echo $item . " "; 167 | } 168 | 169 | // output 170 | 0 1 2 3 4 5 6 7 8 9 171 | ``` 172 | 173 | 174 | ### Slicing 175 | 176 | You may get slices of your np_array in a very convenient way. Just pass string formatted like `start:[stop][:step]` as index and you'll get result. 177 | 178 | ```php 179 | $result = $list['1:5']; 180 | 181 | //result 182 | [1, 2, 3, 4] 183 | 184 | 185 | $result = $list['1:5:2']; 186 | 187 | //result 188 | [1, 3] 189 | ``` 190 | 191 | You can even skip `stop` and `step` values, which means: get all items from `start` to the end of array. 192 | 193 | ```php 194 | $result = $list['1:']; 195 | 196 | //result 197 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 198 | ``` 199 | 200 | You may even skip `start` value; it will be considered as 0 in this case 201 | 202 | ```php 203 | $result = $list[':']; 204 | 205 | //result 206 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 207 | ``` 208 | 209 | Negative `start` or `stop` means indexes count from the end of array 210 | 211 | ```php 212 | $result = $list['-7:6']; 213 | 214 | //result 215 | [3, 4, 5] 216 | ``` 217 | 218 | 219 | ### Set item values 220 | 221 | **Set items by indexes** 222 | 223 | ```php 224 | $result = clone($list); 225 | $result[[2,3]] = 999; 226 | 227 | // result 228 | [0, 1, 999, 999, 4, 5, 6, 7, 8, 9] 229 | ``` 230 | 231 | **Set items by conditions** 232 | 233 | ```php 234 | $result = clone($list); 235 | $result[$result->gte(5)] = 999; 236 | 237 | // result 238 | [0, 1, 2, 3, 4, 999, 999, 999, 999, 999] 239 | ``` 240 | 241 | **Set items by slice** 242 | 243 | ```php 244 | $result = clone($list); 245 | $result['1:3'] = 999; 246 | 247 | // result 248 | [0, 999, 999, 3, 4, 5, 6, 7, 8, 9] 249 | ``` 250 | 251 | **Adding new items** 252 | 253 | Of course, you may add new items as usual 254 | 255 | ```php 256 | $result = clone($list); 257 | $result[] = 999; 258 | 259 | // result 260 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 999] 261 | ``` 262 | 263 | ### Math operations 264 | 265 | You are able to apply certain math operations to the whole array. It will apply to each element. 266 | 267 | ```php 268 | $result = $list->add(100); 269 | 270 | // result 271 | [100, 101, 102, 103, 104, 105, 106, 107, 108, 109] 272 | ``` 273 | 274 | You may also perform math operation under two np_arrays 275 | 276 | ```php 277 | $result = $list->add(new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) 278 | 279 | //result 280 | [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 281 | ``` 282 | 283 | Or event np_array and normal array! 284 | 285 | ```php 286 | $result = $list->add([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 287 | 288 | //result 289 | [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 290 | ``` 291 | 292 | 293 | 294 | ### Random module 295 | 296 | **Create array with random floats** 297 | 298 | ```php 299 | use numphp\Random\Random; 300 | 301 | $result = Random::rand(5) 302 | 303 | // result 304 | [0.64488127438579, 0.21702189986455, 0.96931800524207, 0.78197341448719, 0.89214772632911] 305 | 306 | ``` 307 | 308 | **Array with random integers** 309 | 310 | ```php 311 | use numphp\Random\Random; 312 | 313 | $result = Random::randint(5, 15, 10); 314 | 315 | // result 316 | [13, 9, 12, 14, 6, 15, 8, 9, 5, 13] 317 | ``` 318 | 319 | 320 | ### Generators module 321 | 322 | **create array full of zeros, ones or fill_value** 323 | 324 | ```php 325 | use numphp\Generator\Generator; 326 | 327 | $result = Generator::zeros(5); 328 | 329 | //result 330 | [0, 0, 0, 0, 0] 331 | 332 | 333 | $result = Generator::ones(5); 334 | 335 | //result 336 | [1, 1, 1, 1, 1] 337 | 338 | $result = Generator::full(5, 999); 339 | 340 | //result 341 | [999, 999, 999, 999, 999] 342 | ``` 343 | 344 | **Create array within a range and given interval** 345 | 346 | ```php 347 | use numphp\Generator\Generator; 348 | 349 | $result = Generator::arange(1, 15, 2); 350 | 351 | //result 352 | [1, 3, 5, 7, 9, 11, 13] 353 | ``` 354 | 355 | **Generate N [Fibonacci](https://en.wikipedia.org/wiki/Fibonacci_number) numbers** 356 | 357 | ```php 358 | use numphp\Generator\Generator; 359 | 360 | $result = Generator::fib(6); 361 | 362 | //result 363 | [1, 1, 2, 3, 5, 8] 364 | ``` 365 | 366 | 367 | **Generate numbers according to formula** 368 | 369 | Provide [callable](http://php.net/manual/en/language.types.callable.php) as a first argument. It must return value, that will be used in sequence. 370 | 371 | ```php 372 | use numphp\Generator\Generator; 373 | 374 | $result = Generator::formula(function($n){return 2*$n+1;}, 1, 5); 375 | 376 | //result 377 | [3, 5, 7, 9] 378 | ``` 379 | 380 | **Generate matrix with given diagonal** 381 | 382 | ``` 383 | $matrix = Generator::diagonal([5, 3, 1]); 384 | 385 | // matrix 386 | [[5, 0, 0], 387 | [0, 3, 0], 388 | [0, 0, 1]] 389 | ``` 390 | 391 | 392 | ### Matrix operations 393 | 394 | Generally the syntax and features are the same as for arrays 395 | 396 | **Creation** 397 | 398 | ``` 399 | $matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]); 400 | 401 | // matrix 402 | [[ 0, 1, 2, 3], 403 | [ 4, 5, 6, 7], 404 | [ 8, 9, 10, 11]] 405 | ``` 406 | 407 | **Indexing** 408 | 409 | Indexing is done in respect to X-axis (rows) 410 | 411 | ``` 412 | $result = $matrix[0]; 413 | 414 | //result 415 | [0, 1, 2, 3] 416 | ``` 417 | 418 | **Slicing** 419 | 420 | ``` 421 | $result = $matrix['1:3']; 422 | 423 | //result 424 | [[ 4, 5, 6, 7], 425 | [ 8, 9, 10, 11]] 426 | ``` 427 | 428 | **Comparisons** 429 | 430 | ``` 431 | $result = $matrix[$matrix->gt(5)]; 432 | 433 | //result 434 | [6, 7, 8, 9, 10, 11] 435 | ``` 436 | 437 | Keep in mind 'masking' feature 438 | 439 | ``` 440 | $mask = $matrix->gt(5); 441 | 442 | //mask 443 | [[false, false, false, false], 444 | [false, false, true, true], 445 | [true, true, true, true]] 446 | ``` 447 | 448 | **Changing values** 449 | 450 | ``` 451 | $matrix[$matrix->gte(5)] = 999; 452 | 453 | //matrix 454 | [[ 0, 1, 2, 3], 455 | [ 4, 999, 999, 999], 456 | [999, 999, 999, 999]] 457 | ``` 458 | 459 | **Math operations** 460 | 461 | ``` 462 | $result = $matrix->mul(5); 463 | 464 | //result 465 | [[ 0, 5, 10, 15], 466 | [20, 25, 30, 35], 467 | [40, 45, 50, 55]] 468 | ``` 469 | 470 | **Get shape of matrix** 471 | 472 | ``` 473 | $shape = $matrix->shape; 474 | 475 | //shape: [rows, cols] 476 | [3, 4] 477 | ``` 478 | 479 | And if you just need count of dimensions 480 | 481 | ``` 482 | $dimensions = $matrix->dimensions; 483 | 484 | //dimensions 485 | 2 486 | ``` 487 | 488 | 489 | **Diagonal** 490 | 491 | ``` 492 | $result = $matrix->diagonal(); 493 | 494 | //result 495 | [0, 5, 10] 496 | 497 | ``` 498 | 499 | or you can set offset for diagonal 500 | 501 | ``` 502 | $result = $matrix->diagonal(2); 503 | 504 | //result 505 | [2, 7] 506 | 507 | ``` 508 | 509 | ## Changing dimensions 510 | 511 | **Flatten matrix** 512 | 513 | You can get 1-D array from matrix. 514 | 515 | ``` 516 | $result = $matrix->flatten(); 517 | 518 | //result 519 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 520 | ``` 521 | 522 | **Reshaping** 523 | 524 | You also can change current shape of matrix to any desired. 525 | 526 | ``` 527 | $result = $matrix->reshape([6, 2]); 528 | 529 | //result 530 | [[ 0, 1], 531 | [ 2, 3], 532 | [ 4, 5], 533 | [ 6, 7], 534 | [ 8, 9], 535 | [10, 11]] 536 | ``` 537 | 538 | 539 | ## Concatenation 540 | 541 | **concatenate arrays** 542 | 543 | You can concatenate two or more arrays into one. Logic is similar to [array_merge](http://php.net/manual/en/function.array-merge.php) native php method 544 | 545 | ``` 546 | $l1 = Generator::arange(1, 5); 547 | $l2 = Generator::arange(5, 8); 548 | $l3 = Generator::arange(8, 10); 549 | 550 | $result = np::concatenate($l1, $l2, $l3) 551 | 552 | //result 553 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 554 | ``` 555 | 556 | **concatenate matrixes** 557 | 558 | The same logic can be applied to matrixes 559 | 560 | ``` 561 | $m2 = Generator::ones([1, 4]); 562 | $result = np::concatenate($matrix, $m2) 563 | 564 | //result 565 | [[ 0, 1, 2, 3], 566 | [ 4, 5, 6, 7], 567 | [ 8, 9, 10, 11], 568 | [ 1, 1, 1, 1]] 569 | 570 | ``` -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollonin/numphp", 3 | "type": "library", 4 | "description": "Library for numbers manipulation.", 5 | "version": "1.9.0", 6 | "keywords": [ 7 | "numbers", 8 | "array", 9 | "filter" 10 | ], 11 | "license": "MIT", 12 | "require": { 13 | "php": ">=5.5.0" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "numphp\\": "src/" 21 | } 22 | }, 23 | "autoload-dev": { 24 | "psr-4": { 25 | "Tests\\": "tests/" 26 | } 27 | }, 28 | "scripts": { 29 | "test": "./vendor/bin/phpunit" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/matrix.php: -------------------------------------------------------------------------------- 1 | shape) . ')' . "\n\n"; 14 | 15 | /** 16 | * Indexing 17 | */ 18 | 19 | echo 'matrix[0]: ' . "\n" . $matrix[0] . "\n\n"; 20 | 21 | echo 'matrix["1:3"]: ' . "\n" . $matrix['1:3'] . "\n\n"; 22 | 23 | echo 'mask > 5: ' . "\n" . $matrix->gt(5) . "\n\n"; 24 | 25 | echo 'matrix > 5: ' . "\n" . $matrix[$matrix->gt(5)] . "\n\n"; 26 | 27 | 28 | /** 29 | * Set items 30 | */ 31 | 32 | $res5 = clone($matrix); 33 | $res5[[[3], [3], [3]]] = 999; 34 | echo 'set whole 4th column to 999: ' . "\n" . $res5 . "\n\n"; 35 | 36 | $res6 = clone($matrix); 37 | $res6[$res6->gte(5)] = 999; 38 | echo 'set elements >= 5 to 999: ' . "\n" . $res6 . "\n\n"; 39 | 40 | /** Generator **/ 41 | 42 | echo 'zeros array with size of [5, 2]: ' . "\n" . Generator::zeros([5, 2]) . "\n\n"; 43 | echo 'ones array with size of [5, 3]: ' . "\n" . Generator::ones([5, 3]) . "\n\n"; 44 | 45 | echo 'arange with reshaping to [2, 7]: ' . "\n" . Generator::arange(1, 15)->reshape([2, 7]) . "\n\n"; 46 | 47 | echo 'matrix with [5, 3, 1] diagonal: ' . "\n" . Generator::diagonal([5, 3, 1]) . "\n\n"; 48 | 49 | /** 50 | * Math operations 51 | */ 52 | 53 | echo 'multiply to 5: ' . "\n" . $matrix->mul(5) . "\n\n"; 54 | echo 'add [999, 999, 999, 999]: ' . "\n" . $matrix->add([999, 999, 999, 999]) . "\n\n"; 55 | echo 'add matrix of ones: ' . "\n" . $matrix->add(Generator::ones([3, 4])) . "\n\n"; 56 | 57 | /** Statistics **/ 58 | 59 | echo 'max element is: ' . "\n" . $matrix->max() . "\n\n"; 60 | 61 | echo 'describe ' . print_r($matrix->describe(), true) . "\n\n"; 62 | 63 | 64 | /** 65 | * Shaping 66 | */ 67 | 68 | echo 'flatten: ' . "\n" . $matrix->flatten() . "\n\n"; 69 | 70 | echo 'reshape to [6, 2]: ' . "\n" . $matrix->reshape([6, 2]) . "\n\n"; 71 | 72 | echo 'diagonal is: ' . "\n" . $matrix->diagonal() . "\n\n"; 73 | 74 | $m2 = Generator::ones([1, 4]); 75 | echo 'concatenate: ' . "\n" . $matrix . "\n" . ' and ' . "\n" . $m2 . ":\n" . np::concatenate($matrix, $m2) . "\n\n"; 76 | -------------------------------------------------------------------------------- /examples/vector.php: -------------------------------------------------------------------------------- 1 | shape) . ')' . "\n\n"; 23 | 24 | /** 25 | * Indexing 26 | */ 27 | 28 | echo 'mask >= 5' . "\n" . $list->gte(5) . "\n\n"; 29 | echo 'mask >= 5 (explicit)' . "\n" . $list->mask('> 5') . "\n\n"; 30 | 31 | echo 'list >= 5' . "\n" . $list[$list->gte(5)] . "\n\n"; 32 | 33 | 34 | $res1 = $list[Bitwise::b_and($list->gte(5), $list->lt(8))]; 35 | echo '>= 5 and < 8: ' . "\n" . $res1 . "\n\n"; 36 | 37 | echo 'previous > 5: ' . "\n" . $res1[$res1->gt(5)] . "\n\n"; 38 | 39 | echo 'index 2: ' . "\n" . $list[2] . "\n\n"; 40 | 41 | echo 'equal 5: ' . "\n" . $list[$list->eq(5)] . "\n\n"; 42 | 43 | echo 'short indexing < 5: ' . "\n" . $list[$list['< 5']] . "\n\n"; 44 | 45 | /** 46 | * Set items 47 | */ 48 | 49 | $res5 = clone($list); 50 | $res5[[2,3]] = 999; 51 | echo 'set indexes 2 and 3 to 999: ' . "\n" . $res5 . "\n\n"; 52 | 53 | $res6 = clone($list); 54 | $res6[$res6->gte(5)] = 999; 55 | echo 'set elements >= 5 to 999: ' . "\n" . $res6 . "\n\n"; 56 | 57 | 58 | /** 59 | * Math operations 60 | */ 61 | 62 | echo 'multiply all to 5: ' . "\n" . $list->mul(5) . "\n\n"; 63 | 64 | echo 'power > 5 by 2: ' . "\n" . $list[$list->gt(5)]->pow(2) . "\n\n"; 65 | 66 | echo 'add 5' . "\n" . $list->add(5) . "\n\n"; 67 | echo 'add vector [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' . "\n" . ($list->add(Generator::arange(10))) . "\n\n"; 68 | 69 | 70 | 71 | /** 72 | * Random 73 | **/ 74 | 75 | echo 'random count 0: ' . "\n" . Random::rand() . "\n\n"; 76 | 77 | echo 'random count 5: ' . "\n" . Random::rand(5) . "\n\n"; 78 | 79 | echo 'random 10 ints within range 5-15: ' . "\n" . Random::randint(5, 15, 10) . "\n\n"; 80 | 81 | 82 | /** Generator **/ 83 | 84 | echo 'zeros array with size of 5: ' . "\n" . Generator::zeros(5) . "\n\n"; 85 | echo 'ones array with size of 5: ' . "\n" . Generator::ones(5) . "\n\n"; 86 | 87 | echo 'arange: ' . "\n" . Generator::arange(1, 15) . "\n\n"; 88 | 89 | echo 'formula 2n+1 from 1 to 5' . "\n" . Generator::formula(function($n){return 2*$n+1;}, 1, 5) . "\n\n"; 90 | 91 | 92 | /** Slicing **/ 93 | 94 | echo 'slicing [1:5]: ' . "\n" . $list['1:5'] . "\n\n"; 95 | echo 'slicing [1:5:2]: ' . "\n" . $list['1:5:2'] . "\n\n"; 96 | echo 'slicing [1:]: ' . "\n" . $list['1:'] . "\n\n"; 97 | 98 | //negative 99 | echo 'slicing [-7:6]: ' . "\n" . $list['-7:6'] . "\n\n"; 100 | 101 | /** Statistics **/ 102 | 103 | echo 'sum is: ' . "\n" . $list->sum() . "\n\n"; 104 | echo 'mean is: ' . "\n" . $list->mean() . "\n\n"; 105 | 106 | echo 'describe ' . print_r($list->describe(), true) . "\n\n"; 107 | 108 | /** 109 | * Shaping 110 | */ 111 | 112 | echo 'flatten: ' . "\n" . $list->flatten() . "\n\n"; 113 | 114 | echo 'reshape to [2, 6]: ' . "\n" . $list->reshape([2, 5]) . "\n\n"; 115 | 116 | 117 | /** 118 | * Concatenations 119 | */ 120 | 121 | $l1 = Generator::arange(1, 5); 122 | $l2 = Generator::arange(5, 8); 123 | $l3 = Generator::arange(8, 10); 124 | 125 | echo 'concatenate: ' . $l1 . ', ' . $l2 . ' and ' . $l3 . "\n" . np::concatenate($l1, $l2, $l3) . "\n\n"; 126 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests 15 | 16 | 17 | 18 | 19 | ./src 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Generator/Fibonacci.php: -------------------------------------------------------------------------------- 1 | current(); 91 | $generator->next(); 92 | } 93 | 94 | return new np_array($result); 95 | } 96 | 97 | /** 98 | * Generates sequence of numbers according to passed formula 99 | * @param Callable $formula number generator rule. Should return result value 100 | * @param int $start 101 | * @param int $stop 102 | * @param int $step 103 | * @return np_array 104 | */ 105 | public static function formula(Callable $formula, $start, $stop=0, $step=1) 106 | { 107 | if (!$stop) 108 | { 109 | $stop = $start; 110 | $start = 0; 111 | } 112 | 113 | $result = []; 114 | 115 | for ($i=$start; $i < $stop; $i+= $step) 116 | { 117 | $result[] = $formula($i); 118 | } 119 | 120 | return new np_array($result); 121 | } 122 | 123 | public static function diagonal($vector) 124 | { 125 | if (is_array($vector)) 126 | $vector = new np_array($vector); 127 | 128 | if ($vector->dimensions > 1) 129 | throw new \Exception("Except only 1-D array"); 130 | 131 | $size = count($vector); 132 | $result = static::zeros([$size, $size]); 133 | 134 | for ($i=0; $i < $size; $i++) 135 | { 136 | $result[$i][$i] = $vector[$i]; 137 | } 138 | 139 | return $result; 140 | 141 | } 142 | } 143 | 144 | -------------------------------------------------------------------------------- /src/Indexing/Indexer.php: -------------------------------------------------------------------------------- 1 | getIndexes($offset) as $level => $offsets) 20 | { 21 | if (is_array($offsets)) 22 | { 23 | foreach ($offsets as $index) 24 | $this[$level][$index] = $value; 25 | } 26 | else 27 | { 28 | parent::offsetSet($offsets, $value); 29 | } 30 | } 31 | 32 | return; 33 | } 34 | 35 | // slicing 36 | if(Slice::isValidFormat($offset)) 37 | { 38 | list($start, $stop, $step) = Slice::getOffsets($this, $offset); 39 | 40 | for ($i=$start; $i < $stop; $i += $step) 41 | parent::offsetSet($i, $value); 42 | 43 | return; 44 | } 45 | 46 | //use base offsetSet 47 | parent::offsetSet($offset, $value); 48 | } 49 | 50 | public function offsetGet($offset) 51 | { 52 | // multiply indexes or masking 53 | if (is_array($offset) || $offset instanceof $this) 54 | { 55 | $result = []; 56 | 57 | foreach ($this->getIndexes($offset) as $level => $offsets) 58 | { 59 | if (is_array($offsets)) 60 | { 61 | foreach ($offsets as $index) 62 | $result[] = $this[$level][$index]; 63 | } 64 | else 65 | { 66 | $result[] = $this[$offsets]; 67 | } 68 | } 69 | 70 | return new self($result); 71 | } 72 | 73 | // slicing 74 | if(Slice::isValidFormat($offset)) 75 | return Slice::slice($this, $offset); 76 | 77 | // base offsetGet 78 | if(is_numeric($offset)) 79 | return parent::offsetGet($offset); 80 | 81 | 82 | // use string indexer 83 | return $this->getStringOffset($offset); 84 | } 85 | 86 | public function mask($offset) 87 | { 88 | return $this->getStringOffset($offset); 89 | } 90 | 91 | public function getArrayCopy() 92 | { 93 | $result = []; 94 | 95 | foreach ($this as $item) 96 | { 97 | if ($item instanceof $this) 98 | $result[] = $item->getArrayCopy(); 99 | else 100 | return (array) $this; 101 | } 102 | 103 | return $result; 104 | } 105 | 106 | private function getStringOffset($offset) 107 | { 108 | $stringIndexer = new StringIndexer($offset); 109 | $callee = $stringIndexer->convertToMethod(); 110 | 111 | return $this->{$callee['method']}($callee['arg']); 112 | } 113 | 114 | /** 115 | * Get array indexes according to conditions 116 | * @param mixed $offset could be array of indexes or array of booleans 117 | * @return array 118 | */ 119 | private function getIndexes($offset) 120 | { 121 | $indexes = []; 122 | 123 | // no offset - no indexes 124 | if (empty($offset)) 125 | return []; 126 | 127 | // convert mask array to array of indexes 128 | foreach ($offset as $index => $value) 129 | { 130 | if ($value instanceof $this) 131 | { 132 | $indexes[] = $this->getIndexes($value); 133 | } 134 | elseif ($value === true || $value === false) 135 | { 136 | if ($value) 137 | $indexes[] = $index; 138 | } 139 | else 140 | { 141 | $indexes[] = $value; 142 | } 143 | } 144 | 145 | return $indexes; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Indexing/StringIndexer.php: -------------------------------------------------------------------------------- 1 | str = $str; 14 | } 15 | 16 | public function convertToMethod() 17 | { 18 | $result = ['method' => '', 'arg' => '']; 19 | 20 | $this->normalize(); 21 | 22 | //for the first version we accept only single operators: > 30, and so on. 23 | list($method, $arg) = explode(' ', $this->str); 24 | 25 | $result['method'] = Operators::$comparations2Symbol[$method]; 26 | $result['arg'] = $arg; 27 | 28 | return $result; 29 | } 30 | 31 | private function normalize() 32 | { 33 | $str = str_replace( 34 | array_keys(Operators::$comparations2Symbol), 35 | array_map(function($item){ 36 | return ' ' . $item . ' '; 37 | }, array_keys(Operators::$comparations2Symbol)), 38 | $this->str); 39 | 40 | $this->str = trim(str_replace(' ',' ', $str)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Operator/Bitwise.php: -------------------------------------------------------------------------------- 1 | $value) 19 | $result[] = $value && $a2[$key]; 20 | 21 | return $result; 22 | } 23 | 24 | public static function b_or(np_array $a1, np_array $a2) 25 | { 26 | $result = []; 27 | 28 | foreach ($a1 as $key => $value) 29 | $result[] = $value || $a2[$key]; 30 | 31 | return $result; 32 | } 33 | } -------------------------------------------------------------------------------- /src/Operator/Operators.php: -------------------------------------------------------------------------------- 1 | 'eq', 20 | '>' => 'gt', 21 | '>=' => 'gte', 22 | '<' => 'lt', 23 | '<=' => 'lte', 24 | '!=' => 'neq', 25 | ]; 26 | 27 | public static $operators = [ 28 | 'add', 29 | 'div', 30 | 'mod', 31 | 'mul', 32 | 'pow', 33 | 'sub', 34 | ]; 35 | 36 | /** 37 | * Comparison module 38 | */ 39 | 40 | public static function eq(np_array $data, $arg) 41 | { 42 | self::checkArg($data, $arg, $isScalar); 43 | 44 | $result = []; 45 | 46 | foreach ($data as $index => $item) 47 | if ($item instanceof np_array) 48 | $result[] = self::eq($item, $arg); 49 | else 50 | $result[] = $item == $arg[$isScalar?0:$index]; 51 | 52 | return $result; 53 | } 54 | 55 | public static function gt(np_array $data, $arg) 56 | { 57 | self::checkArg($data, $arg, $isScalar); 58 | 59 | $result = []; 60 | 61 | foreach ($data as $index => $item) 62 | if ($item instanceof np_array) 63 | $result[] = self::gt($item, $arg); 64 | else 65 | $result[] = $item > $arg[$isScalar?0:$index]; 66 | 67 | return $result; 68 | } 69 | 70 | public static function gte(np_array $data, $arg) 71 | { 72 | self::checkArg($data, $arg, $isScalar); 73 | 74 | $result = []; 75 | 76 | foreach ($data as $index => $item) 77 | if ($item instanceof np_array) 78 | $result[] = self::gte($item, $arg); 79 | else 80 | $result[] = $item >= $arg[$isScalar?0:$index]; 81 | 82 | return $result; 83 | } 84 | 85 | public static function lt(np_array $data, $arg) 86 | { 87 | self::checkArg($data, $arg, $isScalar); 88 | 89 | $result = []; 90 | 91 | foreach ($data as $index => $item) 92 | if ($item instanceof np_array) 93 | $result[] = self::lt($item, $arg); 94 | else 95 | $result[] = $item < $arg[$isScalar?0:$index]; 96 | 97 | return $result; 98 | } 99 | 100 | public static function lte(np_array $data, $arg) 101 | { 102 | self::checkArg($data, $arg, $isScalar); 103 | 104 | $result = []; 105 | 106 | foreach ($data as $index => $item) 107 | if ($item instanceof np_array) 108 | $result[] = self::lte($item, $arg); 109 | else 110 | $result[] = $item <= $arg[$isScalar?0:$index]; 111 | 112 | return $result; 113 | } 114 | 115 | public static function neq(np_array $data, $arg) 116 | { 117 | self::checkArg($data, $arg, $isScalar); 118 | 119 | $result = []; 120 | 121 | foreach ($data as $index => $item) 122 | if ($item instanceof np_array) 123 | $result[] = self::neq($item, $arg); 124 | else 125 | $result[] = $item != $arg[$isScalar?0:$index]; 126 | 127 | return $result; 128 | } 129 | 130 | 131 | /** 132 | * Math module 133 | */ 134 | 135 | public static function add(np_array $data, $arg) 136 | { 137 | self::checkArg($data, $arg, $isScalar); 138 | 139 | $result = []; 140 | 141 | foreach ($data as $index => $item) 142 | if ($item instanceof np_array) 143 | $result[] = self::add($item, $arg); 144 | else 145 | $result[] = $item + $arg[$isScalar?0:$index]; 146 | 147 | return $result; 148 | } 149 | 150 | public static function div(np_array $data, $arg) 151 | { 152 | self::checkArg($data, $arg, $isScalar); 153 | 154 | $result = []; 155 | 156 | foreach ($data as $index => $item) 157 | if ($item instanceof np_array) 158 | $result[] = self::div($item, $arg); 159 | else 160 | $result[] = $item / $arg[$isScalar?0:$index]; 161 | 162 | return $result; 163 | } 164 | 165 | public static function mod(np_array $data, $arg) 166 | { 167 | self::checkArg($data, $arg, $isScalar); 168 | 169 | $result = []; 170 | 171 | foreach ($data as $index => $item) 172 | if ($item instanceof np_array) 173 | $result[] = self::mod($item, $arg); 174 | else 175 | $result[] = $item % $arg[$isScalar?0:$index]; 176 | 177 | return $result; 178 | } 179 | 180 | public static function mul(np_array $data, $arg) 181 | { 182 | self::checkArg($data, $arg, $isScalar); 183 | 184 | $result = []; 185 | 186 | foreach ($data as $index => $item) 187 | if ($item instanceof np_array) 188 | $result[] = self::mul($item, $arg); 189 | else 190 | $result[] = $item * $arg[$isScalar?0:$index]; 191 | 192 | 193 | return $result; 194 | } 195 | 196 | public static function pow(np_array $data, $arg) 197 | { 198 | self::checkArg($data, $arg, $isScalar); 199 | 200 | $result = []; 201 | 202 | foreach ($data as $index => $item) 203 | if ($item instanceof np_array) 204 | $result[] = self::pow($item, $arg); 205 | else 206 | $result[] = pow($item, $arg[$isScalar?0:$index]); 207 | 208 | return $result; 209 | } 210 | 211 | public static function sub(np_array $data, $arg) 212 | { 213 | self::checkArg($data, $arg, $isScalar); 214 | 215 | $result = []; 216 | 217 | foreach ($data as $index => $item) 218 | if ($item instanceof np_array) 219 | $result[] = self::sub($item, $arg); 220 | else 221 | $result[] = $item - $arg[$isScalar?0:$index]; 222 | 223 | return $result; 224 | } 225 | 226 | /** 227 | * Checks provided arg and cast it to np_array class object 228 | * @param np_array $data source array 229 | * @param mixed &$arg numeric, array or np_array 230 | * @param boolean &$isScalar true if provided 1 element for operator (scalar or array with 1 element) 231 | * @return void 232 | */ 233 | private static function checkArg(np_array $data, &$arg, &$isScalar=false) 234 | { 235 | $arg = self::formatArg($arg)->flatten(); 236 | 237 | $isScalar = (count($arg) == 1); 238 | } 239 | 240 | /** 241 | * Convert arg to np_array 242 | * @param mixed $arg 243 | * @return np_array 244 | */ 245 | private static function formatArg($arg) 246 | { 247 | if (is_numeric($arg)) 248 | return new np_array([$arg]); 249 | 250 | if (is_array($arg)) 251 | return new np_array($arg); 252 | 253 | if (is_object($arg) && $arg instanceof np_array) 254 | return $arg; 255 | 256 | // unknown arg format 257 | throw new \Exception("Operators accept only numbers, array or np_arrays as second argument"); 258 | } 259 | } -------------------------------------------------------------------------------- /src/Printing/Printer.php: -------------------------------------------------------------------------------- 1 | level == 0) 18 | { 19 | $max = $this->max(); 20 | $this->_max_all = $max; 21 | $this->subObjectsWalk(function($item) use ($max){$item->_max_all = $max;}); 22 | } 23 | 24 | $result = $this->formatValues($this); 25 | 26 | if ($this->dimensions > 1) 27 | $glue = $this->level ? ", " : ",\n "; 28 | else 29 | $glue = ", "; 30 | 31 | return sprintf("[%s]", implode($glue, $result)); 32 | } 33 | 34 | private function formatValues(np_array $data) 35 | { 36 | $result = []; 37 | 38 | foreach ($data as $item) { 39 | if ($item === true) 40 | $item = 'true'; 41 | elseif($item === false) 42 | $item = 'false'; 43 | elseif (is_null($item)) 44 | $item = 'null'; 45 | 46 | $result[] = str_pad($item, strlen($this->_max_all), ' ', STR_PAD_LEFT); 47 | } 48 | 49 | return $result; 50 | } 51 | 52 | /** 53 | * Walks through all sub objects and apply $f to each of them 54 | */ 55 | private function subObjectsWalk(Callable $f) 56 | { 57 | foreach ($this as $item) 58 | { 59 | if ($item instanceof $this) 60 | { 61 | $f($item); 62 | $item->subObjectsWalk($f); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Random/Random.php: -------------------------------------------------------------------------------- 1 | getArrayCopy(); 18 | 19 | array_walk_recursive($arrayCopy, function($item) use (&$result) { 20 | $result[] = $item; 21 | }); 22 | 23 | return new np_array($result); 24 | } 25 | 26 | /** 27 | * Change shape of the array 28 | * TODO. improve solution to use recursive algo for any size dimensions 29 | * @param array $size 30 | * @return np_array 31 | */ 32 | public function reshape(array $size) 33 | { 34 | $flatten = $this->flatten(); 35 | 36 | if (count($flatten) != $size[0] * $size[1]) 37 | throw new \Exception('Cannot reshape array of size '. count($flatten) .' into shape (' . implode(', ', $size) . ')'); 38 | 39 | $result = []; 40 | $level = 0; 41 | 42 | for ($i=0; $i < $size[0]; $i++) 43 | { 44 | $result[$level] = []; 45 | for ($j=0; $j < $size[1]; $j++) 46 | { 47 | $result[$level][] = current($flatten); 48 | next($flatten); 49 | } 50 | $level++; 51 | } 52 | 53 | return new np_array($result); 54 | } 55 | 56 | public function diagonal($offset = 0) 57 | { 58 | if ($this->dimensions < 2) 59 | throw new \Exception("Diagonal requires an array of at least two dimensions"); 60 | 61 | $result = []; 62 | 63 | foreach ($this as $level => $row) 64 | { 65 | $index = $level + $offset; 66 | 67 | if (!isset($row[$index])) 68 | break; 69 | 70 | $result[] = $row[$index]; 71 | } 72 | 73 | return new np_array($result); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Slicing/Slice.php: -------------------------------------------------------------------------------- 1 | sum() / $this->countAll(); 10 | } 11 | 12 | public function median() 13 | { 14 | // Flatten matrix for median calculation 15 | $sorted = $this->dimensions > 1 ? (array) $this->flatten() : (array) $this; 16 | 17 | sort($sorted, SORT_NUMERIC); 18 | 19 | $count = count($this); 20 | 21 | $middle = (int) ($count / 2); 22 | 23 | if ($count % 2 == 0) 24 | return ($sorted[$middle] + $sorted[$middle - 1]) / 2; 25 | 26 | return $sorted[$middle]; 27 | 28 | } 29 | 30 | public function min() 31 | { 32 | $min = INF; 33 | 34 | $arrayCopy = $this->getArrayCopy(); 35 | 36 | array_walk_recursive($arrayCopy, function($item) use (&$min) { 37 | if ($item < $min) 38 | $min = $item; 39 | }); 40 | 41 | return $min; 42 | } 43 | 44 | public function max() 45 | { 46 | $max = -INF; 47 | 48 | $arrayCopy = $this->getArrayCopy(); 49 | 50 | array_walk_recursive($arrayCopy, function($item) use (&$max) { 51 | if ($item > $max) 52 | $max = $item; 53 | }); 54 | 55 | return $max; 56 | } 57 | 58 | public function sum() 59 | { 60 | $sum = 0; 61 | 62 | $arrayCopy = $this->getArrayCopy(); 63 | 64 | array_walk_recursive($arrayCopy, function($item) use (&$sum) { 65 | $sum += $item; 66 | }); 67 | 68 | return $sum; 69 | } 70 | 71 | public function countAll() 72 | { 73 | $count = 0; 74 | 75 | $arrayCopy = $this->getArrayCopy(); 76 | 77 | array_walk_recursive($arrayCopy, function($item) use (&$count) { 78 | $count++; 79 | }); 80 | 81 | return $count; 82 | } 83 | 84 | 85 | public function describe() 86 | { 87 | return [ 88 | 'count' => $this->countAll(), 89 | 'max' => $this->max(), 90 | 'mean' => $this->mean(), 91 | 'median' => $this->median(), 92 | 'min' => $this->min(), 93 | 'sum' => $this->sum(), 94 | ]; 95 | } 96 | } -------------------------------------------------------------------------------- /src/np.php: -------------------------------------------------------------------------------- 1 | shape; 30 | unset($allowed_shape[0]); 31 | } 32 | else 33 | { 34 | $current_shape = $array->shape; 35 | unset($current_shape[0]); 36 | 37 | if ($current_shape != $allowed_shape) 38 | throw new \Exception("All the input arrays must have same number of dimensions"); 39 | } 40 | 41 | foreach ($array as $elem) 42 | { 43 | $result[] = $elem; 44 | } 45 | } 46 | 47 | return new np_array($result); 48 | } 49 | } -------------------------------------------------------------------------------- /src/np_array.php: -------------------------------------------------------------------------------- 1 | level = $level; 21 | 22 | // convert each subarray into np_array 23 | if (is_array($input)) 24 | { 25 | $this->size = count($input); 26 | 27 | foreach ($input as &$item) 28 | { 29 | if (is_array($item)) 30 | $item = new np_array($item, $flags, $iterator_class, ($this->level + 1)); 31 | } 32 | } 33 | 34 | return parent::__construct($input, $flags, $iterator_class); 35 | } 36 | 37 | public function __call($method, $args) 38 | { 39 | // comparations call: eq, gt. gte, lt, etc... 40 | // math operations call: add, sub, div, etc... 41 | if (in_array($method, Operators::$comparations) || in_array($method, Operators::$operators)) 42 | { 43 | array_unshift($args, $this); 44 | return new self(forward_static_call_array(['numphp\Operator\Operators', $method], $args)); 45 | } 46 | else 47 | throw new \Exception("Invalid operator: " . $method); 48 | } 49 | 50 | /** 51 | * Deep cloning 52 | * @return void 53 | */ 54 | public function __clone() 55 | { 56 | foreach ($this as &$item) 57 | { 58 | if ($item instanceof $this) 59 | $item = clone($item); 60 | } 61 | } 62 | 63 | public function __get($name) 64 | { 65 | if ($name == 'shape') 66 | return $this->shape(); 67 | 68 | if ($name == 'dimensions') 69 | return $this->dimensions(); 70 | 71 | throw new \Exception("Cannot access private property $" . $name); 72 | 73 | } 74 | 75 | /** 76 | * Calculates the shape of array 77 | * @return array of counts for each axis 78 | */ 79 | private function shape() 80 | { 81 | $shape[] = count($this); 82 | 83 | if (isset($this[0]) && $this[0] instanceof np_array) 84 | $shape[] = $this[0]->shape()[0]; 85 | else 86 | return [$this->size]; 87 | 88 | return $shape; 89 | } 90 | 91 | /** 92 | * Returns count of dimensions 93 | * @return int 94 | */ 95 | private function dimensions() 96 | { 97 | return count($this->shape()); 98 | } 99 | } -------------------------------------------------------------------------------- /tests/ComparationsTest.php: -------------------------------------------------------------------------------- 1 | list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 19 | 20 | $this->matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]); 21 | } 22 | 23 | public function testSimpleGt() 24 | { 25 | $res = $this->list[$this->list->gt(5)]; 26 | 27 | $this->assertEquals((array) $res, [6, 7, 8, 9]); 28 | } 29 | 30 | public function testSimpleEq() 31 | { 32 | $res = $this->list[$this->list->eq(5)]; 33 | 34 | $this->assertEquals((array) $res, [5]); 35 | } 36 | 37 | public function testSimpleLt() 38 | { 39 | $res = $this->list[$this->list->lt(5)]; 40 | 41 | $this->assertEquals((array) $res, [0, 1, 2, 3, 4]); 42 | } 43 | 44 | public function testSimpleLte() 45 | { 46 | $res = $this->list[$this->list->lte(5)]; 47 | 48 | $this->assertEquals((array) $res, [0, 1, 2, 3, 4, 5]); 49 | } 50 | 51 | public function testSimpleNeq() 52 | { 53 | $res = $this->list[$this->list->neq(5)]; 54 | 55 | $this->assertEquals((array) $res, [0, 1, 2, 3, 4, 6, 7, 8, 9]); 56 | } 57 | 58 | public function testBitwiseAnd() 59 | { 60 | $res = $this->list[Bitwise::b_and($this->list->gt(5), $this->list->lt(9))]; 61 | $this->assertEquals((array) $res, [6, 7, 8]); 62 | } 63 | 64 | public function testBitwiseOe() 65 | { 66 | $res = $this->list[Bitwise::b_or($this->list->gt(8), $this->list->lt(2))]; 67 | $this->assertEquals((array) $res, [0, 1, 9]); 68 | } 69 | 70 | public function testStringIndex() 71 | { 72 | $res = $this->list[$this->list['> 5']]; 73 | 74 | $this->assertEquals((array) $res, [6, 7, 8, 9]); 75 | } 76 | 77 | public function testVectorGt() 78 | { 79 | $res = $this->list[$this->list->gt([5, 6, 7, 8, 9, 3, 4, 5, 6, 7])]; 80 | 81 | $this->assertEquals((array) $res, [5, 6, 7, 8, 9]); 82 | } 83 | 84 | public function testVectorEq() 85 | { 86 | $res = $this->list[$this->list->eq([0, 5, 6, 7, 4, 3, 5, 6, 7, 8])]; 87 | 88 | $this->assertEquals((array) $res, [0, 4]); 89 | } 90 | 91 | /** 92 | * Matrix 93 | */ 94 | 95 | public function testMatrixSimpleGt() 96 | { 97 | $res = $this->matrix[$this->matrix->gt(5)]; 98 | 99 | $this->assertEquals((array) $res, [6, 7, 8, 9, 10, 11]); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tests/GeneralTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('numphp\np_array', $res); 14 | $this->assertEquals((array) $res, [1, 2, 3]); 15 | } 16 | 17 | /** 18 | * @expectedException \Exception 19 | */ 20 | public function testExceptionInvalidArg() 21 | { 22 | $res = new np_array('string'); 23 | } 24 | 25 | /** 26 | * @expectedException \Exception 27 | */ 28 | public function testExceptionPrivateProperty() 29 | { 30 | $res = (new np_array([1]))->size; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/GeneratorTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('numphp\np_array', $res); 15 | $this->assertEquals(count($res), 5); 16 | 17 | foreach ($res as $item) 18 | { 19 | $this->assertEquals(1, $item); 20 | } 21 | } 22 | 23 | public function testZeros() 24 | { 25 | $res = Generator::zeros(5); 26 | 27 | $this->assertInstanceOf('numphp\np_array', $res); 28 | $this->assertEquals(count($res), 5); 29 | 30 | foreach ($res as $item) 31 | { 32 | $this->assertEquals(0, $item); 33 | } 34 | } 35 | 36 | public function testFull() 37 | { 38 | $res = Generator::full(15, 999); 39 | 40 | $this->assertInstanceOf('numphp\np_array', $res); 41 | $this->assertEquals(count($res), 15); 42 | 43 | foreach ($res as $item) 44 | { 45 | $this->assertEquals(999, $item); 46 | } 47 | } 48 | 49 | /** 50 | * @expectedException \Exception 51 | */ 52 | public function testFullException() 53 | { 54 | $res = Generator::full(15, 'hi there'); 55 | } 56 | 57 | public function testArange() 58 | { 59 | $res = Generator::arange(1, 15, 2); 60 | 61 | $this->assertInstanceOf('numphp\np_array', $res); 62 | $this->assertEquals(count($res), 7); 63 | 64 | $this->assertEquals((array) $res, [1, 3, 5, 7, 9, 11, 13]); 65 | } 66 | 67 | public function testArangeEmptyStop() 68 | { 69 | $n = 6; 70 | $res = Generator::arange($n); 71 | 72 | $this->assertInstanceOf('numphp\np_array', $res); 73 | $this->assertEquals(count($res), $n); 74 | 75 | $this->assertEquals((array) $res, [0, 1, 2, 3, 4, 5]); 76 | } 77 | 78 | public function testFibonacci() 79 | { 80 | $n = 6; 81 | $res = Generator::fib($n); 82 | 83 | $this->assertInstanceOf('numphp\np_array', $res); 84 | $this->assertEquals(count($res), $n); 85 | 86 | $this->assertEquals((array) $res, [1, 1, 2, 3, 5, 8]); 87 | } 88 | 89 | public function testFormula() 90 | { 91 | $n = 6; 92 | $res = Generator::formula(function($n){return 2*$n+1;}, $n); 93 | 94 | $this->assertInstanceOf('numphp\np_array', $res); 95 | $this->assertEquals(count($res), $n); 96 | 97 | $this->assertEquals((array) $res, [1, 3, 5, 7, 9, 11]); 98 | } 99 | 100 | /** 101 | * Matrix 102 | */ 103 | 104 | public function testMartrixOnes() 105 | { 106 | $res = Generator::ones([5, 2]); 107 | 108 | $this->assertInstanceOf('numphp\np_array', $res); 109 | $this->assertEquals($res->countAll(), 10); 110 | 111 | $arrayCopy = $res->getArrayCopy(); 112 | 113 | array_walk_recursive($arrayCopy, function($item){ 114 | $this->assertEquals(1, $item); 115 | }); 116 | } 117 | 118 | public function testMartrixZeros() 119 | { 120 | $res = Generator::zeros([5, 3]); 121 | 122 | $this->assertInstanceOf('numphp\np_array', $res); 123 | $this->assertEquals($res->countAll(), 15); 124 | 125 | $arrayCopy = $res->getArrayCopy(); 126 | 127 | array_walk_recursive($arrayCopy, function($item){ 128 | $this->assertEquals(0, $item); 129 | }); 130 | } 131 | 132 | public function testMartrixFull() 133 | { 134 | $res = Generator::full([7, 2], 999); 135 | 136 | $this->assertInstanceOf('numphp\np_array', $res); 137 | $this->assertEquals($res->countAll(), 14); 138 | 139 | $arrayCopy = $res->getArrayCopy(); 140 | 141 | array_walk_recursive($arrayCopy, function($item){ 142 | $this->assertEquals(999, $item); 143 | }); 144 | } 145 | 146 | public function testMatrixFromDiagonal() 147 | { 148 | $res = Generator::diagonal([5, 3, 1]); 149 | 150 | $this->assertInstanceOf('numphp\np_array', $res); 151 | 152 | $this->assertEquals(5, $res[0][0]); 153 | $this->assertEquals(3, $res[1][1]); 154 | $this->assertEquals(1, $res[2][2]); 155 | } 156 | 157 | public function testMatrixFromDiagonalNpArray() 158 | { 159 | $res = Generator::diagonal(new np_array([5, 3, 1])); 160 | 161 | $this->assertInstanceOf('numphp\np_array', $res); 162 | 163 | $this->assertEquals(5, $res[0][0]); 164 | $this->assertEquals(3, $res[1][1]); 165 | $this->assertEquals(1, $res[2][2]); 166 | } 167 | 168 | /** 169 | * @expectedException \Exception 170 | */ 171 | public function testMatrixFromDiagonalExceptionInvalidDimension() 172 | { 173 | $res = Generator::diagonal([[1,2], [3,4]]); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /tests/IndexingTest.php: -------------------------------------------------------------------------------- 1 | list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 18 | } 19 | 20 | public function testSimpleIndex() 21 | { 22 | $res = $this->list[2]; 23 | 24 | $this->assertEquals($res, 2); 25 | } 26 | 27 | public function testArrayIndex() 28 | { 29 | $res = $this->list[[3,4]]; 30 | 31 | $this->assertEquals((array) $res, [3, 4]); 32 | } 33 | 34 | public function testConditionIndex() 35 | { 36 | $res = $this->list[$this->list->gte(5)]; 37 | 38 | $this->assertEquals((array) $res, [5, 6, 7, 8, 9]); 39 | } 40 | 41 | public function testMask() 42 | { 43 | $res = $this->list->mask('> 5'); 44 | 45 | $this->assertEquals((array) $res, [false, false, false, false, false, false, true, true, true, true]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/OperatorsTest.php: -------------------------------------------------------------------------------- 1 | list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 18 | } 19 | 20 | public function testAdd() 21 | { 22 | $res = $this->list->add(5); 23 | 24 | $this->assertEquals((array) $res, [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]); 25 | } 26 | 27 | public function testSub() 28 | { 29 | $res = $this->list->sub(10); 30 | 31 | $this->assertEquals((array) $res, [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1]); 32 | } 33 | 34 | public function testDiv() 35 | { 36 | $res = $this->list->div(2); 37 | 38 | $this->assertEquals((array) $res, [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5]); 39 | } 40 | 41 | public function testPow() 42 | { 43 | $res = $this->list->pow(2); 44 | 45 | $this->assertEquals((array) $res, [0, 1, 4, 9, 16, 25,36, 49, 64, 81]); 46 | } 47 | 48 | public function testMod() 49 | { 50 | $res = $this->list->mod(2); 51 | 52 | $this->assertEquals((array) $res, [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]); 53 | } 54 | 55 | public function testMul() 56 | { 57 | $res = $this->list->mul(5); 58 | 59 | $this->assertEquals((array) $res, [0, 5, 10, 15, 20, 25, 30, 35, 40, 45]); 60 | } 61 | 62 | public function testAddVector() 63 | { 64 | $res = $this->list->add(new np_array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])); 65 | 66 | $this->assertEquals((array) $res, [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]); 67 | } 68 | 69 | public function testAddArray() 70 | { 71 | $res = $this->list->add([9, 8, 7, 6, 5, 4, 3, 2, 1, 0]); 72 | 73 | $this->assertEquals((array) $res, [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]); 74 | } 75 | 76 | public function testSubVector() 77 | { 78 | $res = $this->list->sub(new np_array([10, 10, 10, 10, 10, 10, 10, 10, 10, 10])); 79 | 80 | $this->assertEquals((array) $res, [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1]); 81 | } 82 | 83 | /** 84 | * @expectedException \Exception 85 | */ 86 | public function testExceptionInvalidArg() 87 | { 88 | $res = $this->list->add('string'); 89 | } 90 | 91 | /** 92 | * @expectedException \Exception 93 | */ 94 | public function testExceptionInvalidOperator() 95 | { 96 | $res = $this->list->qqq('string'); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/PrinterTest.php: -------------------------------------------------------------------------------- 1 | list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 17 | $this->booleanList = new np_array([true, false, true, null]); 18 | 19 | $this->matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]); 20 | } 21 | 22 | public function testEcho() 23 | { 24 | $this->assertEquals((string) $this->list, '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'); 25 | } 26 | 27 | public function testEchoBoolean() 28 | { 29 | $this->assertEquals((string) $this->booleanList, '[true, false, true, null]'); 30 | } 31 | 32 | public function testMatrix() 33 | { 34 | $this->assertEquals((string) $this->matrix, "[[ 0, 1, 2, 3],\n [ 4, 5, 6, 7],\n [ 8, 9, 10, 11]]"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/RandomTest.php: -------------------------------------------------------------------------------- 1 | assertLessThan(1, $res); 14 | } 15 | 16 | public function testGenerateRandomArray() 17 | { 18 | $res = Random::rand(5); 19 | 20 | $this->assertInstanceOf('numphp\np_array', $res); 21 | $this->assertEquals(count($res), 5); 22 | 23 | foreach ($res as $item) 24 | { 25 | $this->assertLessThan(1, $item); 26 | } 27 | } 28 | 29 | public function testRandomInt() 30 | { 31 | $res = Random::randint(15); 32 | 33 | $this->assertGreaterThanOrEqual(0, $res); 34 | $this->assertLessThanOrEqual(15, $res); 35 | } 36 | 37 | public function testRandomIntInterval() 38 | { 39 | $res = Random::randint(10, 15); 40 | 41 | $this->assertGreaterThanOrEqual(10, $res); 42 | $this->assertLessThanOrEqual(15, $res); 43 | } 44 | 45 | public function testRandomIntIntervalWithSize() 46 | { 47 | $res = Random::randint(10, 15, 5); 48 | 49 | $this->assertInstanceOf('numphp\np_array', $res); 50 | $this->assertEquals(count($res), 5); 51 | 52 | foreach ($res as $item) 53 | { 54 | $this->assertGreaterThanOrEqual(10, $item); 55 | $this->assertLessThanOrEqual(15, $item); 56 | } 57 | } 58 | 59 | /** 60 | * @expectedException \Exception 61 | */ 62 | public function testExceptionWhileInvalidSize() 63 | { 64 | $res = Random::randint(10, 15, -5); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/SetItemsTest.php: -------------------------------------------------------------------------------- 1 | list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 18 | 19 | $this->matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]); 20 | } 21 | 22 | public function testAddNewItemIndex() 23 | { 24 | $res = clone($this->list); 25 | $res[] = 999; 26 | 27 | $this->assertEquals((array) $res, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 999]); 28 | } 29 | 30 | public function testChangeItemByIndex() 31 | { 32 | $res = clone($this->list); 33 | $res[2] = 999; 34 | 35 | $this->assertEquals((array) $res, [0, 1, 999, 3, 4, 5, 6, 7, 8, 9]); 36 | } 37 | 38 | public function testChangeItemByCondition() 39 | { 40 | $res = clone($this->list); 41 | $res[$res->lt(5)] = 999; 42 | 43 | $this->assertEquals((array) $res, [999, 999, 999, 999, 999, 5, 6, 7, 8, 9]); 44 | } 45 | 46 | public function testAddItemAndChangeAll() 47 | { 48 | $res = clone($this->list); 49 | $res[] = 10; 50 | $res[$res->lt(999)] = 999; 51 | 52 | $this->assertEquals((array) $res, [999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999]); 53 | } 54 | 55 | public function testChangeItemByConditionStringIndex() 56 | { 57 | $res = clone($this->list); 58 | $res[$res['< 5']] = 999; 59 | 60 | $this->assertEquals((array) $res, [999, 999, 999, 999, 999, 5, 6, 7, 8, 9]); 61 | } 62 | 63 | public function testSetItemsBySlice() 64 | { 65 | $res = clone($this->list); 66 | $res['1:3'] = 999; 67 | 68 | $this->assertEquals((array) $res, [0, 999, 999, 3, 4, 5, 6, 7, 8, 9]); 69 | } 70 | 71 | 72 | /** 73 | * Matrix 74 | */ 75 | 76 | public function testMatrixChangeItemByCondition() 77 | { 78 | $res = clone($this->matrix); 79 | $res[$res->gte(5)] = 999; 80 | 81 | $this->assertEquals($res, new np_array([[0, 1, 2, 3], [4, 999, 999, 999], [999, 999, 999, 999]])); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tests/ShapingTest.php: -------------------------------------------------------------------------------- 1 | list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 17 | $this->listEven = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8]); 18 | 19 | $this->matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]); 20 | } 21 | 22 | public function testFlatten() 23 | { 24 | $res = $this->matrix->flatten(); 25 | 26 | $this->assertEquals((array) $res, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); 27 | } 28 | 29 | public function testReshape() 30 | { 31 | $res = $this->matrix->reshape([6, 2]); 32 | 33 | $this->assertEquals($res->getArrayCopy(), [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]]); 34 | } 35 | 36 | /** 37 | * @expectedException \Exception 38 | */ 39 | public function testReshapeInvalidShape() 40 | { 41 | $res = $this->matrix->reshape([1, 2]); 42 | } 43 | 44 | /** 45 | * @expectedException \Exception 46 | */ 47 | public function testDiagonalInvalidShape() 48 | { 49 | $res = $this->list->diagonal(); 50 | } 51 | 52 | public function testDiagonal() 53 | { 54 | $res = $this->matrix->diagonal(); 55 | 56 | $this->assertEquals($res->getArrayCopy(), [0, 5, 10]); 57 | } 58 | 59 | public function testDiagonalWithOffset() 60 | { 61 | $res = $this->matrix->diagonal(2); 62 | 63 | $this->assertEquals($res->getArrayCopy(), [2, 7]); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/SliceTest.php: -------------------------------------------------------------------------------- 1 | list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 17 | } 18 | 19 | public function testStartStop() 20 | { 21 | $res = $this->list['1:3']; 22 | 23 | $this->assertInstanceOf('numphp\np_array', $res); 24 | $this->assertEquals((array) $res, [1, 2]); 25 | } 26 | 27 | public function testStartStopStep() 28 | { 29 | $res = $this->list['1:5:2']; 30 | 31 | $this->assertInstanceOf('numphp\np_array', $res); 32 | $this->assertEquals((array) $res, [1, 3]); 33 | } 34 | 35 | public function testNegativeStart() 36 | { 37 | $res = $this->list['-7:6']; 38 | 39 | $this->assertInstanceOf('numphp\np_array', $res); 40 | $this->assertEquals((array) $res, [3, 4, 5]); 41 | } 42 | 43 | public function testNegativeEnd() 44 | { 45 | $res = $this->list['-7:-2']; 46 | 47 | $this->assertInstanceOf('numphp\np_array', $res); 48 | $this->assertEquals((array) $res, [3, 4, 5, 6, 7]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/StatisticsTest.php: -------------------------------------------------------------------------------- 1 | list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 17 | $this->listEven = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8]); 18 | 19 | $this->matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]); 20 | } 21 | 22 | public function testSum() 23 | { 24 | $res = $this->list->sum(); 25 | 26 | $this->assertEquals($res, 45); 27 | } 28 | 29 | public function testMean() 30 | { 31 | $res = $this->list->mean(); 32 | 33 | $this->assertEquals($res, 4.5); 34 | } 35 | 36 | public function testMedian() 37 | { 38 | $res = $this->list->median(); 39 | 40 | $this->assertEquals($res, 4.5); 41 | } 42 | 43 | public function testMedianEven() 44 | { 45 | $res = $this->listEven->median(); 46 | 47 | $this->assertEquals($res, 4); 48 | } 49 | 50 | public function testMin() 51 | { 52 | $res = $this->list->min(); 53 | 54 | $this->assertEquals($res, 0); 55 | } 56 | 57 | public function testMax() 58 | { 59 | $res = $this->list->max(); 60 | 61 | $this->assertEquals($res, 9); 62 | } 63 | 64 | public function testDescribe() 65 | { 66 | $res = $this->list->describe(); 67 | 68 | $this->assertEquals(array_keys((array)$res), ['count', 'max', 'mean', 'median', 'min', 'sum']); 69 | } 70 | 71 | /** 72 | * Matrix 73 | */ 74 | 75 | public function testMatrixMax() 76 | { 77 | $res = $this->matrix->max(); 78 | 79 | $this->assertEquals($res, 11); 80 | } 81 | 82 | public function testMatrixDescribe() 83 | { 84 | $res = $this->matrix->describe(); 85 | 86 | $this->assertEquals(array_keys((array)$res), ['count', 'max', 'mean', 'median', 'min', 'sum']); 87 | } 88 | } 89 | --------------------------------------------------------------------------------