test_rules_fractions.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. # This file is part of TRS (http://math.kompiler.org)
  2. #
  3. # TRS is free software: you can redistribute it and/or modify it under the
  4. # terms of the GNU Affero General Public License as published by the Free
  5. # Software Foundation, either version 3 of the License, or (at your option) any
  6. # later version.
  7. #
  8. # TRS is distributed in the hope that it will be useful, but WITHOUT ANY
  9. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  10. # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  11. # details.
  12. #
  13. # You should have received a copy of the GNU Affero General Public License
  14. # along with TRS. If not, see <http://www.gnu.org/licenses/>.
  15. from src.rules.fractions import match_constant_division, division_by_one, \
  16. division_of_zero, division_by_self, match_add_fractions, \
  17. equalize_denominators, add_nominators, match_multiply_fractions, \
  18. multiply_fractions, multiply_with_fraction, match_divide_fractions, \
  19. divide_fraction, divide_by_fraction, match_extract_fraction_terms, \
  20. constant_to_fraction, extract_nominator_term, extract_fraction_terms, \
  21. match_division_in_denominator, multiply_with_term, \
  22. divide_fraction_by_term, match_combine_fractions, combine_fractions, \
  23. match_remove_division_negation, remove_division_negation, \
  24. match_fraction_in_division, fraction_in_division
  25. from src.node import ExpressionNode as N, Scope, OP_MUL
  26. from src.possibilities import Possibility as P
  27. from tests.rulestestcase import RulesTestCase, tree
  28. class TestRulesFractions(RulesTestCase):
  29. def test_match_constant_division(self):
  30. a, zero = tree('a,0')
  31. root = a / zero
  32. with self.assertRaises(ZeroDivisionError) as cm:
  33. match_constant_division(root)
  34. self.assertEqual(cm.exception.message, 'Division by zero: a / 0.')
  35. root = a / 1
  36. possibilities = match_constant_division(root)
  37. self.assertEqualPos(possibilities, [P(root, division_by_one, (a,))])
  38. root = zero / a
  39. possibilities = match_constant_division(root)
  40. self.assertEqualPos(possibilities, [P(root, division_of_zero, (a,))])
  41. root = a / a
  42. possibilities = match_constant_division(root)
  43. self.assertEqualPos(possibilities, [P(root, division_by_self, (a,))])
  44. def test_division_by_one(self):
  45. a = tree('a')
  46. root = a / 1
  47. self.assertEqualNodes(division_by_one(root, (a,)), a)
  48. def test_division_of_zero(self):
  49. a, zero = tree('a,0')
  50. root = zero / a
  51. self.assertEqualNodes(division_of_zero(root, ()), zero)
  52. def test_division_by_self(self):
  53. a, one = tree('a,1')
  54. root = a / a
  55. self.assertEqualNodes(division_by_self(root, ()), one)
  56. def test_match_add_fractions(self):
  57. a, b, c, l1, l2, l3, l4 = tree('a,b,c,1,2,3,4')
  58. n0, n1 = root = l1 / l2 + l3 / l4
  59. possibilities = match_add_fractions(root)
  60. self.assertEqualPos(possibilities,
  61. [P(root, equalize_denominators, (Scope(root), n0, n1, 4)),
  62. P(root, equalize_denominators, (Scope(root), n0, n1, 8))])
  63. (((n0, n1), n2), n3), n4 = root = a + l1 / l2 + b + l3 / l4 + c
  64. possibilities = match_add_fractions(root)
  65. self.assertEqualPos(possibilities,
  66. [P(root, equalize_denominators, (Scope(root), n1, n3, 4)),
  67. P(root, equalize_denominators, (Scope(root), n1, n3, 8))])
  68. n0, n1 = root = l2 / l4 + l3 / l4
  69. possibilities = match_add_fractions(root)
  70. self.assertEqualPos(possibilities,
  71. [P(root, add_nominators, (Scope(root), n0, n1))])
  72. (((n0, n1), n2), n3), n4 = root = a + l2 / l4 + b + l3 / l4 + c
  73. possibilities = match_add_fractions(root)
  74. self.assertEqualPos(possibilities,
  75. [P(root, add_nominators, (Scope(root), n1, n3))])
  76. def test_match_add_fractions_constant_to_fraction(self):
  77. l23, l1 = root = tree('2 / 3 + 1')
  78. self.assertEqualPos(match_add_fractions(root),
  79. [P(root, constant_to_fraction, (Scope(root), l23, l1))])
  80. def test_add_fractions_with_negation(self):
  81. a, b, c, l1, l2, l3, l4 = tree('a,b,c,1,2,3,4')
  82. (((n0, n1), n2), n3), n4 = root = a + l2 / l2 + b + (-l3 / l4) + c
  83. self.assertEqualPos(match_add_fractions(root),
  84. [P(root, equalize_denominators, (Scope(root), n1, n3, 4)),
  85. P(root, equalize_denominators, (Scope(root), n1, n3, 8))])
  86. n0, n1 = root = l1 / l2 + l4 / l3
  87. self.assertEqualPos(match_add_fractions(root),
  88. [P(root, equalize_denominators, (Scope(root), n0, n1, 6))])
  89. (((n0, n1), n2), n3), n4 = root = a + l2 / l4 + b + (-l3 / l4) + c
  90. self.assertEqualPos(match_add_fractions(root),
  91. [P(root, add_nominators, (Scope(root), n1, n3))])
  92. def test_equalize_denominators(self):
  93. a, b, l1, l2, l3, l4 = tree('a,b,1,2,3,4')
  94. n0, n1 = root = l1 / l2 + l3 / l4
  95. self.assertEqualNodes(equalize_denominators(root,
  96. (Scope(root), n0, n1, 4)), l2 / l4 + l3 / l4)
  97. n0, n1 = root = a / l2 + b / l4
  98. self.assertEqualNodes(equalize_denominators(root,
  99. (Scope(root), n0, n1, 4)), (l2 * a) / l4 + b /
  100. l4)
  101. #2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
  102. n0, n1 = root = l1 / l2 + (-l3 / l4)
  103. self.assertEqualNodes(equalize_denominators(root,
  104. (Scope(root), n0, n1, 4)), l2 / l4 + (-l3 / l4))
  105. #2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
  106. n0, n1 = root = a / l2 + (-b / l4)
  107. self.assertEqualNodes(equalize_denominators(root,
  108. (Scope(root), n0, n1, 4)), (l2 * a) / l4 + (-b / l4))
  109. def test_add_nominators(self):
  110. a, b, c = tree('a,b,c')
  111. n0, n1 = root = a / b + c / b
  112. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  113. (a + c) / b)
  114. n0, n1 = root = a / b + -c / b
  115. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  116. (a + -c) / b)
  117. n0, n1 = root = a / b + -(c / b)
  118. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  119. (a + -c) / b)
  120. n0, n1 = root = a / -b + c / -b
  121. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  122. (a + c) / -b)
  123. n0, n1 = root = a / -b + -c / -b
  124. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  125. (a + -c) / -b)
  126. def test_constant_to_fraction(self):
  127. root, e = tree('2 / 3 + 1, 2 / 3 + (3 * 1) / 3')
  128. l23, l1 = root
  129. self.assertEqual(constant_to_fraction(root, (Scope(root), l23, l1)), e)
  130. def test_match_multiply_fractions(self):
  131. (a, b), (c, d) = ab, cd = root = tree('a / b * (c / d)')
  132. self.assertEqualPos(match_multiply_fractions(root),
  133. [P(root, multiply_fractions, (Scope(root), ab, cd))])
  134. (ab, e), cd = root = tree('4 / b * 2 * (3 / d)')
  135. self.assertEqualPos(match_multiply_fractions(root),
  136. [P(root, multiply_fractions, (Scope(root), ab, cd)),
  137. P(root, multiply_with_fraction, (Scope(root), ab, e)),
  138. P(root, multiply_with_fraction, (Scope(root), cd, e))])
  139. ab, c = root = tree('1 / sqrt(3) * 2')
  140. self.assertEqualPos(match_multiply_fractions(root),
  141. [P(root, multiply_with_fraction, (Scope(root), ab, c))])
  142. def test_multiply_fractions(self):
  143. (a, b), (c, d) = ab, cd = root = tree('a / b * (c / d)')
  144. self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)),
  145. a * c / (b * d))
  146. (ab, e), cd = root = tree('a / b * e * (c / d)')
  147. self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)),
  148. a * c / (b * d) * e)
  149. def test_match_divide_fractions(self):
  150. (a, b), c = root = tree('a / b / c')
  151. self.assertEqualPos(match_divide_fractions(root),
  152. [P(root, divide_fraction, (a, b, c))])
  153. root = tree('a / (b / c)')
  154. self.assertEqualPos(match_divide_fractions(root),
  155. [P(root, divide_by_fraction, (a, b, c))])
  156. def test_divide_fraction(self):
  157. (a, b), c = root = tree('a / b / c')
  158. self.assertEqual(divide_fraction(root, (a, b, c)), a / (b * c))
  159. (a, b), c = root = tree('-a / b / c')
  160. self.assertEqual(divide_fraction(root, (a, b, c)), -(a / (b * c)))
  161. root = tree('a / b / -c')
  162. self.assertEqual(divide_fraction(root, (a, b, c)), a / (b * -c))
  163. def test_divide_by_fraction(self):
  164. a, (b, c) = root = tree('a / (b / c)')
  165. self.assertEqual(divide_by_fraction(root, (a, b, c)), a * c / b)
  166. a, (b, c) = root = tree('-a / (b / c)')
  167. self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
  168. root = tree('a / -(b / c)')
  169. self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
  170. def test_match_extract_fraction_terms(self):
  171. root, a, b, c = tree('(ab) / (ca), a, b, c')
  172. n, d = root
  173. self.assertEqualPos(match_extract_fraction_terms(root),
  174. [P(root, divide_fraction_by_term, (Scope(n), Scope(d), a, a))])
  175. lscp = lambda l: Scope(N(OP_MUL, l))
  176. n, d = root = tree('(ab) / a')
  177. self.assertEqualPos(match_extract_fraction_terms(root),
  178. [P(root, divide_fraction_by_term, (Scope(n), lscp(d), a, a))])
  179. n, d = root = tree('a / (ab)')
  180. self.assertEqualPos(match_extract_fraction_terms(root),
  181. [P(root, divide_fraction_by_term, (lscp(n), Scope(d), a, a))])
  182. n, d = root = tree('(abc) / (cba)')
  183. self.assertEqualPos(match_extract_fraction_terms(root),
  184. [P(root, divide_fraction_by_term, (Scope(n), Scope(d), a, a)),
  185. P(root, divide_fraction_by_term, (Scope(n), Scope(d), b, b)),
  186. P(root, divide_fraction_by_term, (Scope(n), Scope(d), c, c))])
  187. root = tree('a / a')
  188. self.assertEqualPos(match_extract_fraction_terms(root), [])
  189. (ap, b), aq = n, d = root = tree('(a ^ p * b) / a ^ q')
  190. self.assertEqualPos(match_extract_fraction_terms(root),
  191. [P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, aq))])
  192. (a, b), aq = n, d = root = tree('(ab) / a ^ q')
  193. self.assertEqualPos(match_extract_fraction_terms(root),
  194. [P(root, extract_fraction_terms, (Scope(n), lscp(d), a, aq))])
  195. (ap, b), a = n, d = root = tree('(a ^ p * b) / a')
  196. self.assertEqualPos(match_extract_fraction_terms(root),
  197. [P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, a))])
  198. (l2, a), l3 = n, d = root = tree('(2a) / 3')
  199. self.assertEqualPos(match_extract_fraction_terms(root),
  200. [P(root, extract_nominator_term, (2, a))])
  201. a, l3 = n, d = root = tree('a / 3')
  202. self.assertEqualPos(match_extract_fraction_terms(root),
  203. [P(root, extract_nominator_term, (1, a))])
  204. root = tree('(2 * 4) / 3')
  205. self.assertEqualPos(match_extract_fraction_terms(root), [])
  206. n, d = root = tree('(2a) / 2')
  207. self.assertEqualPos(match_extract_fraction_terms(root),
  208. [P(root, extract_nominator_term, (2, a)),
  209. P(root, divide_fraction_by_term, (Scope(n), lscp(d), 2, 2))])
  210. def test_extract_nominator_term(self):
  211. root, expect = tree('(2a) / 3, 2 / 3 * a')
  212. l2, a = root[0]
  213. self.assertEqual(extract_nominator_term(root, (l2, a)), expect)
  214. root, expect, l1 = tree('a / 3, 1 / 3 * a, 1')
  215. self.assertEqual(extract_nominator_term(root, (l1, root[0])), expect)
  216. def test_extract_fraction_terms_basic(self):
  217. root, expect = tree('(ab) / (ca), a / a * b / c')
  218. n, d = root
  219. self.assertEqual(extract_fraction_terms(root,
  220. (Scope(n), Scope(d), n[0], d[1])), expect)
  221. def test_extract_fraction_terms_leaf(self):
  222. root, expect = tree('(ba) / a, a / a * b / 1')
  223. n, d = root
  224. self.assertEqual(extract_fraction_terms(root,
  225. (Scope(n), Scope(N(OP_MUL, d)), n[1], d)), expect)
  226. root, expect = tree('a / (ab), a / a * 1 / b')
  227. n, d = root
  228. self.assertEqual(extract_fraction_terms(root,
  229. (Scope(N(OP_MUL, n)), Scope(d), n, d[0])), expect)
  230. def test_extract_fraction_terms_chain(self):
  231. self.assertRewrite([
  232. '(a ^ 3 * 4) / (a ^ 2 * 5)',
  233. 'a ^ 3 / a ^ 2 * 4 / 5',
  234. 'a ^ (3 - 2)4 / 5',
  235. 'a ^ 1 * 4 / 5',
  236. 'a * 4 / 5',
  237. # FIXME: '4 / 5 * a',
  238. ])
  239. def test_divide_fraction_by_term(self):
  240. (ab, a), expect = root = tree('(ab) / a, b')
  241. args = Scope(ab), Scope(N(OP_MUL, a)), ab[0], a
  242. self.assertEqual(divide_fraction_by_term(root, args), expect)
  243. def test_match_division_in_denominator(self):
  244. a, ((b, c), d) = root = tree('a / (b / c + d)')
  245. self.assertEqualPos(match_division_in_denominator(root),
  246. [P(root, multiply_with_term, (c,))])
  247. a, ((d, (b, c)), e) = root = tree('a / (d + b / c + e)')
  248. self.assertEqualPos(match_division_in_denominator(root),
  249. [P(root, multiply_with_term, (c,))])
  250. def test_multiply_with_term_chain(self):
  251. self.assertRewrite([
  252. '1 / (1 / b - 1 / a)',
  253. '1 / ((1 * -a) / (b * -a) + (b * 1) / (b * -a))',
  254. '1 / ((1 * -a + b * 1) / (b * -a))',
  255. '1 / ((-a + b * 1) / (b * -a))',
  256. '1 / ((-a + b) / (b * -a))',
  257. '1 / ((-a + b) / (-ba))',
  258. '1 / (-(-a + b) / (ba))',
  259. '1 / ((--a - b) / (ba))',
  260. '1 / ((a - b) / (ba))',
  261. '(1ba) / (a - b)',
  262. '(ba) / (a - b)',
  263. '(ab) / (a - b)',
  264. ])
  265. def test_match_combine_fractions(self):
  266. ab, cd = root = tree('a / b + c / d')
  267. self.assertEqualPos(match_combine_fractions(root),
  268. [P(root, combine_fractions, (Scope(root), ab, cd))])
  269. def test_combine_fractions(self):
  270. (a, b), (c, d) = ab, cd = root = tree('a / b + c / d')
  271. self.assertEqual(combine_fractions(root, (Scope(root), ab, cd)),
  272. a * d / (b * d) + b * c / (b * d))
  273. def test_match_remove_division_negation(self):
  274. root = tree('-(-a + b) / c')
  275. self.assertEqualPos(match_remove_division_negation(root),
  276. [P(root, remove_division_negation, (True, root[0]))])
  277. root = tree('-a / (-b + c)')
  278. self.assertEqualPos(match_remove_division_negation(root),
  279. [P(root, remove_division_negation, (False, root[1]))])
  280. def test_remove_division_negation(self):
  281. (a, b), c = root = tree('-(-a + b) / c')
  282. self.assertEqual(remove_division_negation(root, (True, root[0])),
  283. (-a - b) / c)
  284. a, (b, c) = root = tree('-a / (-b + c)')
  285. self.assertEqual(remove_division_negation(root, (False, root[1])),
  286. +a / (-b - c))
  287. def test_match_fraction_in_division(self):
  288. (fr, b), c = root = tree('(1 / a * b) / c')
  289. self.assertEqualPos(match_fraction_in_division(root),
  290. [P(root, fraction_in_division, (True, Scope(root[0]), fr))])
  291. c, (fr, b) = root = tree('c / (1 / a * b)')
  292. self.assertEqualPos(match_fraction_in_division(root),
  293. [P(root, fraction_in_division, (False, Scope(root[1]), fr))])
  294. (fr0, b), (fr1, d) = root = tree('(1 / a * b) / (1 / c * d)')
  295. self.assertEqualPos(match_fraction_in_division(root),
  296. [P(root, fraction_in_division, (True, Scope(root[0]), fr0)),
  297. P(root, fraction_in_division, (False, Scope(root[1]), fr1))])
  298. def test_fraction_in_division(self):
  299. root, expected = tree('(1 / a * b) / c, b / (ac)')
  300. self.assertEqual(fraction_in_division(root,
  301. (True, Scope(root[0]), root[0][0])), expected)
  302. root, expected = tree('c / (1 / a * b), (ac) / b')
  303. self.assertEqual(fraction_in_division(root,
  304. (False, Scope(root[1]), root[1][0])), expected)