symath 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|