This file is indexed.

/usr/lib/python2.7/dist-packages/ffc/quadrature/product.py is in python-ffc 1.4.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
"This file implements a class to represent a product."

# Copyright (C) 2009-2010 Kristian B. Oelgaard
#
# This file is part of FFC.
#
# FFC is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# FFC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with FFC. If not, see <http://www.gnu.org/licenses/>.
#
# First added:  2009-07-12
# Last changed: 2010-03-11

# FFC modules.
from ffc.log import error
from ffc.cpp import format

# FFC quadrature modules.
from symbolics import create_float
from symbolics import create_product
from symbolics import create_sum
from symbolics import create_fraction
from expr import Expr

#class Product(object):
class Product(Expr):
    __slots__ = ("vrs", "_expanded")
    def __init__(self, variables):
        """Initialise a Product object, it derives from Expr and contains
        the additional variables:

        vrs       - a list of variables
        _expanded - object, an expanded object of self, e.g.,
                    self = x*(2+y) -> self._expanded = (2*x + x*y) (a sum), or
                    self = 2*x -> self._expanded = 2*x (self).
        NOTE: self._prec = 2."""

        # Initialise value, list of variables, class.
        self.val = 1.0
        self.vrs = []
        self._prec = 2

        # Initially set _expanded to True.
        self._expanded = True

        # Process variables if we have any.
        if variables:
            # Remove nested Products and test for expansion.
            float_val = 1.0
            for var in variables:
                # If any value is zero the entire product is zero.
                if var.val == 0.0:
                    self.val = 0.0
                    self.vrs = [create_float(0.0)]
                    float_val = 0.0
                    break

                # Collect floats into one variable
                if var._prec == 0: # float
                    float_val *= var.val
                    continue
                # Take care of product such that we don't create nested products.
                elif var._prec == 2: # prod
#                    if var.vrs[0]._prec == 0:
#                        float_val *= var.vrs[0].val
#                        self.vrs += var.vrs[1:]
#                        continue
#                    self.vrs += var.vrs
#                    continue
                    # If expanded product is a float, just add it.
                    if var._expanded and var._expanded._prec == 0:
                        float_val *= var._expanded.val
                    # If expanded product is symbol, this product is still expanded and add symbol.
                    elif var._expanded and var._expanded._prec == 1:
                        self.vrs.append(var._expanded)
                    # If expanded product is still a product, add the variables.
                    elif var._expanded and var._expanded._prec == 2:
