SearchTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. <?php
  2. namespace MathPHP\Tests\Search;
  3. use MathPHP\Exception;
  4. use MathPHP\Search;
  5. class SearchTest extends \PHPUnit\Framework\TestCase
  6. {
  7. /**
  8. * @test sorted
  9. * @dataProvider dataProviderForSearchSorted
  10. * @param array $haystack
  11. * @param float $needle
  12. * @param int $expected
  13. */
  14. public function testSearchSorted(array $haystack, float $needle, int $expected)
  15. {
  16. // When
  17. $insertionPoint = Search::sorted($haystack, $needle);
  18. // Then
  19. $this->assertSame($expected, $insertionPoint);
  20. }
  21. /**
  22. * Test data created with Python NumPy searchsorted
  23. * @return array (haystack, needle, expected)
  24. */
  25. public function dataProviderForSearchSorted(): array
  26. {
  27. return [
  28. [[1, 2, 3, 4, 5], -100, 0],
  29. [[1, 2, 3, 4, 5], -1, 0],
  30. [[1, 2, 3, 4, 5], 0, 0],
  31. [[1, 2, 3, 4, 5], 1, 0],
  32. [[1, 2, 3, 4, 5], 2, 1],
  33. [[1, 2, 3, 4, 5], 3, 2],
  34. [[1, 2, 3, 4, 5], 4, 3],
  35. [[1, 2, 3, 4, 5], 5, 4],
  36. [[1, 2, 3, 4, 5], 6, 5],
  37. [[1, 2, 3, 4, 5], 7, 5],
  38. [[1, 2, 3, 4, 5], 100, 5],
  39. [[-8, -5, -1, 3, 6, 10], -10, 0],
  40. [[-8, -5, -1, 3, 6, 10], -9, 0],
  41. [[-8, -5, -1, 3, 6, 10], -8, 0],
  42. [[-8, -5, -1, 3, 6, 10], -7, 1],
  43. [[-8, -5, -1, 3, 6, 10], -6, 1],
  44. [[-8, -5, -1, 3, 6, 10], -5, 1],
  45. [[-8, -5, -1, 3, 6, 10], -4, 2],
  46. [[-8, -5, -1, 3, 6, 10], -3, 2],
  47. [[-8, -5, -1, 3, 6, 10], -2, 2],
  48. [[-8, -5, -1, 3, 6, 10], -1, 2],
  49. [[-8, -5, -1, 3, 6, 10], 0, 3],
  50. [[-8, -5, -1, 3, 6, 10], 1, 3],
  51. [[-8, -5, -1, 3, 6, 10], 2, 3],
  52. [[-8, -5, -1, 3, 6, 10], 3, 3],
  53. [[-8, -5, -1, 3, 6, 10], 4, 4],
  54. [[-8, -5, -1, 3, 6, 10], 5, 4],
  55. [[-8, -5, -1, 3, 6, 10], 6, 4],
  56. [[-8, -5, -1, 3, 6, 10], 7, 5],
  57. [[-8, -5, -1, 3, 6, 10], 8, 5],
  58. [[-8, -5, -1, 3, 6, 10], 9, 5],
  59. [[-8, -5, -1, 3, 6, 10], 10, 5],
  60. [[-8, -5, -1, 3, 6, 10], 11, 6],
  61. [[-8, -5, -1, 3, 6, 10], 12, 6],
  62. [[-8, -5, -1, 3, 6, 10], 100, 6],
  63. [[1.1, 2.2, 3.3, 4.4, 5.5], -1.4, 0],
  64. [[1.1, 2.2, 3.3, 4.4, 5.5], -0.1, 0],
  65. [[1.1, 2.2, 3.3, 4.4, 5.5], 0, 0],
  66. [[1.1, 2.2, 3.3, 4.4, 5.5], 1.045, 0],
  67. [[1.1, 2.2, 3.3, 4.4, 5.5], 1.2, 1],
  68. [[1.1, 2.2, 3.3, 4.4, 5.5], 2, 1],
  69. [[1.1, 2.2, 3.3, 4.4, 5.5], 2.5, 2],
  70. [[1.1, 2.2, 3.3, 4.4, 5.5], 3.2, 2],
  71. [[1.1, 2.2, 3.3, 4.4, 5.5], 3.8, 3],
  72. [[1.1, 2.2, 3.3, 4.4, 5.5], 4.3, 3],
  73. [[1.1, 2.2, 3.3, 4.4, 5.5], 4.5, 4],
  74. [[1.1, 2.2, 3.3, 4.4, 5.5], 5.4, 4],
  75. [[1.1, 2.2, 3.3, 4.4, 5.5], 5.55, 5],
  76. [[1.1, 2.2, 3.3, 4.4, 5.5], 20.9, 5],
  77. [[4, 4.14285714, 4.28571429, 4.42857143, 4.57142857, 4.71428571, 4.85714286, 5, 5.14285714, 5.28571429, 5.42857143, 5.57142857, 5.71428571, 5.85714286, 6, 6.14285714, 6.28571429, 6.42857143, 6.57142857, 6.71428571, 6.85714286, 7], 6.2, 16],
  78. ];
  79. }
  80. /**
  81. * @test sorted on empty list
  82. */
  83. public function testSearchSortedEmptyList()
  84. {
  85. // Given
  86. $haystack = [];
  87. $needle = 5;
  88. // When
  89. $insertionPoint = Search::sorted($haystack, $needle);
  90. // Then
  91. $this->assertSame(0, $insertionPoint);
  92. }
  93. /**
  94. * @test argMax
  95. * @dataProvider dataProviderForArgMax
  96. * @dataProvider dataProviderForArgMaxWithNans
  97. * @param array $values
  98. * @param int $expected
  99. */
  100. public function testArgMax(array $values, int $expected)
  101. {
  102. // When
  103. $indexOfMax = Search::argMax($values);
  104. // Then
  105. $this->assertSame($expected, $indexOfMax);
  106. }
  107. /**
  108. * Test data created with Python NumPy argmax
  109. * @return array
  110. */
  111. public function dataProviderForArgMax(): array
  112. {
  113. return [
  114. [[-100], 0],
  115. [[-1], 0],
  116. [[0], 0],
  117. [[1], 0],
  118. [[2], 0],
  119. [[100], 0],
  120. [[-100.43], 0],
  121. [[-1.3], 0],
  122. [[0.0], 0],
  123. [[0.0000003829], 0],
  124. [[1.5], 0],
  125. [[2.2], 0],
  126. [[100.4089], 0],
  127. [[0, 1], 1],
  128. [[1, 2], 1],
  129. [[3, 6], 1],
  130. [[94, 95], 1],
  131. [[9384935, 900980398049], 1],
  132. [[0.0, 0.1], 1],
  133. [[0.00004, 0.00005], 1],
  134. [[39.34, 39.35], 1],
  135. [[-4, -3], 1],
  136. [[-0.00001, 0], 1],
  137. [[-1, -1], 0],
  138. [[0, 0], 0],
  139. [[1, 1], 0],
  140. [[1.7, 1.7], 0],
  141. [[34535, 34535], 0],
  142. [[5, 1, 2, 3, 4], 0],
  143. [[1, 5, 2, 3, 4], 1],
  144. [[1, 2, 5, 3, 4], 2],
  145. [[1, 2, 3, 5, 4], 3],
  146. [[1, 2, 3, 4, 5], 4],
  147. [[5, 1, 2, 3, 5], 0],
  148. [[1, 5, 2, 5, 4], 1],
  149. [[1, 5, 5, 3, 4], 1],
  150. [[1, 2, 5, 5, 4], 2],
  151. [[1, 2, 3, 5, 5], 3],
  152. [[1.1, 1.2, 1.3, 1.4, 1.5], 4],
  153. [[92830482039, 980983209480923, 823094802943, \INF], 3],
  154. ];
  155. }
  156. /**
  157. * Test data created with Python NumPy argmax
  158. * @return array
  159. */
  160. public function dataProviderForArgMaxWithNans(): array
  161. {
  162. return [
  163. [[0, 1, 2, 3, \NAN], 4],
  164. [[0, 1, 2, \NAN, 3], 3],
  165. [[0, 1, \NAN, 2, 3], 2],
  166. [[0, \NAN, 1, 2, 3], 1],
  167. [[\NAN, 0, 1, 2, 3], 0],
  168. [[\NAN, 0, \NAN, 1, 2, 3], 0],
  169. [[\NAN, \INF], 0],
  170. [[\INF, \NAN], 1],
  171. ];
  172. }
  173. /**
  174. * @test argMax error when the input array is empty
  175. */
  176. public function testArgMaxErrorOnEmptyArray()
  177. {
  178. // Given
  179. $values = [];
  180. // Then
  181. $this->expectException(Exception\BadDataException::class);
  182. // When
  183. $index = Search::argMax($values);
  184. }
  185. /**
  186. * @test nanArgMax
  187. * @dataProvider dataProviderForArgMax
  188. * @dataProvider dataProviderForNanArgMaxWithNans
  189. * @param array $values
  190. * @param int $expected
  191. */
  192. public function testNanArgMax(array $values, int $expected)
  193. {
  194. // When
  195. $indexOfMax = Search::nanArgMax($values);
  196. // Then
  197. $this->assertSame($expected, $indexOfMax);
  198. }
  199. /**
  200. * Test data created with Python NumPy nanargmax
  201. * @return array
  202. */
  203. public function dataProviderForNanArgMaxWithNans(): array
  204. {
  205. return [
  206. [[0, 1, 2, 3, \NAN], 3],
  207. [[0, 1, 2, \NAN, 3], 4],
  208. [[0, 1, \NAN, 2, 3], 4],
  209. [[0, \NAN, 1, 2, 3], 4],
  210. [[\NAN, 0, 1, 2, 3], 4],
  211. [[\NAN, 0, \NAN, 1, 2, 3], 5],
  212. [[\NAN, \INF], 1],
  213. [[\INF, \NAN], 0],
  214. ];
  215. }
  216. /**
  217. * @test nanArgMax error when the input array is empty
  218. */
  219. public function testNanArgMaxErrorOnEmptyArray()
  220. {
  221. // Given
  222. $values = [];
  223. // Then
  224. $this->expectException(Exception\BadDataException::class);
  225. // When
  226. $index = Search::nanArgMax($values);
  227. }
  228. /**
  229. * @test nanArgMax error when the input array is empty
  230. */
  231. public function testNanArgMaxErrorOnArrayOfAllNans()
  232. {
  233. // Given
  234. $values = [\NAN, \NAN, \NAN];
  235. // Then
  236. $this->expectException(Exception\BadDataException::class);
  237. // When
  238. $index = Search::nanArgMax($values);
  239. }
  240. /**
  241. * @test argMin
  242. * @dataProvider dataProviderForArgMin
  243. * @dataProvider dataProviderForArgMinWithNans
  244. * @param array $values
  245. * @param int $expected
  246. */
  247. public function testArgMin(array $values, int $expected)
  248. {
  249. // When
  250. $indexOfMax = Search::argMin($values);
  251. // Then
  252. $this->assertSame($expected, $indexOfMax);
  253. }
  254. /**
  255. * Test data created with Python NumPy argmax
  256. * @return array
  257. */
  258. public function dataProviderForArgMin(): array
  259. {
  260. return [
  261. [[-100], 0],
  262. [[-1], 0],
  263. [[0], 0],
  264. [[1], 0],
  265. [[2], 0],
  266. [[100], 0],
  267. [[-100.43], 0],
  268. [[-1.3], 0],
  269. [[0.0], 0],
  270. [[0.0000003829], 0],
  271. [[1.5], 0],
  272. [[2.2], 0],
  273. [[100.4089], 0],
  274. [[0, 1], 0],
  275. [[1, 2], 0],
  276. [[3, 6], 0],
  277. [[94, 95], 0],
  278. [[9384935, 900980398049], 0],
  279. [[0.0, 0.1], 0],
  280. [[0.00004, 0.00005], 0],
  281. [[39.34, 39.35], 0],
  282. [[-4, -3], 0],
  283. [[-0.00001, 0], 0],
  284. [[-1, -1], 0],
  285. [[0, 0], 0],
  286. [[1, 1], 0],
  287. [[1.7, 1.7], 0],
  288. [[34535, 34535], 0],
  289. [[1, 2, 3, 4, 5], 0],
  290. [[2, 1, 3, 4, 5], 1],
  291. [[3, 4, 2, 5, 6], 2],
  292. [[2, 3, 4, 1, 5], 3],
  293. [[2, 3, 4, 5, 1], 4],
  294. [[1, 2, 3, 4, 1], 0],
  295. [[2, 1, 3, 4, 1], 1],
  296. [[3, 4, 2, 1, 5], 3],
  297. [[2, 3, 4, 1, 1], 3],
  298. [[2, 3, 4, 5, 1], 4],
  299. [[1.1, 1.2, 1.3, 1.4, 1.5], 0],
  300. [[-92830482039, -980983209480923, -823094802943, -\INF], 3],
  301. ];
  302. }
  303. /**
  304. * Test data created with Python NumPy argmax
  305. * @return array
  306. */
  307. public function dataProviderForArgMinWithNans(): array
  308. {
  309. return [
  310. [[0, 1, 2, 3, \NAN], 4],
  311. [[0, 1, 2, \NAN, 3], 3],
  312. [[0, 1, \NAN, 2, 3], 2],
  313. [[0, \NAN, 1, 2, 3], 1],
  314. [[\NAN, 0, 1, 2, 3], 0],
  315. [[\NAN, 0, \NAN, 1, 2, 3], 0],
  316. [[\NAN, -\INF], 0],
  317. [[-\INF, \NAN], 1],
  318. ];
  319. }
  320. /**
  321. * @test argMin error when the input array is empty
  322. */
  323. public function testArgMinErrorOnEmptyArray()
  324. {
  325. // Given
  326. $values = [];
  327. // Then
  328. $this->expectException(Exception\BadDataException::class);
  329. // When
  330. $index = Search::argMin($values);
  331. }
  332. /**
  333. * @test nanArgMin
  334. * @dataProvider dataProviderForArgMin
  335. * @dataProvider dataProviderForNanArgMinWithNans
  336. * @param array $values
  337. * @param int $expected
  338. */
  339. public function testNanArgMin(array $values, int $expected)
  340. {
  341. // When
  342. $indexOfMax = Search::nanArgMin($values);
  343. // Then
  344. $this->assertSame($expected, $indexOfMax);
  345. }
  346. /**
  347. * Test data created with Python NumPy nanargmin
  348. * @return array
  349. */
  350. public function dataProviderForNanArgMinWithNans(): array
  351. {
  352. return [
  353. [[0, 1, 2, 3, \NAN], 0],
  354. [[0, 1, 2, \NAN, 3], 0],
  355. [[0, 1, \NAN, 2, 3], 0],
  356. [[0, \NAN, 1, 2, 3], 0],
  357. [[\NAN, 0, 1, 2, 3], 1],
  358. [[\NAN, 0, \NAN, 1, 2, 3], 1],
  359. [[\NAN, -\INF], 1],
  360. [[-\INF, \NAN], 0],
  361. ];
  362. }
  363. /**
  364. * @test nanArgMin error when the input array is empty
  365. */
  366. public function testNanArgMinErrorOnEmptyArray()
  367. {
  368. // Given
  369. $values = [];
  370. // Then
  371. $this->expectException(Exception\BadDataException::class);
  372. // When
  373. $index = Search::nanArgMin($values);
  374. }
  375. /**
  376. * @test nanArgMin error when the input array is empty
  377. */
  378. public function testNanArgMinErrorOnArrayOfAllNans()
  379. {
  380. // Given
  381. $values = [\NAN, \NAN, \NAN];
  382. // Then
  383. $this->expectException(Exception\BadDataException::class);
  384. // When
  385. $index = Search::nanArgMin($values);
  386. }
  387. /**
  388. * @test nonZero
  389. * @dataProvider dataProviderForNonZero
  390. * @param array $values
  391. * @param array $expected
  392. */
  393. public function testNonZero(array $values, array $expected)
  394. {
  395. // When
  396. $indices = Search::nonZero($values);
  397. // Then
  398. $this->assertEquals($expected, $indices);
  399. }
  400. /**
  401. * @return array (values, expected)
  402. */
  403. public function dataProviderForNonZero(): array
  404. {
  405. return [
  406. [
  407. [],
  408. [],
  409. ],
  410. [
  411. [0],
  412. [],
  413. ],
  414. [
  415. [0, 0, 0],
  416. [],
  417. ],
  418. [
  419. [0.0],
  420. [],
  421. ],
  422. [
  423. [0.0],
  424. [],
  425. ],
  426. [
  427. [0, -0, 0.0, -0.0, '0', '-0', '0.0', '-0.0', false],
  428. [],
  429. ],
  430. [
  431. [1],
  432. [0],
  433. ],
  434. [
  435. [-1],
  436. [0],
  437. ],
  438. [
  439. [1],
  440. [0],
  441. ],
  442. [
  443. [1.1],
  444. [0],
  445. ],
  446. [
  447. [-1.1],
  448. [0],
  449. ],
  450. [
  451. [98273492837],
  452. [0],
  453. ],
  454. [
  455. [-90273402738049],
  456. [0],
  457. ],
  458. [
  459. [0.0000005],
  460. [0],
  461. ],
  462. [
  463. [-0.0000005],
  464. [0],
  465. ],
  466. [
  467. [\INF],
  468. [0],
  469. ],
  470. [
  471. [-\INF],
  472. [0],
  473. ],
  474. [
  475. [\NAN],
  476. [0],
  477. ],
  478. [
  479. [1, 2, 3],
  480. [0, 1, 2],
  481. ],
  482. [
  483. [1, -1, 0.1, -0.1, 83928, -8939823, \INF, \NAN],
  484. [0, 1, 2, 3, 4, 5, 6, 7],
  485. ],
  486. [
  487. [0, 1, 0, 1, 0],
  488. [1, 3],
  489. ],
  490. [
  491. [0, 0, 0, 0, 1, 1, 1, 1],
  492. [4, 5, 6, 7],
  493. ],
  494. [
  495. [1, 1, 1, 1, 0, 0, 0, 0],
  496. [0, 1, 2, 3],
  497. ],
  498. [
  499. [0.0, -0.0, 0.000000000000001, -0.000000000000001],
  500. [2, 3],
  501. ],
  502. [
  503. ['0', '1', '0.0'],
  504. [1],
  505. ],
  506. [
  507. [true, false, true, false],
  508. [0, 2],
  509. ],
  510. [
  511. [1, 2, [], [], new \stdClass(), new \stdClass(), 0, 1, 2],
  512. [0, 1, 7, 8],
  513. ],
  514. ];
  515. }
  516. }