lineq.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. from .utils import find_variable, evals_to_numeric
  2. from ..node import ExpressionLeaf as L, Scope, OP_EQ, OP_ADD, OP_MUL, OP_DIV, \
  3. eq, OP_ABS
  4. from ..possibilities import Possibility as P, MESSAGES
  5. from ..translate import _
  6. def match_move_term(node):
  7. """
  8. Perform the same action on both sides of the equation such that variable
  9. terms are moved to the left, and constants (in relation to the variable
  10. that is being solved) are brought to the right side of the equation.
  11. If the variable is only present on the right side of the equation, swap the
  12. sides first.
  13. # Swap
  14. a = b * x -> b * x = a
  15. # Subtraction
  16. x + a = b -> x + a - a = b - a
  17. a = b + x -> a - x = b + x - x # =>* x = b / a
  18. # Division
  19. x * a = b -> x * a / a = b / a # =>* x = b / a
  20. # Multiplication
  21. x / a = b -> x / a * a = b * a # =>* x = a * b
  22. a / x = b -> a / x * x = b * x # =>* x = a / b
  23. -x = b -> -x * -1 = b * -1 # =>* x = -b
  24. # Absolute value
  25. |f(x)| = c and eval(c) in Z -> f(x) = c vv f(x) = -c
  26. """
  27. assert node.is_op(OP_EQ)
  28. x = find_variable(node)
  29. left, right = node
  30. p = []
  31. if not left.contains(x):
  32. # Swap the left and right side if only the right side contains x
  33. if right.contains(x):
  34. p.append(P(node, swap_sides))
  35. return p
  36. # Bring terms without x to the right
  37. if left.is_op(OP_ADD):
  38. for n in Scope(left):
  39. if not n.contains(x):
  40. p.append(P(node, subtract_term, (n,)))
  41. # Bring terms with x to the left
  42. if right.is_op(OP_ADD):
  43. for n in Scope(right):
  44. if n.contains(x):
  45. p.append(P(node, subtract_term, (n,)))
  46. # Divide both sides by a constant to 'free' x
  47. if left.is_op(OP_MUL):
  48. for n in Scope(left):
  49. if not n.contains(x):
  50. p.append(P(node, divide_term, (n,)))
  51. # Multiply both sides by the denominator to move x out of the division
  52. if left.is_op(OP_DIV):
  53. p.append(P(node, multiply_term, (left[1],)))
  54. # Remove any negation from the left side of the equation
  55. if left.negated:
  56. p.append(P(node, multiply_term, (-L(1),)))
  57. # Split absolute equations into two separate, non-absolute equations
  58. if left.is_op(OP_ABS) and evals_to_numeric(right):
  59. p.append(P(node, split_absolute_equation))
  60. return p
  61. def swap_sides(root, args):
  62. """
  63. a = bx -> bx = a
  64. """
  65. left, right = root
  66. return eq(right, left)
  67. MESSAGES[swap_sides] = _('Swap the left and right side of the equation so ' \
  68. 'that the variable is on the left side.')
  69. def subtract_term(root, args):
  70. """
  71. x + a = b -> x + a - a = b - a
  72. a = b + x -> a - x = b + x - x
  73. """
  74. left, right = root
  75. term = args[0]
  76. return eq(left - term, right - term)
  77. MESSAGES[subtract_term] = _('Subtract {1} from both sides of the equation.')
  78. def divide_term(root, args):
  79. """
  80. x * a = b -> x * a / a = b / a # =>* x = b / a
  81. """
  82. left, right = root
  83. term = args[0]
  84. return eq(left / term, right / term)
  85. MESSAGES[divide_term] = _('Divide both sides of the equation by {1}.')
  86. def multiply_term(root, args):
  87. """
  88. x / a = b -> x / a * a = b * a # =>* x = a * b
  89. a / x = b -> a / x * x = b * x # =>* x = a / b
  90. """
  91. left, right = root
  92. term = args[0]
  93. return eq(left * term, right * term)
  94. MESSAGES[multiply_term] = _('Multiply both sides of the equation with {1}.')
  95. def split_absolute_equation(root, args):
  96. """
  97. |f(x)| = c and eval(c) in Z -> f(x) = c vv f(x) = -c
  98. """
  99. (f,), c = root
  100. return eq(f, c) | eq(f, -c)
  101. MESSAGES[split_absolute_equation] = _('Split absolute equation {0} into a ' \
  102. 'negative and a positive equation.')