sqrt.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import math
  2. from .utils import dividers, is_prime
  3. from ..node import ExpressionLeaf as Leaf, Scope, OP_SQRT, OP_MUL, sqrt
  4. from ..possibilities import Possibility as P, MESSAGES
  5. from ..translate import _
  6. def is_eliminateable_sqrt(n):
  7. """
  8. Check if the square root of n can be evaluated so that the square root
  9. disappears (is eliminated).
  10. """
  11. if isinstance(n, int):
  12. return n > 3 and int(math.sqrt(n)) ** 2 == n
  13. if n.negated:
  14. return False
  15. if n.is_numeric():
  16. return is_eliminateable_sqrt(n.value)
  17. return n.is_power(2)
  18. def match_reduce_sqrt(node):
  19. """
  20. sqrt(a ^ 2) -> a
  21. sqrt(a) and eval(sqrt(a)) in Z -> eval(sqrt(a))
  22. sqrt(a) and a == b ^ 2 * c with a,b,c in Z -> sqrt(eval(b ^ 2) * c)
  23. sqrt(ab) -> sqrt(a)sqrt(b)
  24. """
  25. assert node.is_op(OP_SQRT)
  26. exp = node[0]
  27. if exp.negated:
  28. return []
  29. if exp.is_power(2):
  30. return [P(node, quadrant_sqrt)]
  31. if exp.is_numeric():
  32. reduced = int(math.sqrt(exp.value))
  33. if reduced ** 2 == exp.value:
  34. return [P(node, constant_sqrt, (reduced,))]
  35. div = filter(is_eliminateable_sqrt, dividers(exp.value))
  36. div.sort(lambda a, b: cmp(is_prime(b), is_prime(a)))
  37. return [P(node, split_dividers, (m, exp.value / m)) for m in div]
  38. if exp.is_op(OP_MUL):
  39. scope = Scope(exp)
  40. p = []
  41. for n in scope:
  42. if is_eliminateable_sqrt(n):
  43. p.append(P(node, extract_sqrt_mult_priority, (scope, n)))
  44. else:
  45. p.append(P(node, extract_sqrt_multiplicant, (scope, n)))
  46. return p
  47. return []
  48. def quadrant_sqrt(root, args):
  49. """
  50. sqrt(a ^ 2) -> a
  51. """
  52. return root[0][0].negate(root.negated)
  53. MESSAGES[quadrant_sqrt] = \
  54. _('The square root of a quadrant reduces to the raised root.')
  55. def constant_sqrt(root, args):
  56. """
  57. sqrt(a) and eval(sqrt(a)) in Z -> eval(sqrt(a))
  58. """
  59. return Leaf(args[0]).negate(root.negated)
  60. MESSAGES[constant_sqrt] = \
  61. _('The square root of {0[0]} is {1}.')
  62. def split_dividers(root, args):
  63. """
  64. sqrt(a) and b * c = a with a,b,c in Z -> sqrt(a * b)
  65. """
  66. b, c = args
  67. return sqrt(Leaf(b) * c)
  68. MESSAGES[split_dividers] = _('Write {0[0]} as {1} * {2} to so that {1} can ' \
  69. 'be brought outside of the square root.')
  70. def extract_sqrt_multiplicant(root, args):
  71. """
  72. sqrt(ab) -> sqrt(a)sqrt(b)
  73. """
  74. scope, a = args
  75. scope.remove(a)
  76. return (sqrt(a) * sqrt(scope.as_nary_node())).negate(root.negated)
  77. MESSAGES[extract_sqrt_multiplicant] = _('Extract {2} from {0}.')
  78. def extract_sqrt_mult_priority(root, args):
  79. """
  80. sqrt(ab) and sqrt(a) in Z -> sqrt(a)sqrt(b)
  81. """
  82. return extract_sqrt_multiplicant(root, args)
  83. MESSAGES[extract_sqrt_mult_priority] = MESSAGES[extract_sqrt_multiplicant]