1"""A module providing information about the necessity of brackets"""
2
3from ..core.function import _coeff_isneg
4
5
6#: Default precedence values for some basic types.
7PRECEDENCE = {
8    'Lambda': 1,
9    'Xor': 10,
10    'Or': 20,
11    'And': 30,
12    'Relational': 35,
13    'Add': 40,
14    'Mul': 50,
15    'Mod': 50,
16    'Pow': 60,
17    'Func': 70,
18    'Not': 100,
19    'Atom': 1000,
20    'BitwiseOr': 36,
21    'BitwiseAnd': 38,
22}
23
24#: A dictionary assigning precedence values to certain classes. These values
25#: are treated like they were inherited, so not every single class has to
26#: be named here.
27PRECEDENCE_VALUES = {
28    'Equivalent': PRECEDENCE['Xor'],
29    'Xor': PRECEDENCE['Xor'],
30    'Implies': PRECEDENCE['Xor'],
31    'Or': PRECEDENCE['Or'],
32    'And': PRECEDENCE['And'],
33    'Add': PRECEDENCE['Add'],
34    'Mod': PRECEDENCE['Mod'],
35    'Pow': PRECEDENCE['Pow'],
36    'Relational': PRECEDENCE['Relational'],
37    'Sub': PRECEDENCE['Add'],
38    'Not': PRECEDENCE['Not'],
39    'factorial': PRECEDENCE['Func'],
40    'factorial2': PRECEDENCE['Func'],
41    'Function': PRECEDENCE['Func'],
42    'NegativeInfinity': PRECEDENCE['Add'],
43    'MatAdd': PRECEDENCE['Add'],
44    'MatMul': PRECEDENCE['Mul'],
45    'MatPow': PRECEDENCE['Pow'],
46    'HadamardProduct': PRECEDENCE['Mul'],
47    'Fraction': PRECEDENCE['Atom'],
48    'Equality': PRECEDENCE['Mul'],
49    'Unequality': PRECEDENCE['Mul'],
50}
51
52# Sometimes it's not enough to assign a fixed precedence value to a
53# class. Then a function can be inserted in this dictionary that takes
54# an instance of this class as argument and returns the appropriate
55# precedence value.
56
57# Precedence functions
58
59
60def precedence_Mul(item):
61    if _coeff_isneg(item):
62        return PRECEDENCE['Add']
63    return PRECEDENCE['Mul']
64
65
66def precedence_Rational(item):
67    if item.numerator < 0:
68        return PRECEDENCE['Add']
69    return PRECEDENCE['Mul']
70
71
72def precedence_Integer(item):
73    if item.numerator < 0:
74        return PRECEDENCE['Add']
75    return PRECEDENCE['Atom']
76
77
78def precedence_Float(item):
79    if item < 0:
80        return PRECEDENCE['Add']
81    return PRECEDENCE['Atom']
82
83
84def precedence_PolyElement(item):
85    if item.is_generator:
86        return PRECEDENCE['Atom']
87    elif item.is_ground:
88        return precedence(item[1])
89    elif item.is_term:
90        return PRECEDENCE['Mul']
91    else:
92        return PRECEDENCE['Add']
93
94
95def precedence_FracElement(item):
96    if item.denominator == 1:
97        return precedence_PolyElement(item.numerator)
98    else:
99        return PRECEDENCE['Mul']
100
101
102#: Sometimes it's not enough to assign a fixed precedence value to a class. Then
103#: a function can be inserted in this dictionary that takes an instance of this
104#: class as argument and returns the appropriate precedence value.
105PRECEDENCE_FUNCTIONS = {
106    'Integer': precedence_Integer,
107    'Mul': precedence_Mul,
108    'Rational': precedence_Rational,
109    'Float': precedence_Float,
110    'PolyElement': precedence_PolyElement,
111    'FracElement': precedence_FracElement,
112}
113
114
115def precedence(item):
116    """
117    Returns the precedence of a given object.
118    """
119    mro = item.__class__.__mro__
120    for i in mro:
121        n = i.__name__
122        if n in PRECEDENCE_FUNCTIONS:
123            return PRECEDENCE_FUNCTIONS[n](item)
124        elif n in PRECEDENCE_VALUES:
125            return PRECEDENCE_VALUES[n]
126    return PRECEDENCE['Atom']
127