#                        self.vrs.append(var)
                        # Add copies of the variables of other product (collect floats).
                        if var._expanded.vrs[0]._prec == 0:
                            float_val *= var._expanded.vrs[0].val
                            self.vrs += var._expanded.vrs[1:]
                            continue
                        self.vrs += var._expanded.vrs
                    # If expanded product is a sum or fraction, we must expand this product later.
                    elif var._expanded and var._expanded._prec in (3, 4):
                        self._expanded = False
                        self.vrs.append(var._expanded)
                    # Else the product is not expanded, and we must expand this one later
                    else:
                        self._expanded = False
                        # Add copies of the variables of other product (collect floats).
                        if var.vrs[0]._prec == 0:
                            float_val *= var.vrs[0].val
                            self.vrs += var.vrs[1:]
                            continue
                        self.vrs += var.vrs
                    continue
                # If we have sums or fractions in the variables the product is not expanded.
                elif var._prec in (3, 4): # sum or frac
                    self._expanded = False

                # Just add any variable at this point to list of new vars.
                self.vrs.append(var)

            # If value is 1 there is no need to include it, unless it is the
            # only parameter left i.e., 2*0.5 = 1.
            if float_val and float_val != 1.0:
                self.val = float_val
                self.vrs.append(create_float(float_val))
            # If we no longer have any variables add the float.
            elif not self.vrs:
                self.val = float_val
                self.vrs = [create_float(float_val)]
            # If 1.0 is the only value left, add it.
            elif abs(float_val - 1.0) < format["epsilon"] and not self.vrs:
                self.val = 1.0
                self.vrs = [create_float(1)]

        # If we don't have any variables the product is zero.
        else:
            self.val = 0.0
            self.vrs = [create_float(0)]

        # The type is equal to the lowest variable type.
        self.t = min([v.t for v in self.vrs])

        # Sort the variables such that comparisons work.
        self.vrs.sort()

        # Compute the representation now, such that we can use it directly
        # in the __eq__ and __ne__ methods (improves performance a bit, but
        # only when objects are cached).
        self._repr = "Product([%s])" % ", ".join([v._repr for v in self.vrs])

        # Use repr as hash value.
        self._hash = hash(self._repr)

        # Store self as expanded value, if we did not encounter any sums or fractions.
        if self._expanded:
            self._expanded = self

    # Print functions.
    def __str__(self):
        "Simple string representation which will appear in the generated code."
        # If we have more than one variable and the first float is -1 exlude the 1.
        if len(self.vrs) > 1 and self.vrs[0]._prec == 0 and self.vrs[0].val == -1.0:
            # Join string representation of members by multiplication
            return   format["sub"](["", format["mul"]([str(v) for v in self.vrs[1:]])])
        return format["mul"]([str(v) for v in self.vrs])

    # Binary operators.
    def __add__(self, other):
        "Addition by other objects."
        # NOTE: Assuming expanded variables.
        # If two products are equal, add their float values.
        if other._prec == 2 and self.get_vrs() == other.get_vrs():
            # Return expanded product, to get rid of 3*x + -2*x -> x, not 1*x.
            return create_product([create_float(self.val + other.val)] + list(self.get_vrs())).expand()
        # if self == 2*x and other == x return 3*x.
        elif other._prec == 1: # sym
            if self.get_vrs() == (other,):
                # Return expanded product, to get rid of -x + x -> 0, not product(0).
                return create_product([create_float(self.val + 1.0), other]).expand()
        # Return sum
        return create_sum([self, other])

    def __sub__(self, other):
        "Subtract other objects."
        if other._prec == 2 and self.get_vrs() == other.get_vrs():
            # Return expanded product, to get rid of 3*x + -2*x -> x, not 1*x.
            return create_product([create_float(self.val - other.val)] + list(self.get_vrs())).expand()
        # if self == 2*x and other == x return 3*x.
        elif other._prec == 1: # sym
            if self.get_vrs() == (other,):
                # Return expanded product, to get rid of -x + x -> 0, not product(0).
                return create_product([create_float(self.val - 1.0), other]).expand()
        # Return sum
        return create_sum([self, create_product([FloatValue(-1), other])])

    def __mul__(self, other):
        "Multiplication by other objects."
        # If product will be zero.
        if self.val == 0.0 or other.val == 0.0:
            return create_float(0)

        # If other is a Sum or Fraction let them handle it.
        if other._prec in (3, 4): # sum or frac
            return other.__mul__(self)

        # NOTE: We expect expanded sub-expressions with no nested operators.
        # Create new product adding float or symbol.
        if other._prec in (0, 1): # float or sym
            return create_product(self.vrs + [other])
        # Create new product adding all variables from other Product.
        return create_product(self.vrs + other.vrs)

    def __div__(self, other):
        "Division by other objects."
        # If division is illegal (this should definitely not happen).
        if other.val == 0.0:
            error("Division by zero.")

        # If fraction will be zero.
        if self.val == 0.0:
            return self.vrs[0]

        # If other is a Sum we can only return a fraction.
        # NOTE: Expect that other is expanded i.e., x + x -> 2*x which can be handled
        # TODO: Fix x / (x + x*y) -> 1 / (1 + y).
        # Or should this be handled when reducing a fraction?
        if other._prec == 3: # sum
            return create_fraction(self, other)

        # Handle division by FloatValue, Symbol, Product and Fraction.
        # NOTE: assuming that we get expanded variables.

        # Copy numerator, and create list for denominator.
        num = self.vrs[:]
        denom = []
        # Add floatvalue, symbol and products to the list of denominators.
        if other._prec in (0, 1): # float or sym
            denom = [other]
        elif other._prec == 2: # prod
            # Get copy.
            denom = other.vrs[:]
        # fraction.
        else:
            error("Did not expected to divide by fraction.")

        # Loop entries in denominator and remove from numerator (and denominator).
        for d in denom[:]:
            # Add the inverse of a float to the numerator and continue.
            if d._prec == 0: # float
                num.append(create_float(1.0/d.val))
                denom.remove(d)
                continue
            if d in num:
                num.remove(d)
                denom.remove(d)

        # Create appropriate return value depending on remaining data.
        if len(num) > 1:
            # TODO: Make this more efficient?
            # Create product and expand to reduce
            # Product([5, 0.2]) == Product([1]) -> Float(1).
            num = create_product(num).expand()
        elif num:
            num = num[0]
        # If all variables in the numerator has been eliminated we need to add '1'.
        else:
            num = create_float(1)

        if len(denom) > 1:
            return create_fraction(num, create_product(denom))
        elif denom:
            return create_fraction(num, denom[0])
        # If we no longer have a denominater, just return the numerator.
        return num

    # Public functions.
    def expand(self):
        "Expand all members of the product."
        # If we just have one variable, compute the expansion of it
        # (it is not a Product, so it should be safe). We need this to get
        # rid of Product([Symbol]) type expressions.
        if len(self.vrs) == 1:
            self._expanded = self.vrs[0].expand()
            return self._expanded

        # If product is already expanded, simply return the expansion.
        if self._expanded:
            return self._expanded

        # Sort variables such that we don't call the '*' operator more than we have to.
        float_syms = []
        sum_fracs = []
        for v in self.vrs:
            if v._prec in (0, 1): # float or sym
                float_syms.append(v)
                continue
            exp = v.expand()

            # If the expanded expression is a float, sym or product,
            # we can add the variables.
            if exp._prec in (0, 1): # float or sym
                float_syms.append(exp)
            elif exp._prec == 2: # prod
                float_syms += exp.vrs
            else:
                sum_fracs.append(exp)
        # If we have floats or symbols add the symbols to the rest as a single
        # product (for speed).
        if len(float_syms) > 1:
            sum_fracs.append( create_product(float_syms) )
        elif float_syms:
            sum_fracs.append(float_syms[0])

        # Use __mult__ to reduce list to one single variable.
        # TODO: Can this be done more efficiently without creating all the
        # intermediate variables?
        self._expanded = reduce(lambda x,y: x*y, sum_fracs)
        return self._expanded

    def get_unique_vars(self, var_type):
        "Get unique variables (Symbols) as a set."
        # Loop all members and update the set.
        var = set()
        for v in self.vrs:
            var.update(v.get_unique_vars(var_type))
        return var

    def get_var_occurrences(self):
        """Determine the number of times all variables occurs in the expression.
        Returns a dictionary of variables and the number of times they occur."""
        # TODO: The product should be expanded at this stage, should we check
        # this?
        # Create dictionary and count number of occurrences of each variable.
        d = {}
        for v in self.vrs:
            if v in d:
                d[v] += 1
                continue
            d[v] = 1
        return d

    def get_vrs(self):
        "Return all 'real' variables."
        # A product should only have one float value after initialisation.
        # TODO: Use this knowledge directly in other classes?
        if self.vrs[0]._prec == 0: # float
            return tuple(self.vrs[1:])
        return tuple(self.vrs)

    def ops(self):
        "Get the number of operations to compute product."
        # It takes n-1 operations ('*') for a product of n members.
        op = len(self.vrs) - 1

        # Loop members and add their count.
        for v in self.vrs:
            op += v.ops()

        # Subtract 1, if the first member is -1 i.e., -1*x*y -> x*y is only 1 op.
        if self.vrs[0]._prec == 0 and self.vrs[0].val == -1.0:
            op -= 1
        return op

    def reduce_ops(self):
        "Reduce the number of operations to evaluate the product."
        # It's not possible to reduce a product if it is already expanded and
        # it should be at this stage.
        # TODO: Is it safe to return self.expand().reduce_ops() if product is
        # not expanded? And do we want to?
#        if self._expanded:
#            return self._expanded
#        error("Product must be expanded first before we can reduce the number of operations.")
        # TODO: This should crash if it goes wrong (the above is more correct but slower).
        return self._expanded

    def reduce_vartype(self, var_type):
        """Reduce expression with given var_type. It returns a tuple
        (found, remain), where 'found' is an expression that only has variables
        of type == var_type. If no variables are found, found=(). The 'remain'
        part contains the leftover after division by 'found' such that:
        self = found*remain."""
        # Sort variables according to type.
        found = []
        remains = []
        for v in self.vrs:
            if v.t == var_type:
                found.append(v)
                continue
            remains.append(v)

        # Create appropriate object for found.
        if len(found) > 1:
            found = create_product(found)
        elif found:
            found = found.pop()
        # We did not find any variables.
        else:
            return [((), self)]

        # Create appropriate object for remains.
        if len(remains) > 1:
            remains = create_product(remains)
        elif remains:
            remains = remains.pop()
        # We don't have anything left.
        else:
            return [(self, create_float(1))]

        # Return whatever we found.
        return [(found, remains)]

# FFC quadrature modules.
from floatvalue import FloatValue
from symbol     import Symbol
from sumobj    import Sum
from fraction   import Fraction