RationalTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. <?php
  2. namespace MathPHP\Tests\Number;
  3. use MathPHP\Number\Rational;
  4. use MathPHP\Exception;
  5. class RationalTest extends \PHPUnit\Framework\TestCase
  6. {
  7. /**
  8. * @test createZeroValue
  9. */
  10. public function testCreateZeroValue()
  11. {
  12. // Given
  13. $zero = Rational::createZeroValue();
  14. // Then
  15. $this->assertEquals(0, $zero->getWholePart());
  16. $this->assertEquals(0, $zero->getNumerator());
  17. $this->assertEquals(1, $zero->getDenominator());
  18. }
  19. /**
  20. * @test getWholePart
  21. * @dataProvider dataProviderForData
  22. * @param int $w
  23. * @param int $n
  24. * @param int $d
  25. * @param int $expectedWholePart
  26. */
  27. public function testGetWholePart($w, $n, $d, $expectedWholePart)
  28. {
  29. // Given
  30. $number = new Rational($w, $n, $d);
  31. // When
  32. $wholePart = $number->getWholePart();
  33. // Then
  34. $this->assertSame($expectedWholePart, $wholePart);
  35. }
  36. /**
  37. * @test getNumerator
  38. * @dataProvider dataProviderForData
  39. * @param int $w
  40. * @param int $n
  41. * @param int $d
  42. * @param int $_
  43. * @param int $expectedNumerator
  44. */
  45. public function testGetNumerator($w, $n, $d, $_, $expectedNumerator)
  46. {
  47. // Given
  48. $number = new Rational($w, $n, $d);
  49. // When
  50. $numerator = $number->getNumerator();
  51. // Then
  52. $this->assertSame($expectedNumerator, $numerator);
  53. }
  54. /**
  55. * @test getDenominator
  56. * @dataProvider dataProviderForData
  57. * @param int $w
  58. * @param int $n
  59. * @param int $d
  60. * @param int $_
  61. * @param int $__
  62. * @param int $expectedDenominator
  63. */
  64. public function testGetDenominator($w, $n, $d, $_, $__, $expectedDenominator)
  65. {
  66. // Given
  67. $number = new Rational($w, $n, $d);
  68. // When
  69. $denominator = $number->getDenominator();
  70. // Then
  71. $this->assertSame($expectedDenominator, $denominator);
  72. }
  73. /**
  74. * @return array
  75. */
  76. public function dataProviderForData(): array
  77. {
  78. return [
  79. [0, 0, 1, 0, 0, 1],
  80. [1, 0, 1, 1, 0, 1],
  81. [-1, 0, 1, -1, 0, 1],
  82. [0, 1, 1, 1, 0, 1],
  83. [0, -1, 1, -1, 0, 1],
  84. [0, 1, -1, -1, 0, 1],
  85. [-5, -1, 2, -5, -1, 2],
  86. [-5, 1, 2, -4, -1, 2],
  87. [0, 1, 2, 0, 1, 2],
  88. [0, -1, 2, 0, -1, 2],
  89. [0, 2, 3, 0, 2, 3],
  90. [0, 3, 4, 0, 3, 4],
  91. [0, 4, 5, 0, 4, 5],
  92. [0, 5, 6, 0, 5, 6],
  93. [0, 6, 7, 0, 6, 7],
  94. [0, 7, 8, 0, 7, 8],
  95. [0, 8, 9, 0, 8, 9],
  96. [0, 9, 10, 0, 9, 10],
  97. [0, 10, 21, 0, 10, 21],
  98. [0, 3, 2, 1, 1, 2],
  99. [0, 4, 2, 2, 0, 1],
  100. [0, 5, 2, 2, 1, 2],
  101. [0, 10, 2, 5, 0, 1],
  102. [0, 4, 3, 1, 1, 3],
  103. ];
  104. }
  105. /**
  106. * @test __toString returns the proper string representation of a rational number
  107. * @dataProvider dataProviderForToString
  108. * @param number $w
  109. * @param number $n
  110. * @param number $d
  111. * @param string $string
  112. */
  113. public function testToString($w, $n, $d, string $string)
  114. {
  115. // Given
  116. $number = new Rational($w, $n, $d);
  117. // When
  118. $stringRepresentation = (string) $number;
  119. // Then
  120. $this->assertSame($string, $stringRepresentation);
  121. }
  122. /**
  123. * @return array
  124. */
  125. public function dataProviderForToString(): array
  126. {
  127. return [
  128. [0, 0, 1, '0'],
  129. [1, 0, 1, '1'],
  130. [-1, 0, 1, '-1'],
  131. [0, 1, 1, '1'],
  132. [0, -1, 1, '-1'],
  133. [0, 1, -1, '-1'],
  134. [-5, -1, 2, '-5 ¹/₂'],
  135. [-5, 1, 2, '-4 ¹/₂'],
  136. [0, 1, 2, '¹/₂'],
  137. [0, -1, 2, '-¹/₂'],
  138. [0, 2, 3, '²/₃'],
  139. [0, 3, 4, '³/₄'],
  140. [0, 4, 5, '⁴/₅'],
  141. [0, 5, 6, '⁵/₆'],
  142. [0, 6, 7, '⁶/₇'],
  143. [0, 7, 8, '⁷/₈'],
  144. [0, 8, 9, '⁸/₉'],
  145. [0, 9, 10, '⁹/₁₀'],
  146. [0, 10, 21, '¹⁰/₂₁'],
  147. [0, 3, 2, '1 ¹/₂'],
  148. [0, 4, 2, '2'],
  149. [0, 5, 2, '2 ¹/₂'],
  150. [0, 10, 2, '5'],
  151. [0, 4, 3, '1 ¹/₃'],
  152. ];
  153. }
  154. /**
  155. * @test toFloat returns the correct floating point number
  156. * @dataProvider dataProviderForToFloat
  157. * @param number $w
  158. * @param number $n
  159. * @param number $d
  160. * @param float $float
  161. */
  162. public function testToFloat($w, $n, $d, float $float)
  163. {
  164. // Given
  165. $number = new Rational($w, $n, $d);
  166. // When
  167. $computedFloat = $number->toFloat();
  168. // Then
  169. $this->assertEquals($float, $computedFloat);
  170. }
  171. /**
  172. * @return array
  173. */
  174. public function dataProviderForToFloat(): array
  175. {
  176. return [
  177. [0, 0, 1, 0],
  178. [1, 0, 1, 1],
  179. [-1, 0, 1, -1],
  180. [0, 1, 1, 1],
  181. [0, -1, 1, -1],
  182. [0, 1, -1, -1],
  183. [-5, -1, 2, -5.5],
  184. [-5, 1, 2, -4.5],
  185. [0, 1, 2, .5],
  186. [0, -1, 2, -.5],
  187. ];
  188. }
  189. /**
  190. * @test normalization throws an Exception\BadDataException if the denominator is zero
  191. */
  192. public function testNormalizeException()
  193. {
  194. // Then
  195. $this->expectException(Exception\BadDataException::class);
  196. // When
  197. $number = new Rational(1, 1, 0);
  198. }
  199. /**
  200. * @test abs returns the correct number
  201. * @dataProvider dataProviderForAbs
  202. * @param number $w
  203. * @param number $n
  204. * @param number $d
  205. * @param array $result
  206. */
  207. public function testAbs($w, $n, $d, array $result)
  208. {
  209. // Given
  210. $number = new Rational($w, $n, $d);
  211. // When
  212. $result_rn = new Rational(...$result);
  213. // Then
  214. $this->assertTrue($number->abs()->equals($result_rn));
  215. }
  216. /**
  217. * @return array
  218. */
  219. public function dataProviderForAbs(): array
  220. {
  221. return [
  222. [0, 0, 1, [0, 0, 1]],
  223. [1, 0, 1, [1, 0, 1]],
  224. [-1, 0, 1, [1, 0, 1]],
  225. [0, 1, 1, [1, 0, 1]],
  226. [0, -1, 1, [1, 0, 1]],
  227. [0, 1, -1, [1, 0, 1]],
  228. [-5, -1, 2, [5, 1, 2]],
  229. [-5, 1, 2, [4, 1, 2]],
  230. [0, 1, 2, [0, 1, 2]],
  231. [0, -1, 2, [0, 1, 2]],
  232. ];
  233. }
  234. /**
  235. * @test inverse returns the correct number
  236. * @dataProvider dataProviderForInverse
  237. * @param number $w
  238. * @param number $n
  239. * @param number $d
  240. * @param array $result
  241. */
  242. public function testInverse($w, $n, $d, array $result)
  243. {
  244. // Given
  245. $number = new Rational($w, $n, $d);
  246. // When
  247. $result_rn = new Rational(...$result);
  248. // Then
  249. $this->assertTrue($number->inverse()->equals($result_rn));
  250. }
  251. /**
  252. * @return array
  253. */
  254. public function dataProviderForInverse(): array
  255. {
  256. return [
  257. [1, 0, 1, [1, 0, 1]],
  258. [-1, 0, 1, [-1, 0, 1]],
  259. [0, 1, 1, [1, 0, 1]],
  260. [0, -1, 1, [-1, 0, 1]],
  261. [0, 1, -1, [-1, 0, 1]],
  262. [-5, -1, 2, [0, -2, 11]],
  263. [-5, 1, 2, [0, -2, 9]],
  264. [0, 1, 2, [2, 0, 1]],
  265. [0, -1, 2, [-2, 0, 1]],
  266. ];
  267. }
  268. /**
  269. * @test add returns the correct number
  270. * @dataProvider dataProviderForAdd
  271. * @param array $rn1
  272. * @param array $rn2
  273. * @param array $expected
  274. * @throws \Exception
  275. */
  276. public function testAdd(array $rn1, array $rn2, array $expected)
  277. {
  278. // Given
  279. $rational_number_1 = new Rational($rn1[0], $rn1[1], $rn1[2]);
  280. $rational_number_2 = new Rational($rn2[0], $rn2[1], $rn2[2]);
  281. $expected_rn = new Rational(...$expected);
  282. // When
  283. $addition_result = $rational_number_1->add($rational_number_2);
  284. // Then
  285. $this->assertTrue($addition_result->equals($expected_rn));
  286. $this->assertSame($expected_rn->__toString(), $addition_result->__toString());
  287. $this->assertSame($expected_rn->toFloat(), $addition_result->toFloat());
  288. }
  289. /**
  290. * @return array
  291. */
  292. public function dataProviderForAdd(): array
  293. {
  294. return [
  295. [[0, 0, 1], [0, 0, 1], [0, 0, 1]],
  296. [[1, 0, 1], [1, 0, 1], [2, 0, 1]],
  297. [[1, 0, 1], [-1, 0, 1], [0, 0, 1]],
  298. [[-5, -1, 2], [0, 1, 2], [-5, 0, 1]],
  299. [[15, 0, 1], [0, 3, 4], [15, 3, 4]],
  300. ];
  301. }
  302. /**
  303. * @test subtract returns the correct number
  304. * @dataProvider dataProviderForSubtract
  305. * @param array $rn1
  306. * @param array $rn2
  307. * @param array $expected
  308. * @throws \Exception
  309. */
  310. public function testSubtract(array $rn1, array $rn2, array $expected)
  311. {
  312. // Given
  313. $rational_number_1 = new Rational($rn1[0], $rn1[1], $rn1[2]);
  314. $rational_number_2 = new Rational($rn2[0], $rn2[1], $rn2[2]);
  315. $expected_rn = new Rational(...$expected);
  316. // When
  317. $subtraction_result = $rational_number_1->subtract($rational_number_2);
  318. // Then
  319. $this->assertTrue($subtraction_result->equals($expected_rn));
  320. $this->assertSame($expected_rn->__toString(), $subtraction_result->__toString());
  321. $this->assertSame($expected_rn->toFloat(), $subtraction_result->toFloat());
  322. }
  323. /**
  324. * @return array
  325. */
  326. public function dataProviderForSubtract(): array
  327. {
  328. return [
  329. [[0, 0, 1], [0, 0, 1], [0, 0, 1]],
  330. [[1, 0, 1], [1, 0, 1], [0, 0, 1]],
  331. [[1, 0, 1], [-1, 0, 1], [2, 0, 1]],
  332. [[-5, -1, 2], [0, 1, 2], [-6, 0, 1]],
  333. [[15, 0, 1], [0, 3, 4], [14, 1, 4]],
  334. ];
  335. }
  336. /**
  337. * @test multiply returns the correct number
  338. * @dataProvider dataProviderForMultiply
  339. * @param array $rn1
  340. * @param array $rn2
  341. * @param array $expected
  342. * @throws \Exception
  343. */
  344. public function testMultiply(array $rn1, array $rn2, array $expected)
  345. {
  346. // Given
  347. $rational_number_1 = new Rational($rn1[0], $rn1[1], $rn1[2]);
  348. $rational_number_2 = new Rational($rn2[0], $rn2[1], $rn2[2]);
  349. $expected_rn = new Rational(...$expected);
  350. // When
  351. $multiplication_result = $rational_number_1->multiply($rational_number_2);
  352. // Then
  353. $this->assertTrue($multiplication_result->equals($expected_rn));
  354. $this->assertSame($expected_rn->__toString(), $multiplication_result->__toString());
  355. $this->assertSame($expected_rn->toFloat(), $multiplication_result->toFloat());
  356. }
  357. /**
  358. * @return array
  359. */
  360. public function dataProviderForMultiply(): array
  361. {
  362. return [
  363. [[0, 0, 1], [0, 0, 1], [0, 0, 1]],
  364. [[1, 0, 1], [1, 0, 1], [1, 0, 1]],
  365. [[1, 0, 1], [-1, 0, 1], [-1, 0, 1]],
  366. [[-5, -1, 2], [0, 1, 2], [-2, -3, 4]],
  367. [[15, 0, 1], [0, 3, 4], [11, 1, 4]],
  368. ];
  369. }
  370. /**
  371. * @test divide returns the correct number
  372. * @dataProvider dataProviderForDivide
  373. * @param array $rn1
  374. * @param array $rn2
  375. * @param array $expected
  376. * @throws \Exception
  377. */
  378. public function testDivide(array $rn1, array $rn2, array $expected)
  379. {
  380. // Given
  381. $rational_number_1 = new Rational($rn1[0], $rn1[1], $rn1[2]);
  382. $rational_number_2 = new Rational($rn2[0], $rn2[1], $rn2[2]);
  383. $expected_rn = new Rational(...$expected);
  384. // When
  385. $division_result = $rational_number_1->divide($rational_number_2);
  386. // Then
  387. $this->assertTrue($division_result->equals($expected_rn));
  388. $this->assertSame($expected_rn->__toString(), $division_result->__toString());
  389. $this->assertSame($expected_rn->toFloat(), $division_result->toFloat());
  390. }
  391. /**
  392. * @return array
  393. */
  394. public function dataProviderForDivide(): array
  395. {
  396. return [
  397. [[1, 0, 1], [1, 0, 1], [1, 0, 1]],
  398. [[1, 0, 1], [-1, 0, 1], [-1, 0, 1]],
  399. [[3, 4, 2], [3, 5, 2], [0, 10, 11]],
  400. [[-5, -1, 2], [0, 1, 2], [-11, 0, 1]],
  401. [[15, 0, 1], [0, 3, 4], [20, 0, 1]],
  402. ];
  403. }
  404. /**
  405. * @test add int returns the correct number
  406. * @dataProvider dataProviderForAddInt
  407. * @param array $rn
  408. * @param int $int
  409. * @param array $result
  410. * @throws \Exception
  411. */
  412. public function testAddInt(array $rn, int $int, array $result)
  413. {
  414. // Given
  415. $rational_number = new Rational(...$rn);
  416. $result_rn = new Rational(...$result);
  417. // When
  418. $additionResult = $rational_number->add($int);
  419. // Then
  420. $this->assertTrue($additionResult->equals($result_rn));
  421. }
  422. /**
  423. * @return array
  424. */
  425. public function dataProviderForAddInt(): array
  426. {
  427. return [
  428. [[1, 0, 1], 0, [1, 0, 1]],
  429. [[1, 0, 1], -1, [0, 0, 1]],
  430. [[3, 5, 2], 10, [15, 1, 2]],
  431. [[-5, -1, 2], -4, [-9, -1, 2]],
  432. [[15, 6, 13], -15, [0, 6, 13]],
  433. ];
  434. }
  435. /**
  436. * @test subtract int returns the correct number
  437. * @dataProvider dataProviderForSubtractInt
  438. * @param array $rn
  439. * @param int $int
  440. * @param array $result
  441. * @throws \Exception
  442. */
  443. public function testSubtractInt(array $rn, int $int, array $result)
  444. {
  445. // Given
  446. $rational_number = new Rational(...$rn);
  447. $result_rn = new Rational(...$result);
  448. // When
  449. $subtractionResult = $rational_number->subtract($int);
  450. // Then
  451. $this->assertTrue($subtractionResult->equals($result_rn));
  452. }
  453. /**
  454. * @return array
  455. */
  456. public function dataProviderForSubtractInt(): array
  457. {
  458. return [
  459. [[1, 0, 1], 0, [1, 0, 1]],
  460. [[1, 0, 1], -1, [2, 0, 1]],
  461. [[3, 5, 2], 10, [-4, -1, 2]],
  462. [[-5, -1, 2], -4, [-1, -1, 2]],
  463. [[15, 6, 13], -15, [30, 6, 13]],
  464. ];
  465. }
  466. /**
  467. * @test multiply int returns the correct number
  468. * @dataProvider dataProviderForMultiplyInt
  469. * @param array $rn
  470. * @param int $int
  471. * @param array $expected
  472. * @throws \Exception
  473. */
  474. public function testMultiplyInt(array $rn, int $int, array $expected)
  475. {
  476. // Given
  477. $rational_number = new Rational(...$rn);
  478. $expected_rn = new Rational(...$expected);
  479. // When
  480. $multiplication_result = $rational_number->multiply($int);
  481. // Then
  482. $this->assertTrue($multiplication_result->equals($expected_rn));
  483. }
  484. /**
  485. * @return array
  486. */
  487. public function dataProviderForMultiplyInt(): array
  488. {
  489. return [
  490. [[1, 0, 1], 0, [0, 0, 1]],
  491. [[1, 0, 1], -1, [-1, 0, 1]],
  492. [[3, 5, 2], 10, [55, 0, 1]],
  493. [[-5, -1, 2], -4, [22, 0, 1]],
  494. [[15, 6, 13], 2, [30, 12, 13]],
  495. ];
  496. }
  497. /**
  498. * @test divide int returns the correct number
  499. * @dataProvider dataProviderForDivideInt
  500. * @param array $rn
  501. * @param int $int
  502. * @param array $result
  503. * @throws \Exception
  504. */
  505. public function testDivideInt(array $rn, int $int, array $result)
  506. {
  507. // Given
  508. $rational_number = new Rational(...$rn);
  509. $result_rn = new Rational(...$result);
  510. // When
  511. $divisionResult = $rational_number->divide($int);
  512. // Then
  513. $this->assertTrue($divisionResult->equals($result_rn));
  514. }
  515. /**
  516. * @return array
  517. */
  518. public function dataProviderForDivideInt(): array
  519. {
  520. return [
  521. [[1, 0, 1], 1, [1, 0, 1]],
  522. [[1, 0, 1], -1, [-1, 0, 1]],
  523. [[3, 5, 2], 10, [0, 11, 20]],
  524. [[-5, -1, 2], -4, [1, 3, 8]],
  525. [[15, 6, 13], -15, [-1, -2, 65]],
  526. ];
  527. }
  528. /**
  529. * @test pow returns the correct number
  530. * @dataProvider dataProviderForPow
  531. * @param array $rn
  532. * @param int $int
  533. * @param array $result
  534. * @throws \Exception
  535. */
  536. public function testPow(array $rn, int $int, array $result)
  537. {
  538. // Given
  539. $rational_number = new Rational(...$rn);
  540. $result_rn = new Rational(...$result);
  541. // When
  542. $powResult = $rational_number->pow($int);
  543. // Then
  544. $this->assertTrue($powResult->equals($result_rn));
  545. }
  546. /**
  547. * @return array
  548. */
  549. public function dataProviderForPow(): array
  550. {
  551. return [
  552. [[1, 0, 1], -1, [1, 0, 1]],
  553. [[1, 0, 1], 0, [1, 0, 1]],
  554. [[1, 0, 1], 1, [1, 0, 1]],
  555. [[1, 0, 1], 2, [1, 0, 1]],
  556. [[0, 0, 1], 0, [1, 0, 1]],
  557. [[0, 0, 1], 10, [0, 0, 1]],
  558. [[0, 1, 2], 0, [0, 1, 1]],
  559. [[1, 0, 1], 0, [1, 0, 1]],
  560. [[0, 1, 1], 0, [0, 1, 1]],
  561. [[0, 1, 2], -1, [0, 2, 1]],
  562. [[0, 1, 2], -2, [0, 4, 1]],
  563. [[4, 5, 2], -2, [0, 4, 169]],
  564. [[3, 5, 2], 5, [5032, 27, 32]],
  565. ];
  566. }
  567. /**
  568. * @test The inverse of zero throws an exception.
  569. * @throws \Exception
  570. */
  571. public function testInverseException()
  572. {
  573. // Given
  574. $number = new Rational(0, 0, 1);
  575. // Then
  576. $this->expectException(Exception\DivisionByZeroException::class);
  577. // When
  578. $number->inverse();
  579. }
  580. /**
  581. * @test Adding a float throws an exception
  582. * @throws \Exception
  583. */
  584. public function testAddException()
  585. {
  586. $this->expectException(Exception\IncorrectTypeException::class);
  587. $number = new Rational(1, 0, 1);
  588. $number->add(1.5);
  589. }
  590. /**
  591. * @test Subtracting a float throws an exception
  592. * @throws \Exception
  593. */
  594. public function testSubtractException()
  595. {
  596. // Given
  597. $number = new Rational(1, 0, 1);
  598. // Then
  599. $this->expectException(Exception\IncorrectTypeException::class);
  600. // When
  601. $number->subtract(1.5);
  602. }
  603. /**
  604. * @test Multiplying a float throws an exception
  605. * @throws \Exception
  606. */
  607. public function testMultiplyException()
  608. {
  609. // Given
  610. $number = new Rational(1, 0, 1);
  611. // Then
  612. $this->expectException(Exception\IncorrectTypeException::class);
  613. // When
  614. $number->multiply(1.5);
  615. }
  616. /**
  617. * @test Dividing a float throws an exception
  618. * @throws \Exception
  619. */
  620. public function testDivideException()
  621. {
  622. // Given
  623. $number = new Rational(1, 0, 1);
  624. // Then
  625. $this->expectException(Exception\IncorrectTypeException::class);
  626. // When
  627. $number->divide(1.5);
  628. }
  629. /**
  630. * @test Raising zero to a negative exponent throws an exception.
  631. * @throws \Exception
  632. */
  633. public function testPowException()
  634. {
  635. // Given
  636. $number = new Rational(0, 0, 1);
  637. // Then
  638. $this->expectException(Exception\DivisionByZeroException::class);
  639. // When
  640. $number->pow(-2);
  641. }
  642. }