utils.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_MUL, OP_DIV
  2. def greatest_common_divisor(a, b):
  3. """
  4. Return greatest common divisor of a and b using Euclid's Algorithm.
  5. """
  6. while b:
  7. a, b = b, a % b
  8. return a
  9. def lcm(a, b):
  10. """
  11. Return least common multiple of a and b.
  12. """
  13. return a * b // greatest_common_divisor(a, b)
  14. def least_common_multiple(*args):
  15. """
  16. Return lcm of args.
  17. """
  18. return reduce(lcm, args)
  19. def is_fraction(node, nominator, denominator):
  20. """
  21. Check if a node represents the fraction of a given nominator and
  22. denominator.
  23. >>> a, l1, l2 = L('a'), L(1), L(2)
  24. >>> is_fraction(a / l2, a, 2)
  25. True
  26. >>> is_fraction(l1 / l2 * a, a, 2)
  27. True
  28. >>> is_fraction(l2 / l1 * a, a, 2)
  29. False
  30. """
  31. if node.is_op(OP_DIV):
  32. nom, denom = node
  33. return nom == nominator and denom == denominator
  34. if node.is_op(OP_MUL):
  35. # 1 / denominator * nominator
  36. # nominator * 1 / denominator
  37. left, right = node
  38. fraction = L(1) / denominator
  39. return (left == nominator and right == fraction) \
  40. or (right == nominator and left == fraction)
  41. return False
  42. def partition(callback, iterable):
  43. """
  44. Partition an iterable into two parts using a callback that returns a
  45. boolean.
  46. Example:
  47. >>> partition(lambda x: x & 1, range(6))
  48. ([1, 3, 5], [0, 2, 4])
  49. """
  50. a, b = [], []
  51. for item in iterable:
  52. (a if callback(item) else b).append(item)
  53. return a, b
  54. def find_variables(node):
  55. """
  56. Find all variables in a node.
  57. """
  58. if node.is_variable():
  59. return set([node.value])
  60. if not node.is_leaf:
  61. return reduce(lambda a, b: a | b, map(find_variables, node))
  62. return set()
  63. def first_sorted_variable(variables):
  64. """
  65. In a set of variables, find the main variable to be used in a derivation or
  66. integral. The prioritized order is x, y, z, a, b, c, d, ...
  67. """
  68. for x in 'xyz':
  69. if x in variables:
  70. return x
  71. return sorted(variables)[0]
  72. def find_variable(exp):
  73. """
  74. Find the main (e.g. first prioritized) variable in an expression and return
  75. it as an ExpressionNode object. If no variable is present, return 'x' by
  76. default.
  77. """
  78. variables = find_variables(exp)
  79. if not len(variables):
  80. variables.add('x')
  81. return L(first_sorted_variable(variables))
  82. def substitute(f, x, replacement):
  83. """
  84. Replace all occurences of variable x in function f with the specified
  85. replacement.
  86. """
  87. if f == x:
  88. return replacement.clone()
  89. if f.is_leaf:
  90. return f
  91. children = map(lambda c: substitute(c, x, replacement), f)
  92. return N(f.op, *children)
  93. def divides(m, n):
  94. """
  95. Check if m | n (m divides n).
  96. """
  97. return not divmod(n, m)[1]