goniometry.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. from .utils import is_fraction
  2. from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, OP_ADD, \
  3. OP_POW, OP_MUL, OP_DIV, OP_SIN, OP_COS, OP_TAN, PI, TYPE_OPERATOR
  4. from ..possibilities import Possibility as P, MESSAGES
  5. from ..translate import _
  6. def sin(*args):
  7. return N('sin', *args)
  8. def cos(*args):
  9. return N('cos', *args)
  10. def tan(*args):
  11. return N('tan', *args)
  12. def match_add_quadrants(node):
  13. """
  14. sin(t) ^ 2 + cos(t) ^ 2 -> 1
  15. """
  16. assert node.is_op(OP_ADD)
  17. p = []
  18. sin_q, cos_q = node
  19. if sin_q.is_power(2) and cos_q.is_power(2):
  20. sin, cos = sin_q[0], cos_q[0]
  21. if sin.is_op(OP_SIN) and cos.is_op(OP_COS):
  22. p.append(P(node, add_quadrants, ()))
  23. return p
  24. def add_quadrants(root, args):
  25. """
  26. sin(t) ^ 2 + cos(t) ^ 2 -> 1
  27. """
  28. return L(1)
  29. MESSAGES[add_quadrants] = _('Add the sinus and cosinus quadrants to 1.')
  30. def match_negated_parameter(node):
  31. """
  32. sin(-t) -> -sin(t)
  33. cos(-t) -> cos(t)
  34. """
  35. assert node.is_op(OP_SIN) or node.is_op(OP_COS)
  36. t = node[0]
  37. if t.negated:
  38. if node.op == OP_SIN:
  39. return [P(node, negated_sinus_parameter, (t,))]
  40. return [P(node, negated_cosinus_parameter, (t,))]
  41. return []
  42. def negated_sinus_parameter(root, args):
  43. """
  44. sin(-t) -> -sin(t)
  45. """
  46. return -sin(+args[0])
  47. MESSAGES[negated_sinus_parameter] = \
  48. _('Bring the negation from the sinus parameter {1} to the outside.')
  49. def negated_cosinus_parameter(root, args):
  50. """
  51. cos(-t) -> cos(t)
  52. """
  53. return cos(+args[0])
  54. MESSAGES[negated_cosinus_parameter] = \
  55. _('Remove the negation from the cosinus parameter {1}.')
  56. def match_half_pi_subtraction(node):
  57. """
  58. sin(pi / 2 - t) -> cos(t)
  59. cos(pi / 2 - t) -> sin(t)
  60. """
  61. assert node.is_op(OP_SIN) or node.is_op(OP_COS)
  62. if node[0].is_op(OP_ADD):
  63. half_pi, t = node[0]
  64. if half_pi == L(PI) / 2:
  65. if node.op == OP_SIN:
  66. return [P(node, half_pi_subtraction_sinus, (t,))]
  67. return []
  68. def is_pi_frac(node, denominator):
  69. """
  70. Check if a node is a fraction of 1 multiplied with PI.
  71. Example:
  72. >>> print is_pi_frac(L(1) / 2 * L(PI), 2)
  73. True
  74. """
  75. if not node.is_op(OP_MUL):
  76. return False
  77. frac, pi = node
  78. if not frac.is_op(OP_DIV) or not pi.is_leaf or pi.value != PI:
  79. return False
  80. n, d = frac
  81. return n == 1 and d == denominator
  82. def sqrt(value):
  83. return N('sqrt', L(value))
  84. l0, l1, sq2, sq3 = L(0), L(1), sqrt(2), sqrt(3)
  85. half = l1 / 2
  86. CONSTANTS = {
  87. OP_SIN: [l0, half, half * sq2, half * sq3, l1],
  88. OP_COS: [l1, half * sq3, half * sq2, half, l0],
  89. OP_TAN: [l0, l1 / 3 * sq3, l1, sq3]
  90. }
  91. def match_standard_radian(node):
  92. """
  93. Apply a direct constant calculation from the constants table.
  94. | 0 | pi / 6 | pi / 4 | pi / 3 | pi / 2
  95. ----+---+-----------+-----------+-----------+-------
  96. sin | 0 | 1/2 | sqrt(2)/2 | sqrt(3)/2 | 1
  97. cos | 1 | sqrt(3)/2 | sqrt(2)/2 | 1/2 | 0
  98. tan | 0 | sqrt(3)/3 | 1 | sqrt(3) | -
  99. """
  100. assert node.type == TYPE_OPERATOR and node.op in (OP_SIN, OP_COS, OP_TAN)
  101. t = node[0]
  102. if t == 0:
  103. return [P(node, standard_radian, (node.op, 0))]
  104. denoms = [6, 4, 3]
  105. if node.op != OP_TAN:
  106. denoms.append(2)
  107. for i, denominator in enumerate(denoms):
  108. if is_pi_frac(t, denominator):
  109. return [P(node, standard_radian, (node.op, i + 1))]
  110. return []
  111. def standard_radian(root, args):
  112. op, column = args
  113. return CONSTANTS[op][column].clone()
  114. MESSAGES[standard_radian] = _('Replace standard radian {0}.')