symath 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +616 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/symath/definition/abs.rb +48 -0
- data/lib/symath/definition/arccos.rb +25 -0
- data/lib/symath/definition/arccot.rb +23 -0
- data/lib/symath/definition/arccsc.rb +24 -0
- data/lib/symath/definition/arcsec.rb +24 -0
- data/lib/symath/definition/arcsin.rb +25 -0
- data/lib/symath/definition/arctan.rb +23 -0
- data/lib/symath/definition/bounds.rb +39 -0
- data/lib/symath/definition/codiff.rb +31 -0
- data/lib/symath/definition/constant.rb +111 -0
- data/lib/symath/definition/cos.rb +17 -0
- data/lib/symath/definition/cot.rb +17 -0
- data/lib/symath/definition/csc.rb +17 -0
- data/lib/symath/definition/curl.rb +27 -0
- data/lib/symath/definition/d.rb +62 -0
- data/lib/symath/definition/div.rb +27 -0
- data/lib/symath/definition/exp.rb +112 -0
- data/lib/symath/definition/fact.rb +55 -0
- data/lib/symath/definition/flat.rb +31 -0
- data/lib/symath/definition/function.rb +197 -0
- data/lib/symath/definition/grad.rb +23 -0
- data/lib/symath/definition/hodge.rb +23 -0
- data/lib/symath/definition/int.rb +75 -0
- data/lib/symath/definition/laplacian.rb +23 -0
- data/lib/symath/definition/lmd.rb +97 -0
- data/lib/symath/definition/ln.rb +45 -0
- data/lib/symath/definition/number.rb +51 -0
- data/lib/symath/definition/operator.rb +228 -0
- data/lib/symath/definition/sec.rb +17 -0
- data/lib/symath/definition/sharp.rb +31 -0
- data/lib/symath/definition/sin.rb +17 -0
- data/lib/symath/definition/sqrt.rb +62 -0
- data/lib/symath/definition/tan.rb +17 -0
- data/lib/symath/definition/trig.rb +95 -0
- data/lib/symath/definition/variable.rb +284 -0
- data/lib/symath/definition/xd.rb +28 -0
- data/lib/symath/definition.rb +205 -0
- data/lib/symath/equation.rb +67 -0
- data/lib/symath/fraction.rb +177 -0
- data/lib/symath/matrix.rb +252 -0
- data/lib/symath/minus.rb +125 -0
- data/lib/symath/operation/differential.rb +167 -0
- data/lib/symath/operation/distributivelaw.rb +367 -0
- data/lib/symath/operation/exterior.rb +64 -0
- data/lib/symath/operation/integration.rb +329 -0
- data/lib/symath/operation/match.rb +166 -0
- data/lib/symath/operation/normalization.rb +458 -0
- data/lib/symath/operation.rb +36 -0
- data/lib/symath/operator.rb +163 -0
- data/lib/symath/parser.rb +473 -0
- data/lib/symath/parser.y +129 -0
- data/lib/symath/poly/dup.rb +835 -0
- data/lib/symath/poly/galois.rb +621 -0
- data/lib/symath/poly.rb +142 -0
- data/lib/symath/power.rb +224 -0
- data/lib/symath/product.rb +183 -0
- data/lib/symath/sum.rb +174 -0
- data/lib/symath/type.rb +282 -0
- data/lib/symath/value.rb +372 -0
- data/lib/symath/version.rb +3 -0
- data/lib/symath/wedge.rb +48 -0
- data/lib/symath.rb +157 -0
- data/symath.gemspec +39 -0
- metadata +160 -0
@@ -0,0 +1,458 @@
|
|
1
|
+
require 'symath/operation'
|
2
|
+
require 'prime'
|
3
|
+
|
4
|
+
module SyMath::Operation::Normalization
|
5
|
+
include SyMath::Operation
|
6
|
+
|
7
|
+
# This operation provides an expression object with the normalize() method
|
8
|
+
# which normalizes an expression:
|
9
|
+
#
|
10
|
+
# equal arguments of a product are contracted to integer powers
|
11
|
+
# arguments of a product are sorted
|
12
|
+
#
|
13
|
+
# equal arguments of a sum (with subtractions) are contracted to integer
|
14
|
+
# products arguments in a sum are sorted
|
15
|
+
# subtractive elements are put after the additive elements
|
16
|
+
#
|
17
|
+
# integer sums are calculated
|
18
|
+
# integer products are calculated
|
19
|
+
#
|
20
|
+
# fractions of integers are simplified as far as possible
|
21
|
+
#
|
22
|
+
# The operation is repeated until the expression is no longer changed
|
23
|
+
|
24
|
+
def normalize()
|
25
|
+
if self.is_a?(SyMath::Equation)
|
26
|
+
return SyMath::Equation.new(args[0].normalize, args[1].normalize)
|
27
|
+
end
|
28
|
+
|
29
|
+
return iterate('normalize_single_pass')
|
30
|
+
end
|
31
|
+
|
32
|
+
def normalize_single_pass
|
33
|
+
if is_sum_exp?
|
34
|
+
return normalize_sum
|
35
|
+
end
|
36
|
+
|
37
|
+
if is_prod_exp?
|
38
|
+
return normalize_product
|
39
|
+
end
|
40
|
+
|
41
|
+
if is_a?(SyMath::Power)
|
42
|
+
return normalize_power
|
43
|
+
end
|
44
|
+
|
45
|
+
if is_a?(SyMath::Matrix)
|
46
|
+
return normalize_matrix
|
47
|
+
end
|
48
|
+
|
49
|
+
if is_a?(SyMath::Definition::Operator) and !@exp.nil?
|
50
|
+
@exp = @exp.normalize
|
51
|
+
end
|
52
|
+
|
53
|
+
return recurse('normalize', 'reduce')
|
54
|
+
end
|
55
|
+
|
56
|
+
def normalize_sum()
|
57
|
+
# Get normalized terms
|
58
|
+
terms = self.terms.map do |e|
|
59
|
+
if e.is_a?(SyMath::Minus)
|
60
|
+
e.argument.normalize.neg
|
61
|
+
else
|
62
|
+
e.normalize
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Collect equal elements into integer products
|
67
|
+
|
68
|
+
# Hash: product[vector part][scalar part]
|
69
|
+
products = {}
|
70
|
+
|
71
|
+
terms.each do |t|
|
72
|
+
c = 1
|
73
|
+
p = []
|
74
|
+
|
75
|
+
t.factors.each do |f|
|
76
|
+
if f == -1
|
77
|
+
c *= -1
|
78
|
+
next
|
79
|
+
elsif f.is_number?
|
80
|
+
c *= f.value
|
81
|
+
else
|
82
|
+
p.push f
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if products.key?(p)
|
87
|
+
products[p] += c
|
88
|
+
else
|
89
|
+
products[p] = c
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
terms2 = []
|
94
|
+
products.keys.sort.each do |p|
|
95
|
+
p.unshift products[p]
|
96
|
+
|
97
|
+
p = p.inject(1.to_m, :*)
|
98
|
+
|
99
|
+
if !SyMath.setting(:fraction_exponent_form)
|
100
|
+
p = p.product_on_fraction_form
|
101
|
+
end
|
102
|
+
|
103
|
+
terms2.push p
|
104
|
+
end
|
105
|
+
|
106
|
+
ret = terms2.reverse.inject(:+)
|
107
|
+
|
108
|
+
return ret
|
109
|
+
end
|
110
|
+
|
111
|
+
def normalize_product()
|
112
|
+
# Flatten the expression and order it
|
113
|
+
e = factors.map do |f|
|
114
|
+
f = f.normalize
|
115
|
+
end
|
116
|
+
|
117
|
+
e = e.inject(:*)
|
118
|
+
|
119
|
+
if e.is_prod_exp?
|
120
|
+
e = e.order_product
|
121
|
+
end
|
122
|
+
|
123
|
+
if e.is_prod_exp?
|
124
|
+
e = e.reduce_constant_factors
|
125
|
+
end
|
126
|
+
|
127
|
+
if !SyMath.setting(:fraction_exponent_form)
|
128
|
+
e = e.product_on_fraction_form
|
129
|
+
end
|
130
|
+
|
131
|
+
return e
|
132
|
+
end
|
133
|
+
|
134
|
+
def normalize_power()
|
135
|
+
norm = base.normalize.power(exponent.normalize)
|
136
|
+
e, sign, changed = norm.reduce_modulo_sign
|
137
|
+
e *= -1 if sign == -1
|
138
|
+
|
139
|
+
return e
|
140
|
+
end
|
141
|
+
|
142
|
+
def normalize_matrix()
|
143
|
+
data = (0..nrows - 1).map do |r|
|
144
|
+
row(r).map { |e| e.normalize }
|
145
|
+
end
|
146
|
+
|
147
|
+
return SyMath::Matrix.new(data)
|
148
|
+
end
|
149
|
+
|
150
|
+
def product_on_fraction_form
|
151
|
+
ret = []
|
152
|
+
fact = 1.to_m
|
153
|
+
divf = 1.to_m
|
154
|
+
|
155
|
+
factors.each do |f|
|
156
|
+
if f.type.is_scalar?
|
157
|
+
if f.is_divisor_factor?
|
158
|
+
divf *= f.base**f.exponent.argument
|
159
|
+
else
|
160
|
+
fact *= f
|
161
|
+
end
|
162
|
+
else
|
163
|
+
if divf != 1
|
164
|
+
fact = fact/divf
|
165
|
+
end
|
166
|
+
if fact != 1
|
167
|
+
ret.push fact
|
168
|
+
end
|
169
|
+
|
170
|
+
fact = f
|
171
|
+
divf = 1.to_m
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
if divf != 1
|
176
|
+
fact = fact/divf
|
177
|
+
end
|
178
|
+
if fact != 1
|
179
|
+
ret.push fact
|
180
|
+
end
|
181
|
+
|
182
|
+
return ret.empty? ? 1.to_m : ret.inject(:*)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Order the factors first by type, then, for commutative and anti-
|
186
|
+
# commutative factors, by content using bubble sort:
|
187
|
+
# sign * constant numbers * scalar factors * other factors
|
188
|
+
#
|
189
|
+
# - Commutative factors are swapped without changing sign.
|
190
|
+
# - Swapping anti-commutative factors changes the sign.
|
191
|
+
#
|
192
|
+
# Constant numers are multiplied to a single coefficient
|
193
|
+
# Other factors are reduced if possible:
|
194
|
+
# fundamental quaternions can always be reduced.
|
195
|
+
# exterior algebra basis vectors can be reduced whenever
|
196
|
+
# a double occurrence is found.
|
197
|
+
def order_product()
|
198
|
+
# Bubble sort factors. Reduce factors and combine thm whenever possible
|
199
|
+
done = false
|
200
|
+
sign = 1
|
201
|
+
head = self
|
202
|
+
|
203
|
+
while !done
|
204
|
+
done = true
|
205
|
+
|
206
|
+
ex = head
|
207
|
+
prev = nil
|
208
|
+
|
209
|
+
while ex.is_a?(SyMath::Product)
|
210
|
+
if factor1.is_a?(SyMath::Product)
|
211
|
+
f, sign2, changed = factor1.factor2.reduce_modulo_sign
|
212
|
+
if changed
|
213
|
+
self.factor1.factor2 = f
|
214
|
+
done = false
|
215
|
+
end
|
216
|
+
else
|
217
|
+
f, sign2, changed = factor1.reduce_modulo_sign
|
218
|
+
if changed
|
219
|
+
self.factor1 = f
|
220
|
+
done = false
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
sign *= sign2
|
225
|
+
|
226
|
+
ex, sign2, changed = ex.combine_factors
|
227
|
+
done = false if changed
|
228
|
+
sign *= sign2
|
229
|
+
|
230
|
+
# The product has been combined.
|
231
|
+
if prev.nil?
|
232
|
+
# No prev element. Replace head with ex
|
233
|
+
head = ex
|
234
|
+
else
|
235
|
+
# Attach the combined expression onto the previous product
|
236
|
+
# exp and continue
|
237
|
+
prev.factor1 = ex
|
238
|
+
end
|
239
|
+
|
240
|
+
if !ex.is_a?(SyMath::Product)
|
241
|
+
next
|
242
|
+
end
|
243
|
+
|
244
|
+
sign2, changed = ex.compare_factors_and_swap
|
245
|
+
done = false if changed
|
246
|
+
sign *= sign2
|
247
|
+
|
248
|
+
prev = ex
|
249
|
+
ex = ex.factor1
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
if sign == -1
|
254
|
+
return -head
|
255
|
+
else
|
256
|
+
return head
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# FIXME: Do the reduction in the combine_factors part.
|
261
|
+
# Reduce c and c**-1 by gdc. The expression is expected to be flattened
|
262
|
+
# and ordered so that the first argument is the constand and the second
|
263
|
+
# argument is the divisor constant.
|
264
|
+
def reduce_constant_factors()
|
265
|
+
c = nil
|
266
|
+
dc = nil
|
267
|
+
ret = []
|
268
|
+
|
269
|
+
self.factors.each do |f|
|
270
|
+
if dc.nil?
|
271
|
+
if f.is_divisor_factor?
|
272
|
+
if f.base.is_number?
|
273
|
+
dc = f.base.value**f.exponent.argument.value
|
274
|
+
next
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
if c.nil?
|
280
|
+
if f.is_number?
|
281
|
+
c = f.value
|
282
|
+
next
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
c = 1 if c.nil?
|
287
|
+
dc = 1 if dc.nil?
|
288
|
+
|
289
|
+
ret.push f
|
290
|
+
end
|
291
|
+
|
292
|
+
c = 1 if c.nil?
|
293
|
+
dc = 1 if dc.nil?
|
294
|
+
|
295
|
+
# First examine the coefficients
|
296
|
+
if c == 0 and dc > 0
|
297
|
+
return 0.to_m
|
298
|
+
end
|
299
|
+
|
300
|
+
if c > 0
|
301
|
+
# Reduce coefficients by greatest common divisor
|
302
|
+
gcd = c.gcd(dc)
|
303
|
+
c /= gcd
|
304
|
+
dc /= gcd
|
305
|
+
end
|
306
|
+
|
307
|
+
if dc != 1
|
308
|
+
ret.unshift dc.to_m**-1
|
309
|
+
end
|
310
|
+
|
311
|
+
if c != 1
|
312
|
+
ret.unshift c.to_m
|
313
|
+
end
|
314
|
+
|
315
|
+
return ret.inject(:*)
|
316
|
+
end
|
317
|
+
|
318
|
+
# Return result of the two factors multiplied if it simplifies
|
319
|
+
# the expression.
|
320
|
+
# Returns (new_exp, sign, changed)
|
321
|
+
def combine_factors
|
322
|
+
if factor1.is_a?(SyMath::Product)
|
323
|
+
f1 = factor1.factor2
|
324
|
+
else
|
325
|
+
f1 = factor1
|
326
|
+
end
|
327
|
+
f2 = factor2
|
328
|
+
|
329
|
+
# Natural numbers are calculated
|
330
|
+
if f1.is_number? and f2.is_number?
|
331
|
+
return replace_combined_factors((f1.value*f2.value).to_m), 1, true
|
332
|
+
end
|
333
|
+
|
334
|
+
if f1.is_unit_quaternion? and f2.is_unit_quaternion?
|
335
|
+
ret = f1.calc_unit_quaternions(f2)
|
336
|
+
if ret.is_a?(SyMath::Minus)
|
337
|
+
return replace_combined_factors(ret.argument), -1, true
|
338
|
+
else
|
339
|
+
return replace_combined_factors(ret), 1, true
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
if f1.is_a?(SyMath::Power)
|
344
|
+
base1 = f1.base
|
345
|
+
exp1 = f1.exponent
|
346
|
+
else
|
347
|
+
base1 = f1
|
348
|
+
exp1 = 1.to_m
|
349
|
+
end
|
350
|
+
|
351
|
+
if f2.is_a?(SyMath::Power)
|
352
|
+
base2 = f2.base
|
353
|
+
exp2 = f2.exponent
|
354
|
+
else
|
355
|
+
base2 = f2
|
356
|
+
exp2 = 1.to_m
|
357
|
+
end
|
358
|
+
|
359
|
+
if base1 == base2
|
360
|
+
if base1.type.is_subtype?('tensor') and
|
361
|
+
base2.type.is_subtype?('tensor') and
|
362
|
+
(exp1 + exp2).is_number? and
|
363
|
+
(exp1 + exp2).value > 1
|
364
|
+
return replace_combined_factors(0.to_m), 1, true
|
365
|
+
end
|
366
|
+
|
367
|
+
return replace_combined_factors(base1**(exp1 + exp2)), 1, true
|
368
|
+
end
|
369
|
+
|
370
|
+
return self, 1, false
|
371
|
+
end
|
372
|
+
|
373
|
+
# Replace factor1 and factor2 with e. Return new combined expression
|
374
|
+
def replace_combined_factors(e)
|
375
|
+
if factor1.is_a?(SyMath::Product)
|
376
|
+
return factor1.factor1*e
|
377
|
+
else
|
378
|
+
return e
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# Compare first and second element in product. Swap if they can and
|
383
|
+
# should be swapped. Return (sign, changed).
|
384
|
+
def compare_factors_and_swap()
|
385
|
+
f1 = factor1.is_a?(SyMath::Product) ? factor1.factor2 : factor1
|
386
|
+
f2 = factor2
|
387
|
+
|
388
|
+
if !f1.type.is_subtype?(:linop) or !f2.type.is_subtype?(:linop)
|
389
|
+
# Non-linear operator cannot be swapped
|
390
|
+
return 1, false
|
391
|
+
end
|
392
|
+
|
393
|
+
if !f1.type.is_scalar? and f2.type.is_scalar?
|
394
|
+
# Scalars always go before non-scalar linops
|
395
|
+
swap_factors
|
396
|
+
return 1, true
|
397
|
+
end
|
398
|
+
|
399
|
+
if (f1.type.is_vector? or f1.type.is_dform?) and
|
400
|
+
(f2.type.is_vector? or f2.type.is_dform?)
|
401
|
+
# Only order simple vectors. Don't order vector
|
402
|
+
# expressions
|
403
|
+
# FIXME: We could do that. If so, we must get the dimension
|
404
|
+
# of the variable and swap sign only if dim(f1)*dim(f2) is
|
405
|
+
# odd.
|
406
|
+
if f1.is_a?(SyMath::Definition::Variable) and f2.is_a?(SyMath::Definition::Variable)
|
407
|
+
# Order vector factors
|
408
|
+
if f2 < f1
|
409
|
+
swap_factors
|
410
|
+
return -1, true
|
411
|
+
else
|
412
|
+
return 1, false
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
if f1.type.is_scalar? and f2.type.is_scalar?
|
418
|
+
# Corner case. Order the imagninary unit above other scalars in order
|
419
|
+
# to make it bubble up to the other quaternions.
|
420
|
+
if f2 == :i
|
421
|
+
return 1, false
|
422
|
+
end
|
423
|
+
|
424
|
+
if f1 == :i
|
425
|
+
swap_factors
|
426
|
+
return 1, true
|
427
|
+
end
|
428
|
+
|
429
|
+
# Normalize as power factors so all factors with the same base
|
430
|
+
# end up at the same place and can be combined.
|
431
|
+
f1 = f1.power(1) if !f1.is_a?(SyMath::Power)
|
432
|
+
f2 = f2.power(1) if !f2.is_a?(SyMath::Power)
|
433
|
+
|
434
|
+
# Order scalar factors
|
435
|
+
if f2 < f1
|
436
|
+
swap_factors
|
437
|
+
return 1, true
|
438
|
+
else
|
439
|
+
return 1, false
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
# FIXME: Order other commutative and anti-commutative operators
|
444
|
+
return 1, false
|
445
|
+
end
|
446
|
+
|
447
|
+
# Swap first and second argument in product
|
448
|
+
def swap_factors()
|
449
|
+
f2 = self.factor2
|
450
|
+
if self.factor1.is_a?(SyMath::Product)
|
451
|
+
self.factor2 = self.factor1.factor2
|
452
|
+
self.factor1.factor2 = f2
|
453
|
+
else
|
454
|
+
self.factor2 = self.factor1
|
455
|
+
self.factor1 = f2
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SyMath::Operation
|
2
|
+
# Repeat method until there are no changes
|
3
|
+
def iterate(method)
|
4
|
+
ret = deep_clone.send(method)
|
5
|
+
if ret == self
|
6
|
+
return ret
|
7
|
+
else
|
8
|
+
return ret.iterate(method)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Call method recursively down the arguments of the expression
|
13
|
+
# and call self_method on self.
|
14
|
+
def recurse(method, self_method = method)
|
15
|
+
if is_a?(SyMath::Definition) or is_a?(SyMath::Matrix)
|
16
|
+
if self_method.nil?
|
17
|
+
return self
|
18
|
+
else
|
19
|
+
ret = self.send(self_method)
|
20
|
+
return ret
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Call method on each argument
|
25
|
+
newargs = args.map { |a| a.send('recurse', method) }
|
26
|
+
|
27
|
+
ret = self.deep_clone
|
28
|
+
ret.args = newargs
|
29
|
+
|
30
|
+
if self_method.nil?
|
31
|
+
return ret
|
32
|
+
else
|
33
|
+
return ret.send(self_method)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'symath/value'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module SyMath
|
5
|
+
class Operator < Value
|
6
|
+
attr_reader :definition
|
7
|
+
attr_accessor :args
|
8
|
+
|
9
|
+
# Compose with simplify. Defaults to composition with no reductions
|
10
|
+
def self.compose_with_simplify(*args)
|
11
|
+
d = args[0]
|
12
|
+
if d.is_a?(SyMath::Definition::Operator)
|
13
|
+
ret = d.compose_with_simplify(*args)
|
14
|
+
if ret
|
15
|
+
return ret
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
return self.new(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def name()
|
23
|
+
return definition.name
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return arguments
|
27
|
+
def args_assoc()
|
28
|
+
if is_associative?
|
29
|
+
return args.map { |a| a.class == self.class ? a.args_assoc : [a] }.inject(:+)
|
30
|
+
else
|
31
|
+
return args
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_commutative?()
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_associative?()
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
|
43
|
+
def evaluate()
|
44
|
+
# Hack: Don't evaluate arguments if the operator is the integral.
|
45
|
+
# this is taken care of inside the definition.
|
46
|
+
if name != :int
|
47
|
+
@args = @args.map { |a| a.evaluate }
|
48
|
+
end
|
49
|
+
definition.evaluate_call(self)
|
50
|
+
end
|
51
|
+
|
52
|
+
def arity()
|
53
|
+
return @args.length
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(definition, args)
|
57
|
+
if !definition.is_a?(SyMath::Value)
|
58
|
+
definition = SyMath::Definition.get(definition)
|
59
|
+
end
|
60
|
+
|
61
|
+
@definition = definition
|
62
|
+
@args = args
|
63
|
+
|
64
|
+
if definition.is_a?(SyMath::Definition::Operator)
|
65
|
+
definition.validate_args(self)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s()
|
70
|
+
if definition.is_a?(SyMath::Definition::Operator)
|
71
|
+
return definition.to_s(@args)
|
72
|
+
else
|
73
|
+
# Expression call
|
74
|
+
arglist = @args.map { |a| a.to_s }.join(',')
|
75
|
+
|
76
|
+
return "(#{definition}).(#{arglist})"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_latex()
|
81
|
+
return definition.to_latex(@args)
|
82
|
+
end
|
83
|
+
|
84
|
+
def dump(indent = 0)
|
85
|
+
i = ' '*indent
|
86
|
+
ret = super(indent)
|
87
|
+
arglist = args.map do |a|
|
88
|
+
a.dump(indent + 2)
|
89
|
+
end
|
90
|
+
|
91
|
+
return ret + "\n" + arglist.join("\n")
|
92
|
+
end
|
93
|
+
|
94
|
+
def hash()
|
95
|
+
h = @name.hash
|
96
|
+
@args.each do |a|
|
97
|
+
h ^= a.hash
|
98
|
+
end
|
99
|
+
|
100
|
+
return h
|
101
|
+
end
|
102
|
+
|
103
|
+
def ==(other)
|
104
|
+
o = other.to_m
|
105
|
+
return false if self.class.name != o.class.name
|
106
|
+
return false if name.to_s != o.name.to_s
|
107
|
+
return false if arity != o.arity
|
108
|
+
return args == o.args
|
109
|
+
end
|
110
|
+
|
111
|
+
def <=>(other)
|
112
|
+
if self.class.name != other.class.name
|
113
|
+
return super(other)
|
114
|
+
end
|
115
|
+
|
116
|
+
if name != other.name
|
117
|
+
return name.to_s <=> other.name.to_s
|
118
|
+
end
|
119
|
+
|
120
|
+
if arity != other.arity
|
121
|
+
return arity <=> other.arity
|
122
|
+
end
|
123
|
+
|
124
|
+
(0...arity).to_a.each do |i|
|
125
|
+
diff = args[i] <=> other.args[i]
|
126
|
+
if diff != 0
|
127
|
+
return diff
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
return 0
|
132
|
+
end
|
133
|
+
|
134
|
+
alias eql? ==
|
135
|
+
|
136
|
+
def reduce()
|
137
|
+
return definition.reduce_call(self)
|
138
|
+
end
|
139
|
+
|
140
|
+
def is_constant?(vars = nil)
|
141
|
+
@args.each do |a|
|
142
|
+
return false if !a.is_constant?(vars)
|
143
|
+
end
|
144
|
+
|
145
|
+
return true
|
146
|
+
end
|
147
|
+
|
148
|
+
def variables()
|
149
|
+
vars = @args.map { |a| a.variables }
|
150
|
+
return vars.length == 0 ? vars : vars.inject(:|)
|
151
|
+
end
|
152
|
+
|
153
|
+
def replace(map)
|
154
|
+
@args = @args.map do |a|
|
155
|
+
a.replace(map)
|
156
|
+
end
|
157
|
+
|
158
|
+
@definition = definition.replace(map)
|
159
|
+
|
160
|
+
return self
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|