test_rules_fractions.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. from src.rules.fractions import match_constant_division, division_by_one, \
  2. division_of_zero, division_by_self, match_add_fractions, \
  3. equalize_denominators, add_nominators, match_multiply_fractions, \
  4. multiply_fractions, multiply_with_fraction, match_divide_fractions, \
  5. divide_fraction, divide_by_fraction, match_extract_fraction_terms, \
  6. constant_to_fraction, extract_nominator_term, extract_fraction_terms, \
  7. match_division_in_denominator, multiply_with_term, \
  8. divide_fraction_by_term
  9. from src.node import ExpressionNode as N, Scope, OP_MUL
  10. from src.possibilities import Possibility as P
  11. from tests.rulestestcase import RulesTestCase, tree
  12. class TestRulesFractions(RulesTestCase):
  13. def test_match_constant_division(self):
  14. a, zero = tree('a,0')
  15. root = a / zero
  16. with self.assertRaises(ZeroDivisionError) as cm:
  17. match_constant_division(root)
  18. self.assertEqual(cm.exception.message, 'Division by zero: a / 0.')
  19. root = a / 1
  20. possibilities = match_constant_division(root)
  21. self.assertEqualPos(possibilities, [P(root, division_by_one, (a,))])
  22. root = zero / a
  23. possibilities = match_constant_division(root)
  24. self.assertEqualPos(possibilities, [P(root, division_of_zero, (a,))])
  25. root = a / a
  26. possibilities = match_constant_division(root)
  27. self.assertEqualPos(possibilities, [P(root, division_by_self, (a,))])
  28. def test_division_by_one(self):
  29. a = tree('a')
  30. root = a / 1
  31. self.assertEqualNodes(division_by_one(root, (a,)), a)
  32. def test_division_of_zero(self):
  33. a, zero = tree('a,0')
  34. root = zero / a
  35. self.assertEqualNodes(division_of_zero(root, ()), zero)
  36. def test_division_by_self(self):
  37. a, one = tree('a,1')
  38. root = a / a
  39. self.assertEqualNodes(division_by_self(root, ()), one)
  40. def test_match_add_fractions(self):
  41. a, b, c, l1, l2, l3, l4 = tree('a,b,c,1,2,3,4')
  42. n0, n1 = root = l1 / l2 + l3 / l4
  43. possibilities = match_add_fractions(root)
  44. self.assertEqualPos(possibilities,
  45. [P(root, equalize_denominators, (Scope(root), n0, n1, 4)),
  46. P(root, equalize_denominators, (Scope(root), n0, n1, 8))])
  47. (((n0, n1), n2), n3), n4 = root = a + l1 / l2 + b + l3 / l4 + c
  48. possibilities = match_add_fractions(root)
  49. self.assertEqualPos(possibilities,
  50. [P(root, equalize_denominators, (Scope(root), n1, n3, 4)),
  51. P(root, equalize_denominators, (Scope(root), n1, n3, 8))])
  52. n0, n1 = root = l2 / l4 + l3 / l4
  53. possibilities = match_add_fractions(root)
  54. self.assertEqualPos(possibilities,
  55. [P(root, add_nominators, (Scope(root), n0, n1))])
  56. (((n0, n1), n2), n3), n4 = root = a + l2 / l4 + b + l3 / l4 + c
  57. possibilities = match_add_fractions(root)
  58. self.assertEqualPos(possibilities,
  59. [P(root, add_nominators, (Scope(root), n1, n3))])
  60. def test_match_add_fractions_constant_to_fraction(self):
  61. l23, l1 = root = tree('2 / 3 + 1')
  62. self.assertEqualPos(match_add_fractions(root),
  63. [P(root, constant_to_fraction, (Scope(root), l23, l1))])
  64. def test_add_fractions_with_negation(self):
  65. a, b, c, l1, l2, l3, l4 = tree('a,b,c,1,2,3,4')
  66. (((n0, n1), n2), n3), n4 = root = a + l2 / l2 + b + (-l3 / l4) + c
  67. self.assertEqualPos(match_add_fractions(root),
  68. [P(root, equalize_denominators, (Scope(root), n1, n3, 4)),
  69. P(root, equalize_denominators, (Scope(root), n1, n3, 8))])
  70. n0, n1 = root = l1 / l2 + l4 / l3
  71. self.assertEqualPos(match_add_fractions(root),
  72. [P(root, equalize_denominators, (Scope(root), n0, n1, 6))])
  73. (((n0, n1), n2), n3), n4 = root = a + l2 / l4 + b + (-l3 / l4) + c
  74. self.assertEqualPos(match_add_fractions(root),
  75. [P(root, add_nominators, (Scope(root), n1, n3))])
  76. def test_equalize_denominators(self):
  77. a, b, l1, l2, l3, l4 = tree('a,b,1,2,3,4')
  78. n0, n1 = root = l1 / l2 + l3 / l4
  79. self.assertEqualNodes(equalize_denominators(root,
  80. (Scope(root), n0, n1, 4)), l2 / l4 + l3 / l4)
  81. n0, n1 = root = a / l2 + b / l4
  82. self.assertEqualNodes(equalize_denominators(root,
  83. (Scope(root), n0, n1, 4)), (l2 * a) / l4 + b /
  84. l4)
  85. #2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
  86. n0, n1 = root = l1 / l2 + (-l3 / l4)
  87. self.assertEqualNodes(equalize_denominators(root,
  88. (Scope(root), n0, n1, 4)), l2 / l4 + (-l3 / l4))
  89. #2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
  90. n0, n1 = root = a / l2 + (-b / l4)
  91. self.assertEqualNodes(equalize_denominators(root,
  92. (Scope(root), n0, n1, 4)), (l2 * a) / l4 + (-b / l4))
  93. def test_add_nominators(self):
  94. a, b, c = tree('a,b,c')
  95. n0, n1 = root = a / b + c / b
  96. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  97. (a + c) / b)
  98. n0, n1 = root = a / b + -c / b
  99. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  100. (a + -c) / b)
  101. n0, n1 = root = a / b + -(c / b)
  102. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  103. (a + -c) / b)
  104. n0, n1 = root = a / -b + c / -b
  105. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  106. (a + c) / -b)
  107. n0, n1 = root = a / -b + -c / -b
  108. self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
  109. (a + -c) / -b)
  110. def test_constant_to_fraction(self):
  111. root, e = tree('2 / 3 + 1, 2 / 3 + (3 * 1) / 3')
  112. l23, l1 = root
  113. self.assertEqual(constant_to_fraction(root, (Scope(root), l23, l1)), e)
  114. def test_match_multiply_fractions(self):
  115. (a, b), (c, d) = ab, cd = root = tree('a / b * (c / d)')
  116. self.assertEqualPos(match_multiply_fractions(root),
  117. [P(root, multiply_fractions, (Scope(root), ab, cd))])
  118. (ab, e), cd = root = tree('4 / b * 2 * (3 / d)')
  119. self.assertEqualPos(match_multiply_fractions(root),
  120. [P(root, multiply_fractions, (Scope(root), ab, cd)),
  121. P(root, multiply_with_fraction, (Scope(root), ab, e)),
  122. P(root, multiply_with_fraction, (Scope(root), cd, e))])
  123. ab, c = root = tree('1 / sqrt(3) * 2')
  124. self.assertEqualPos(match_multiply_fractions(root),
  125. [P(root, multiply_with_fraction, (Scope(root), ab, c))])
  126. def test_multiply_fractions(self):
  127. (a, b), (c, d) = ab, cd = root = tree('a / b * (c / d)')
  128. self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)),
  129. a * c / (b * d))
  130. (ab, e), cd = root = tree('a / b * e * (c / d)')
  131. self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)),
  132. a * c / (b * d) * e)
  133. def test_match_divide_fractions(self):
  134. (a, b), c = root = tree('a / b / c')
  135. self.assertEqualPos(match_divide_fractions(root),
  136. [P(root, divide_fraction, (a, b, c))])
  137. root = tree('a / (b / c)')
  138. self.assertEqualPos(match_divide_fractions(root),
  139. [P(root, divide_by_fraction, (a, b, c))])
  140. def test_divide_fraction(self):
  141. (a, b), c = root = tree('a / b / c')
  142. self.assertEqual(divide_fraction(root, (a, b, c)), a / (b * c))
  143. (a, b), c = root = tree('-a / b / c')
  144. self.assertEqual(divide_fraction(root, (a, b, c)), -(a / (b * c)))
  145. root = tree('a / b / -c')
  146. self.assertEqual(divide_fraction(root, (a, b, c)), a / (b * -c))
  147. def test_divide_by_fraction(self):
  148. a, (b, c) = root = tree('a / (b / c)')
  149. self.assertEqual(divide_by_fraction(root, (a, b, c)), a * c / b)
  150. a, (b, c) = root = tree('-a / (b / c)')
  151. self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
  152. root = tree('a / -(b / c)')
  153. self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
  154. def test_match_extract_fraction_terms(self):
  155. root, a, b, c = tree('(ab) / (ca), a, b, c')
  156. n, d = root
  157. self.assertEqualPos(match_extract_fraction_terms(root),
  158. [P(root, divide_fraction_by_term, (Scope(n), Scope(d), a, a))])
  159. lscp = lambda l: Scope(N(OP_MUL, l))
  160. n, d = root = tree('(ab) / a')
  161. self.assertEqualPos(match_extract_fraction_terms(root),
  162. [P(root, divide_fraction_by_term, (Scope(n), lscp(d), a, a))])
  163. n, d = root = tree('a / (ab)')
  164. self.assertEqualPos(match_extract_fraction_terms(root),
  165. [P(root, divide_fraction_by_term, (lscp(n), Scope(d), a, a))])
  166. n, d = root = tree('(abc) / (cba)')
  167. self.assertEqualPos(match_extract_fraction_terms(root),
  168. [P(root, divide_fraction_by_term, (Scope(n), Scope(d), a, a)),
  169. P(root, divide_fraction_by_term, (Scope(n), Scope(d), b, b)),
  170. P(root, divide_fraction_by_term, (Scope(n), Scope(d), c, c))])
  171. root = tree('a / a')
  172. self.assertEqualPos(match_extract_fraction_terms(root), [])
  173. (ap, b), aq = n, d = root = tree('(a ^ p * b) / a ^ q')
  174. self.assertEqualPos(match_extract_fraction_terms(root),
  175. [P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, aq))])
  176. (a, b), aq = n, d = root = tree('(ab) / a ^ q')
  177. self.assertEqualPos(match_extract_fraction_terms(root),
  178. [P(root, extract_fraction_terms, (Scope(n), lscp(d), a, aq))])
  179. (ap, b), a = n, d = root = tree('(a ^ p * b) / a')
  180. self.assertEqualPos(match_extract_fraction_terms(root),
  181. [P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, a))])
  182. (l2, a), l3 = n, d = root = tree('(2a) / 3')
  183. self.assertEqualPos(match_extract_fraction_terms(root),
  184. [P(root, extract_nominator_term, (2, a))])
  185. a, l3 = n, d = root = tree('a / 3')
  186. self.assertEqualPos(match_extract_fraction_terms(root),
  187. [P(root, extract_nominator_term, (1, a))])
  188. root = tree('(2 * 4) / 3')
  189. self.assertEqualPos(match_extract_fraction_terms(root), [])
  190. n, d = root = tree('(2a) / 2')
  191. self.assertEqualPos(match_extract_fraction_terms(root),
  192. [P(root, extract_nominator_term, (2, a)),
  193. P(root, divide_fraction_by_term, (Scope(n), lscp(d), 2, 2))])
  194. def test_extract_nominator_term(self):
  195. root, expect = tree('(2a) / 3, 2 / 3 * a')
  196. l2, a = root[0]
  197. self.assertEqual(extract_nominator_term(root, (l2, a)), expect)
  198. root, expect, l1 = tree('a / 3, 1 / 3 * a, 1')
  199. self.assertEqual(extract_nominator_term(root, (l1, root[0])), expect)
  200. def test_extract_fraction_terms_basic(self):
  201. root, expect = tree('(ab) / (ca), a / a * b / c')
  202. n, d = root
  203. self.assertEqual(extract_fraction_terms(root,
  204. (Scope(n), Scope(d), n[0], d[1])), expect)
  205. def test_extract_fraction_terms_leaf(self):
  206. root, expect = tree('(ba) / a, a / a * b / 1')
  207. n, d = root
  208. self.assertEqual(extract_fraction_terms(root,
  209. (Scope(n), Scope(N(OP_MUL, d)), n[1], d)), expect)
  210. root, expect = tree('a / (ab), a / a * 1 / b')
  211. n, d = root
  212. self.assertEqual(extract_fraction_terms(root,
  213. (Scope(N(OP_MUL, n)), Scope(d), n, d[0])), expect)
  214. def test_extract_fraction_terms_chain(self):
  215. self.assertRewrite([
  216. '(a ^ 3 * 4) / (a ^ 2 * 5)',
  217. 'a ^ 3 / a ^ 2 * 4 / 5',
  218. 'a ^ (3 - 2)4 / 5',
  219. 'a ^ 1 * 4 / 5',
  220. 'a * 4 / 5',
  221. # FIXME: '4 / 5 * a',
  222. ])
  223. def test_divide_fraction_by_term(self):
  224. (ab, a), expect = root = tree('(ab) / a, b')
  225. args = Scope(ab), Scope(N(OP_MUL, a)), ab[0], a
  226. self.assertEqual(divide_fraction_by_term(root, args), expect)
  227. def test_match_division_in_denominator(self):
  228. a, ((b, c), d) = root = tree('a / (b / c + d)')
  229. self.assertEqualPos(match_division_in_denominator(root),
  230. [P(root, multiply_with_term, (c,))])
  231. a, ((d, (b, c)), e) = root = tree('a / (d + b / c + e)')
  232. self.assertEqualPos(match_division_in_denominator(root),
  233. [P(root, multiply_with_term, (c,))])
  234. def test_multiply_with_term_chain(self):
  235. self.assertRewrite([
  236. '1 / (1 / b - 1 / a)',
  237. '(b * 1) / (b(1 / b - 1 / a))',
  238. 'b / (b(1 / b - 1 / a))',
  239. 'b / (b * 1 / b + b(-1 / a))',
  240. 'b / ((b * 1) / b + b(-1 / a))',
  241. 'b / (b / b + b(-1 / a))',
  242. 'b / (1 + b(-1 / a))',
  243. 'b / (1 - b * 1 / a)',
  244. 'b / (1 - (b * 1) / a)',
  245. 'b / (1 - b / a)',
  246. '(ab) / (a(1 - b / a))',
  247. '(ab) / (a * 1 + a(-b / a))',
  248. '(ab) / (a + a(-b / a))',
  249. '(ab) / (a - ab / a)',
  250. '(ab) / (a - (ab) / a)',
  251. '(ab) / (a - b)',
  252. ])