Mr.CAS 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +2 -0
- data/lib/Mr.CAS.rb +73 -0
- data/lib/Mr.CAS/auto-diff.rb +129 -0
- data/lib/Mr.CAS/c-opt.rb +225 -0
- data/lib/Mr.CAS/c.rb +126 -0
- data/lib/Mr.CAS/graphviz.rb +132 -0
- data/lib/Mr.CAS/latex.rb +68 -0
- data/lib/Mr.CAS/matlab.rb +81 -0
- data/lib/functions/fnc-base.rb +515 -0
- data/lib/functions/fnc-box-conditions.rb +319 -0
- data/lib/functions/fnc-conditions.rb +365 -0
- data/lib/functions/fnc-piecewise.rb +186 -0
- data/lib/functions/fnc-prod.rb +102 -0
- data/lib/functions/fnc-sum.rb +151 -0
- data/lib/functions/fnc-trig.rb +489 -0
- data/lib/functions/fnc-trsc.rb +192 -0
- data/lib/numbers/constants.rb +350 -0
- data/lib/numbers/functions.rb +194 -0
- data/lib/numbers/variables.rb +202 -0
- data/lib/operators/bary-op.rb +186 -0
- data/lib/operators/nary-op.rb +232 -0
- data/lib/operators/op.rb +285 -0
- data/lib/overloading/fixnum.rb +61 -0
- data/lib/overloading/float.rb +61 -0
- data/lib/version.rb +12 -0
- metadata +88 -0
- metadata.gz.sig +2 -0
@@ -0,0 +1,232 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
# This is an attempt to build some sort of node in the graph that
|
5
|
+
# has arbitrary number of childs node. It should help implement more easily
|
6
|
+
# some sort of better simplifications engine
|
7
|
+
#
|
8
|
+
# This is an incredibly experimental feature.
|
9
|
+
class NaryOp < CAS::Op
|
10
|
+
# List of arguments of the operation
|
11
|
+
attr_reader :x
|
12
|
+
|
13
|
+
# Initialize a new empty N-elements operation container. This is
|
14
|
+
# a virtual class, and other must inherit from this basical container
|
15
|
+
#
|
16
|
+
# * **argument**: `Numeric` to be converted in `CAS::Constant` or `CAS::Op` child operations
|
17
|
+
# * **returns**: `CAS::NaryOp` instance
|
18
|
+
def initialize(*xs)
|
19
|
+
@x = []
|
20
|
+
xs.flatten.each do |x|
|
21
|
+
if x.is_a? Numeric
|
22
|
+
x = Op.numeric_to_const x
|
23
|
+
end
|
24
|
+
CAS::Help.assert(x, CAS::Op)
|
25
|
+
@x << x
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the dependencies of the operation. Require a `CAS::Variable`
|
30
|
+
# and it is one of the recursive method (implicit tree resolution)
|
31
|
+
#
|
32
|
+
# * **argument**: `CAS::Variable` instance
|
33
|
+
# * **returns**: `TrueClass` or `FalseClass`
|
34
|
+
def depend?(v)
|
35
|
+
CAS::Help.assert(v, CAS::Op)
|
36
|
+
@x.include? v
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return a list of derivative using the chain rule. The input is a
|
40
|
+
# operation:
|
41
|
+
#
|
42
|
+
# ```
|
43
|
+
# f(x) = g(x) + h(x) + l(x) + m(x)
|
44
|
+
#
|
45
|
+
# d f(x)
|
46
|
+
# ------ = g'(x) + h'(x) + l'(x) + m'(x)
|
47
|
+
# dx
|
48
|
+
#
|
49
|
+
# d f(x)
|
50
|
+
# ------ = 1
|
51
|
+
# d g(x)
|
52
|
+
# ```
|
53
|
+
# * **argument**: `CAS::Op` object of the derivative
|
54
|
+
# * **returns**: `CAS::NaryOp` of derivative
|
55
|
+
def diff(v)
|
56
|
+
CAS::Help.assert(v, CAS::Op)
|
57
|
+
if self.depend?(v)
|
58
|
+
return @x.map { |x| x.diff(v) }
|
59
|
+
end
|
60
|
+
return CAS::Zero
|
61
|
+
end
|
62
|
+
|
63
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
64
|
+
# or `Float` depends upon promotions).
|
65
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
66
|
+
# as keys, and a `Numeric` as a value
|
67
|
+
#
|
68
|
+
# ``` ruby
|
69
|
+
# x, y = CAS::vars :x, :y
|
70
|
+
# f = (x ** 2) + (y ** 2)
|
71
|
+
# f.call({x => 1, y => 2})
|
72
|
+
# # => 2
|
73
|
+
# ```
|
74
|
+
# * **argument**: `Hash` with feed dictionary
|
75
|
+
# * **returns**: `Array` of `Numeric`
|
76
|
+
def call(fd)
|
77
|
+
CAS::Help.assert(fd, Hash)
|
78
|
+
return @x.map { |x| x.call(fd) }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Perform substitution of a part of the graph using a data table:
|
82
|
+
#
|
83
|
+
# ``` ruby
|
84
|
+
# x, y = CAS::vars :x, :y
|
85
|
+
# f = (x ** 2) + (y ** 2)
|
86
|
+
# puts f
|
87
|
+
# # => (x^2) + (y^2)
|
88
|
+
# puts f.subs({x => CAS::ln(y)})
|
89
|
+
# # => (ln(y)^2) + (y^2)
|
90
|
+
# ```
|
91
|
+
#
|
92
|
+
# * **argument**: `Hash` with substitution table
|
93
|
+
# * **returns**: `CAS::NaryOp` (`self`) with substitution performed
|
94
|
+
def subs(dt)
|
95
|
+
CAS::Help.assert(dt, Hash)
|
96
|
+
@x = @x.map { |z| z.subs(dt) || z }
|
97
|
+
@x.each_with_index do |x, k|
|
98
|
+
sub = dt.keys.select { |e| e == x }[0]
|
99
|
+
if sub
|
100
|
+
if dt[sub].is_a? CAS::Op
|
101
|
+
@x[k] = dt[sub]
|
102
|
+
elsif dt[sub].is_a? Numeric
|
103
|
+
@x[k] = CAS::const dt[sub]
|
104
|
+
else
|
105
|
+
raise CAS::CASError, "Impossible subs. Received a #{dt[sub].class} = #{dt[sub]}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
return self
|
110
|
+
end
|
111
|
+
|
112
|
+
# Convert expression to string
|
113
|
+
#
|
114
|
+
# * **returns**: `String` to print on screen
|
115
|
+
def to_s
|
116
|
+
return "(#{@x.map(&:to_s).join(", ")})"
|
117
|
+
end
|
118
|
+
|
119
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
120
|
+
#
|
121
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
122
|
+
def to_code
|
123
|
+
return "(#{@x.map(&:to_code).join(", ")})"
|
124
|
+
end
|
125
|
+
|
126
|
+
# Simplification callback. It simplify the subgraph of each node
|
127
|
+
# until all possible simplification are performed (thus the execution
|
128
|
+
# time is not deterministic).
|
129
|
+
#
|
130
|
+
# * **returns**: `CAS::Op` simplified
|
131
|
+
def simplify
|
132
|
+
hash = self.to_s
|
133
|
+
@x = @x.map { |x| x.simplify }
|
134
|
+
while self.to_s != hash
|
135
|
+
hash = self.to_s
|
136
|
+
@x = @x.map { |x| x.simplify }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Inspector for the current object
|
141
|
+
#
|
142
|
+
# * **returns**: `String`
|
143
|
+
def inspect
|
144
|
+
"#{self.class}(#{@x.map(&:inspect).join(", ")})"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Equality operator, the standard operator is overloaded
|
148
|
+
# :warning: this operates on the graph, not on the math
|
149
|
+
# See `CAS::equal`, etc.
|
150
|
+
#
|
151
|
+
# * **argument**: `CAS::Op` to be tested against
|
152
|
+
# * **returns**: `TrueClass` if equal, `FalseClass` if differs
|
153
|
+
def ==(op)
|
154
|
+
# CAS::Help.assert(op, CAS::Op)
|
155
|
+
if op.is_a? CAS::NaryOp
|
156
|
+
return false if @x.size != op.x.size
|
157
|
+
0.upto(@x.size - 1) do |i|
|
158
|
+
return false if @x[i] != op.x[i]
|
159
|
+
end
|
160
|
+
return true
|
161
|
+
end
|
162
|
+
false
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns a list of all `CAS::Variable`s of the current tree
|
166
|
+
#
|
167
|
+
# * **returns**: `Array` of `CAS::Variable`s
|
168
|
+
def args
|
169
|
+
r = []
|
170
|
+
@x.each { |x| r += x.args }
|
171
|
+
return r.uniq
|
172
|
+
end
|
173
|
+
|
174
|
+
# Reduce multeplicity will scan for elements that are equal in the definition
|
175
|
+
# of the sum and will reduce their multeplicity. A block can be used to do something
|
176
|
+
# different. For example in nary-product we use it like this:
|
177
|
+
#
|
178
|
+
# ``` ruby
|
179
|
+
# @x = self.__reduce_multeplicity(@x) do |count, op|
|
180
|
+
# count > 1 ? (op ** count) : op
|
181
|
+
# end
|
182
|
+
# ```
|
183
|
+
#
|
184
|
+
# In general it works like that:
|
185
|
+
#
|
186
|
+
# ```
|
187
|
+
# a + a + b + c => 2 * a + b + c
|
188
|
+
# a * a * b * a => (a ** b) * b
|
189
|
+
# ```
|
190
|
+
# But operates only on Array level! This is an internal function
|
191
|
+
# and should never be used
|
192
|
+
#
|
193
|
+
# * **requires**: An `Array`
|
194
|
+
# * **returns**: An `Array` with multeplicity reduced
|
195
|
+
# * **block**: yields the count and the op. Get the value to insert in a new
|
196
|
+
# `Array` that is the returned `Array`
|
197
|
+
def __reduce_multeplicity(xs)
|
198
|
+
count = Hash.new(0)
|
199
|
+
xs.each do |x|
|
200
|
+
e = x
|
201
|
+
count.keys.each { |d| e = d if x == d }
|
202
|
+
count[e] += 1
|
203
|
+
end
|
204
|
+
count.map do |k, v|
|
205
|
+
if block_given?
|
206
|
+
yield(k, v)
|
207
|
+
else
|
208
|
+
v > 1 ? CAS.const(v) * k : k
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Collects all the constants and tries to reduce them to a single constant.
|
214
|
+
# Requires a block to understand what it should do with the constants
|
215
|
+
#
|
216
|
+
# * **requires**: input `Array` of `CAS::Op`
|
217
|
+
# * **returns**: new `Array` of `CAS::Op`
|
218
|
+
# * **block**: yields an `Array` of `CAS::Constant` and an `Array` of others `CAS::Op`,
|
219
|
+
# requires an `Array` back
|
220
|
+
def __reduce_constants(xs)
|
221
|
+
const = []
|
222
|
+
xs.each { |x| const << x if x.is_a? CAS::Constant }
|
223
|
+
if const.size > 0
|
224
|
+
yield const, (xs - const)
|
225
|
+
else
|
226
|
+
xs
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end # NaryOp
|
231
|
+
CAS::NaryOp.init_simplify_dict
|
232
|
+
end
|
data/lib/operators/op.rb
ADDED
@@ -0,0 +1,285 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
class CASError < RuntimeError; end
|
5
|
+
|
6
|
+
# ___
|
7
|
+
# / _ \ _ __
|
8
|
+
# | (_) | '_ \
|
9
|
+
# \___/| .__/
|
10
|
+
# |_|
|
11
|
+
class Op
|
12
|
+
# Argument of the operation
|
13
|
+
attr_reader :x
|
14
|
+
|
15
|
+
# Initialize a new empty operation container. This is a virtual
|
16
|
+
# class and the other must inherit from this basic container.
|
17
|
+
# Some methods raise a `CAS::CASError` if called.
|
18
|
+
# The input element is a Numric, to create a constant.
|
19
|
+
# `CAS::Op` specifies operations with a single variable
|
20
|
+
#
|
21
|
+
# * **argument**: `Numeric` to be converted in `CAS::Constant` or `CAS::Op` child operation
|
22
|
+
# * **returns**: `CAS::Op` instance
|
23
|
+
def initialize(x)
|
24
|
+
if x.is_a? Numeric
|
25
|
+
x = Op.numeric_to_const x
|
26
|
+
end
|
27
|
+
CAS::Help.assert(x, CAS::Op)
|
28
|
+
|
29
|
+
@x = x
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.numeric_to_const(x)
|
33
|
+
if CAS::NumericToConst[x]
|
34
|
+
return CAS::NumericToConst[x]
|
35
|
+
else
|
36
|
+
return CAS::const x
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return the dependencies of the operation. Requires a `CAS::Variable`
|
41
|
+
# and it is one of the recursve method (implicit tree resolution)
|
42
|
+
#
|
43
|
+
# * **argument**: `CAS::Variable` instance
|
44
|
+
# * **returns**: `TrueClass` if depends, `FalseClass` if not
|
45
|
+
def depend?(v)
|
46
|
+
CAS::Help.assert(v, CAS::Op)
|
47
|
+
|
48
|
+
@x.depend? v
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return the derivative of the operation using the chain rule
|
52
|
+
# The input is a `CAS::Op` because it can handle derivatives
|
53
|
+
# with respect to functions. E.g.:
|
54
|
+
#
|
55
|
+
# ```
|
56
|
+
# f(x) = (ln(x))**2
|
57
|
+
# g(x) = ln(x)
|
58
|
+
#
|
59
|
+
# d f(x)
|
60
|
+
# ------ = 2 ln(x)
|
61
|
+
# d g(x)
|
62
|
+
# ```
|
63
|
+
#
|
64
|
+
# * **argument**: `CAS::Op` object of the derivative
|
65
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
66
|
+
def diff(v)
|
67
|
+
CAS::Help.assert(v, CAS::Op)
|
68
|
+
|
69
|
+
if @x.depend? v
|
70
|
+
return @x.diff(v)
|
71
|
+
end
|
72
|
+
CAS::Zero
|
73
|
+
end
|
74
|
+
|
75
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
76
|
+
# or `Float` (depends upon promotions).
|
77
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
78
|
+
# as keys, and a `Numeric` as a value
|
79
|
+
#
|
80
|
+
# ``` ruby
|
81
|
+
# x, y = CAS::vars :x, :y
|
82
|
+
# f = (x ** 2) + (y ** 2)
|
83
|
+
# f.call({x => 1, y => 2})
|
84
|
+
# # => 2
|
85
|
+
# ```
|
86
|
+
#
|
87
|
+
# * **argument**: `Hash` with feed dictionary
|
88
|
+
# * **returns**: `Numeric`
|
89
|
+
def call(f)
|
90
|
+
CAS::Help.assert(f, Hash)
|
91
|
+
|
92
|
+
@x.call(f)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Perform substitution of a part of the graph using a data table:
|
96
|
+
#
|
97
|
+
# ``` ruby
|
98
|
+
# x, y = CAS::vars :x, :y
|
99
|
+
# f = (x ** 2) + (y ** 2)
|
100
|
+
# puts f
|
101
|
+
# # => (x^2) + (y^2)
|
102
|
+
# puts f.subs({x => CAS::ln(y)})
|
103
|
+
# # => (ln(y)^2) + (y^2)
|
104
|
+
# ```
|
105
|
+
#
|
106
|
+
# * **argument**: `Hash` with substitution table
|
107
|
+
# * **returns**: `CAS::Op` (`self`) with substitution performed
|
108
|
+
def subs(dt)
|
109
|
+
CAS::Help.assert(dt, Hash)
|
110
|
+
sub = dt.keys.select { |e| e == @x }[0]
|
111
|
+
if sub
|
112
|
+
if dt[sub].is_a? CAS::Op
|
113
|
+
@x = dt[sub]
|
114
|
+
elsif dt[sub].is_a? Numeric
|
115
|
+
@x = CAS::const dt[sub]
|
116
|
+
else
|
117
|
+
raise CAS::CASError, "Impossible subs. Received a #{dt[@x].class} = #{dt[@x]}"
|
118
|
+
end
|
119
|
+
else
|
120
|
+
@x.subs(dt)
|
121
|
+
end
|
122
|
+
return self
|
123
|
+
end
|
124
|
+
|
125
|
+
# Convert expression to string
|
126
|
+
#
|
127
|
+
# * **returns**: `String` to print on screen
|
128
|
+
def to_s
|
129
|
+
"#{@x}"
|
130
|
+
end
|
131
|
+
|
132
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
133
|
+
#
|
134
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
135
|
+
def to_code
|
136
|
+
"#{@x}"
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns a sum of two `CAS::Op`s
|
140
|
+
#
|
141
|
+
# * **argument**: `CAS::Op` tree
|
142
|
+
# * **returns**: `CAS::Op` new object
|
143
|
+
def +(op)
|
144
|
+
CAS::Sum.new [self, op]
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns a difference of two `CAS::Op`s
|
148
|
+
#
|
149
|
+
# * **argument**: `CAS::Op` tree
|
150
|
+
# * **returns**: `CAS::Op` new object
|
151
|
+
def -(op)
|
152
|
+
CAS::Diff.new self, op
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns a product of two `CAS::Op`s
|
156
|
+
#
|
157
|
+
# * **argument**: `CAS::Op` tree
|
158
|
+
# * **returns**: `CAS::Op` new object
|
159
|
+
def *(op)
|
160
|
+
CAS::Prod.new [self, op]
|
161
|
+
end
|
162
|
+
|
163
|
+
# Returns a division of two `CAS::Op`s
|
164
|
+
#
|
165
|
+
# * **argument**: `CAS::Op` tree
|
166
|
+
# * **returns**: `CAS::Op` new object
|
167
|
+
def /(op)
|
168
|
+
CAS::Div.new self, op
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns the power of two `CAS::Op`s
|
172
|
+
#
|
173
|
+
# * **argument**: `CAS::Op` tree
|
174
|
+
# * **returns**: `CAS::Op` new object
|
175
|
+
def **(op)
|
176
|
+
CAS.pow(self, op)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Unary operator for inversion of a `CAS::Op`
|
180
|
+
#
|
181
|
+
# * **returns**: `CAS::Op` new object
|
182
|
+
def -@
|
183
|
+
CAS.invert(self)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Simplification callback. It simplify the subgraph of each node
|
187
|
+
# until all possible simplification are performed (thus the execution
|
188
|
+
# time is not deterministic).
|
189
|
+
#
|
190
|
+
# * **returns**: `CAS::Op` simplified version
|
191
|
+
def simplify
|
192
|
+
hash = @x.to_s
|
193
|
+
@x = @x.simplify
|
194
|
+
while @x.to_s != hash
|
195
|
+
hash = @x.to_s
|
196
|
+
@x = @x.simplify
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Simplify dictionary performs a dictionary simplification
|
201
|
+
# that is the class variable `@@simplify_dict`
|
202
|
+
#
|
203
|
+
# * **returns**: `CAS::Op` self
|
204
|
+
def simplify_dictionary
|
205
|
+
self.class.simplify_dict(@x) || self
|
206
|
+
end
|
207
|
+
|
208
|
+
# Initializes the simplification dictionary (one for each class)
|
209
|
+
#
|
210
|
+
# * **returns**: `Hash` with simplification dictionary
|
211
|
+
def self.init_simplify_dict
|
212
|
+
@simplify_dict = { }
|
213
|
+
end
|
214
|
+
|
215
|
+
# Returns an element of a
|
216
|
+
def self.simplify_dict(k)
|
217
|
+
@simplify_dict.keys.each do |op|
|
218
|
+
return @simplify_dict[op] if op.simplify == k.simplify
|
219
|
+
end
|
220
|
+
return nil
|
221
|
+
end
|
222
|
+
|
223
|
+
# Inspector for the current object
|
224
|
+
#
|
225
|
+
# * **returns**: `String`
|
226
|
+
def inspect
|
227
|
+
"#{self.class}(#{@x.inspect})"
|
228
|
+
end
|
229
|
+
|
230
|
+
# Equality operator, the standard operator is overloaded
|
231
|
+
# :warning: this operates on the graph, not on the math
|
232
|
+
# See `CAS::equal`, etc.
|
233
|
+
#
|
234
|
+
# * **argument**: `CAS::Op` to be tested against
|
235
|
+
# * **returns**: `TrueClass` if equal, `FalseClass` if differs
|
236
|
+
def ==(op)
|
237
|
+
# CAS::Help.assert(op, CAS::Op)
|
238
|
+
if op.is_a? CAS::Op
|
239
|
+
return false if op.is_a? CAS::BinaryOp
|
240
|
+
return (self.class == op.class and @x == op.x)
|
241
|
+
end
|
242
|
+
false
|
243
|
+
end
|
244
|
+
|
245
|
+
# Disequality operator, the standard operator is overloaded
|
246
|
+
# :warning: this operates on the graph, not on the math
|
247
|
+
# See `CAS::equal`, etc.
|
248
|
+
#
|
249
|
+
# * **argument**: `CAS::Op` to be tested against
|
250
|
+
# * **returns**: `FalseClass` if equal, `TrueClass` if differs
|
251
|
+
def !=(op)
|
252
|
+
not self.==(op)
|
253
|
+
end
|
254
|
+
|
255
|
+
# Evaluates the proc against a given context. It is like having a
|
256
|
+
# snapshot of the tree transformed in a callable object.
|
257
|
+
# Obviously **if the tree changes, the generated proc does notchanges**.
|
258
|
+
# The proc takes as input a feed dictionary in which each variable
|
259
|
+
# is identified through the `CAS::Variable#name` key.
|
260
|
+
#
|
261
|
+
# The proc is evaluated in the context devined by the input `Binding` object
|
262
|
+
# If `nil` is passed, the `eval` will run in this local context
|
263
|
+
#
|
264
|
+
# * **argument**: `Binding` or `NilClass` that is the context of the Ruby VM
|
265
|
+
# * **returns**: `Proc` object with a single argument as an `Hash`
|
266
|
+
def as_proc(bind=nil)
|
267
|
+
args_ext = self.args.map { |e| "#{e} = fd[\"#{e}\"];" }
|
268
|
+
code = "Proc.new do |fd|; #{args_ext.join " "} #{self.to_code}; end"
|
269
|
+
if bind # All objects have eval value, we bind when not nil
|
270
|
+
# CAS::Help.assert(bind, Binding)
|
271
|
+
bind.eval(code)
|
272
|
+
else
|
273
|
+
eval(code)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Returns a list of all `CAS::Variable`s of the current tree
|
278
|
+
#
|
279
|
+
# * **returns**: `Array` of `CAS::Variable`s
|
280
|
+
def args
|
281
|
+
@x.args.uniq
|
282
|
+
end
|
283
|
+
end # Op
|
284
|
+
CAS::Op.init_simplify_dict
|
285
|
+
end
|